This is viad2.c in view mode; [Download] [Up]
/* * ../../../src/true1541/viad2.c * This file is generated from ../../../src/via-tmpl.c and ../../../src/true1541/viad2.def, * Do not edit! */ /* * via-tmpl.c - Template file for VIA emulation. * * Written by * André Fachat (fachat@physik.tu-chemnitz.de) * * Patch by * Andreas Boose (boose@linux.rz.fh-hannover.de) * * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. * */ /* * 24jan97 a.fachat * new interrupt handling, hopefully according to the specs now. * All interrupts (note: not timer events (i.e. alarms) are put * into one interrupt flag, I_VIAD2FL. * if an interrupt condition changes, the function (i.e. cpp macro) * update_viaD2irq() id called, that checks the IRQ line state. * This is now possible, as ettore has decoupled A_* alarm events * from I_* interrupts for performance reasons. * * A new function for signaling rising/falling edges on the * control lines is introduced: * viaD2_signal(VIA_SIG_[CA1|CA2|CB1|CB2], VIA_SIG_[RISE|FALL]) * which signals the corresponding edge to the VIA. The constants * are defined in via.h. * * Except for shift register and input latching everything should be ok now. */ #include "vice.h" #include <stdio.h> #include <time.h> #include "vmachine.h" #include "via.h" #include "resources.h" #include "true1541.h" #include "viad.h" #include "interrupt.h" /*#define VIAD2_TIMER_DEBUG */ /*#define VIAD2_NEED_PB7 *//* when PB7 is really used, set this to enable pulse output from the timer. Otherwise PB7 state is computed only when port B is read - not yet implemented */ /* global */ BYTE viaD2[16]; /* * Local variables */ static int viaD2ifr; /* Interrupt Flag register for viaD2 */ static int viaD2ier; /* Interrupt Enable register for viaD2 */ static unsigned int viaD2tal; /* current timer A latch value */ static unsigned int viaD2tbl; /* current timer B latch value */ static CLOCK viaD2tau; /* time when viaD2 timer A is updated */ static CLOCK viaD2tbu; /* time when viaD2 timer B is updated */ static CLOCK viaD2tai; /* time when next timer A alarm is */ static CLOCK viaD2tbi; /* time when next timer A alarm is */ static int viaD2pb7; /* state of PB7 for pulse output... */ static int viaD2pb7x; /* to be xored herewith */ static int viaD2pb7o; /* to be ored herewith */ static int viaD2pb7xx; static int viaD2pb7sx; /* * local functions */ /* * 01apr98 a.fachat * * One-shot Timing (partly from 6522-VIA.txt): +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ 02 --+ +-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-+ +-+ +- | | | +---+ | WRITE T1C-H ----+ +-----------------#------------------------- ___ | | IRQ OUTPUT --------------------------#---------+ | +--------------- | | PB7 OUTPUT --------+ +--------------- +-----------------#---------+ T1 | N |N-1|N-2|N-3| | 0 | -1|N |N-1|N-2| T2 | N |N-1|N-2|N-3| | 0 | -1| -2| -3| -4| | | |<---- N + 1.5 CYCLES ----->|<--- N + 2 cycles ---> +---+ viaD2t*u* clk ------------------------------------------+ +-------- | | call of int_viaD2* here real viaD2tau value = viaD2tau* + TAUOFFSET viaD2tbu = viaD2tbu* + 0 * * IRQ and PB7 are set/toggled at the low-high transition of Phi2, * but int_* is called a half-cycle before that. Does that matter? * * PB7 output is still to be implemented */ /* timer values do not depend on a certain value here, but PB7 does... */ #define TAUOFFSET (-1) inline static void update_viaD2irq(void) { true1541_set_irq(I_VIAD2FL, (viaD2ifr & viaD2ier & 0x7f) ? IK_IRQ : 0); } /* the next two are used in read_viaD2() */ inline static unsigned int viaD2ta(void) { if (true1541_clk < viaD2tau - TAUOFFSET) return viaD2tau - TAUOFFSET - true1541_clk - 2; else return (viaD2tal - (true1541_clk - viaD2tau + TAUOFFSET) % (viaD2tal + 2)); } inline static unsigned int viaD2tb(void) { return viaD2tbu - true1541_clk - 2; } inline static void update_viaD2tal(CLOCK rclk) { viaD2pb7x = 0; viaD2pb7xx = 0; if (rclk > viaD2tau) { int nuf = (viaD2tal + 1 + rclk - viaD2tau) / (viaD2tal + 2); if (!(viaD2[VIA_ACR] & 0x40)) { if (((nuf - viaD2pb7sx) > 1) || (!viaD2pb7)) { viaD2pb7o = 1; viaD2pb7sx = 0; } } viaD2pb7 ^= (nuf & 1); viaD2tau = TAUOFFSET + viaD2tal + 2 + (rclk - (rclk - viaD2tau + TAUOFFSET) % (viaD2tal + 2)); if (rclk == viaD2tau - viaD2tal - 1) { viaD2pb7xx = 1; } } if (viaD2tau == rclk) viaD2pb7x = 1; viaD2tal = viaD2[VIA_T1LL] + (viaD2[VIA_T1LH] << 8); } inline static void update_viaD2tbl(void) { viaD2tbl = viaD2[VIA_T2CL] + (viaD2[VIA_T2CH] << 8); } /* ------------------------------------------------------------------------- */ /* VIAD2 */ /* * according to Rockwell, all internal registers are cleared, except * for the Timer (1 and 2, counter and latches) and the shift register. */ void reset_viaD2(void) { int i; #ifdef VIAD2_TIMER_DEBUG if (app_resources.debugFlag) printf("VIAD2: reset\n"); #endif /* clear registers */ for (i = 0; i < 4; i++) viaD2[i] = 0; for (i = 4; i < 10; i++) viaD2[i] = 0xff; /* AB 98.08.23 */ for (i = 11; i < 16; i++) viaD2[i] = 0; viaD2tal = 0; viaD2tbl = 0; viaD2tau = true1541_clk; viaD2tbu = true1541_clk; viaD2ier = 0; viaD2ifr = 0; /* disable vice interrupts */ viaD2tai = 0; viaD2tbi = 0; true1541_unset_alarm(A_VIAD2T1); true1541_unset_alarm(A_VIAD2T2); update_viaD2irq(); } void viaD2_signal(int line, int edge) { switch (line) { case VIA_SIG_CA1: viaD2ifr |= ((edge ^ viaD2[VIA_PCR]) & 0x01) ? 0 : VIA_IM_CA1; update_viaD2irq(); break; case VIA_SIG_CA2: if (!(viaD2[VIA_PCR] & 0x08)) { viaD2ifr |= (((edge << 2) ^ viaD2[VIA_PCR]) & 0x04) ? 0 : VIA_IM_CA2; update_viaD2irq(); } break; case VIA_SIG_CB1: viaD2ifr |= (((edge << 4) ^ viaD2[VIA_PCR]) & 0x10) ? 0 : VIA_IM_CB1; update_viaD2irq(); break; case VIA_SIG_CB2: if (!(viaD2[VIA_PCR] & 0x80)) { viaD2ifr |= (((edge << 6) ^ viaD2[VIA_PCR]) & 0x40) ? 0 : VIA_IM_CB2; update_viaD2irq(); } break; } } void REGPARM2 store_viaD2(ADDRESS addr, BYTE byte) { CLOCK rclk = true1541_clk - 1; /* stores have a one-cylce offset */ addr &= 0xf; #ifdef VIAD2_TIMER_DEBUG if ((addr < 10 && addr > 3) || (addr == VIA_ACR) || app_resources.debugFlag) printf("store viaD2[%x] %x, rmwf=%d, clk=%d, rclk=%d\n", (int) addr, (int) byte, true1541_rmw_flag, true1541_clk, rclk); #endif switch (addr) { /* these are done with saving the value */ case VIA_PRA: /* port A */ viaD2ifr &= ~VIA_IM_CA1; if ((viaD2[VIA_PCR] & 0x0a) != 0x2) { viaD2ifr &= ~VIA_IM_CA2; } update_viaD2irq(); case VIA_PRA_NHS: /* port A, no handshake */ viaD2[VIA_PRA_NHS] = byte; addr = VIA_PRA; case VIA_DDRA: viaD2[addr] = byte; true1541_write_gcr(viaD2[VIA_PRA] | ~viaD2[VIA_DDRA]); break; case VIA_PRB: /* port B */ viaD2ifr &= ~VIA_IM_CB1; if ((viaD2[VIA_PCR] & 0xa0) != 0x20) { viaD2ifr &= ~VIA_IM_CB2; } update_viaD2irq(); case VIA_DDRB: { BYTE oldval = viaD2[VIA_PRB] | ~viaD2[VIA_DDRB]; viaD2[addr] = byte; byte = viaD2[VIA_PRB] | ~viaD2[VIA_DDRB]; /* newval */ true1541_led_status = byte & 8; if (((oldval ^ byte) & 0x3) && (byte & 0x4)) /* Stepper motor */ { if ((oldval & 0x3) == ((byte + 1) & 0x3)) true1541_move_head(-1); else if ((oldval & 0x3) == ((byte - 1) & 0x3)) true1541_move_head(+1); } if ((oldval ^ byte) & 0x60) /* Zone bits */ true1541_update_zone_bits((byte >> 5) & 0x3); if ((oldval ^ byte) & 0x04) /* Motor on/off */ true1541_motor_control(byte & 0x04); } break; case VIA_SR: /* Serial Port output buffer */ viaD2[addr] = byte; break; /* Timers */ case VIA_T1CL: case VIA_T1LL: viaD2[VIA_T1LL] = byte; update_viaD2tal(rclk); break; case VIA_T1CH /*TIMER_AH */ : /* Write timer A high */ #ifdef VIAD2_TIMER_DEBUG if (app_resources.debugFlag) printf("Write timer A high: %02x\n", byte); #endif viaD2[VIA_T1LH] = byte; update_viaD2tal(rclk); /* load counter with latch value */ viaD2tau = rclk + viaD2tal + 3 + TAUOFFSET; viaD2tai = rclk + viaD2tal + 2; true1541_set_alarm_clk(A_VIAD2T1, viaD2tai); /* set pb7 state */ viaD2pb7 = 0; viaD2pb7o = 0; /* Clear T1 interrupt */ viaD2ifr &= ~VIA_IM_T1; update_viaD2irq(); break; case VIA_T1LH: /* Write timer A high order latch */ viaD2[addr] = byte; update_viaD2tal(rclk); /* Clear T1 interrupt */ viaD2ifr &= ~VIA_IM_T1; update_viaD2irq(); break; case VIA_T2LL: /* Write timer 2 low latch */ viaD2[VIA_T2LL] = byte; update_viaD2tbl(); break; case VIA_T2CH: /* Write timer 2 high */ viaD2[VIA_T2CH] = byte; update_viaD2tbl(); viaD2tbu = rclk + viaD2tbl + 3; viaD2tbi = rclk + viaD2tbl + 2; true1541_set_alarm(A_VIAD2T2, viaD2tbi); /* Clear T2 interrupt */ viaD2ifr &= ~VIA_IM_T2; update_viaD2irq(); break; /* Interrupts */ case VIA_IFR: /* 6522 Interrupt Flag Register */ viaD2ifr &= ~byte; update_viaD2irq(); break; case VIA_IER: /* Interrupt Enable Register */ #if defined (VIAD2_TIMER_DEBUG) printf("Via#1 set VIA_IER: 0x%x\n", byte); #endif if (byte & VIA_IM_IRQ) { /* set interrupts */ viaD2ier |= byte & 0x7f; } else { /* clear interrupts */ viaD2ier &= ~byte; } update_viaD2irq(); break; /* Control */ case VIA_ACR: /* bit 7 timer 1 output to PB7 */ update_viaD2tal(rclk); if ((viaD2[VIA_ACR] ^ byte) & 0x80) { if (byte & 0x80) { viaD2pb7 = 1 ^ viaD2pb7x; } } if ((viaD2[VIA_ACR] ^ byte) & 0x40) { viaD2pb7 ^= viaD2pb7sx; if ((byte & 0x40)) { if (viaD2pb7x || viaD2pb7xx) { if (viaD2tal) { viaD2pb7o = 1; } else { viaD2pb7o = 0; if ((viaD2[VIA_ACR] & 0x80) && viaD2pb7x && (!viaD2pb7xx)) viaD2pb7 ^= 1; } } } } viaD2pb7sx = viaD2pb7x; viaD2[addr] = byte; /* bit 5 timer 2 count mode */ if (byte & 32) { /* TODO */ /* update_viaD2tb(0); *//* stop timer if mode == 1 */ } /* bit 4, 3, 2 shift register control */ /* bit 1, 0 latch enable port B and A */ break; case VIA_PCR: /* if(viadebug) printf("VIA1: write %02x to PCR\n",byte); */ /* bit 7, 6, 5 CB2 handshake/interrupt control */ /* bit 4 CB1 interrupt control */ /* bit 3, 2, 1 CA2 handshake/interrupt control */ /* bit 0 CA1 interrupt control */ if(byte != viaD2[VIA_PCR]) { register BYTE tmp = byte; /* first set bit 1 and 5 to the real output values */ if((tmp & 0x0c) != 0x0c) tmp |= 0x02; if((tmp & 0xc0) != 0xc0) tmp |= 0x20; /* insert_your_favourite_true1541_function_here(tmp); bit 5 is the write output to the analog circuitry: 0 = writing, 0x20 = reading */ true1541_update_viad2_pcr(tmp); if ((byte&0x20) != (viaD2[addr]&0x20)) { true1541_rotate_disk(0); true1541_rotate_disk(1); } byte = tmp; } viaD2[addr] = byte; break; default: viaD2[addr] = byte; } /* switch */ } /* ------------------------------------------------------------------------- */ BYTE REGPARM1 read_viaD2(ADDRESS addr) { #ifdef VIAD2_TIMER_DEBUG BYTE read_viaD2_(ADDRESS); BYTE retv = read_viaD2_(addr); addr &= 0x0f; if ((addr > 3 && addr < 10) || app_resources.debugFlag) printf("read_viaD2(%x) -> %02x, clk=%d\n", addr, retv, true1541_clk); return retv; } BYTE REGPARM1 read_viaD2_(ADDRESS addr) { #endif CLOCK rclk = true1541_clk; addr &= 0xf; if (viaD2tai && (viaD2tai <= true1541_clk)) int_viaD2t1(true1541_clk - viaD2tai); if (viaD2tbi && (viaD2tbi <= true1541_clk)) int_viaD2t2(true1541_clk - viaD2tbi); switch (addr) { case VIA_PRA: /* port A */ viaD2ifr &= ~VIA_IM_CA1; if ((viaD2[VIA_PCR] & 0x0a) != 0x02) { viaD2ifr &= ~VIA_IM_CA2; } update_viaD2irq(); case VIA_PRA_NHS: /* port A, no handshake */ return ((true1541_read_disk_byte() & ~viaD2[VIA_DDRA]) | (viaD2[VIA_PRA] & viaD2[VIA_DDRA])); case VIA_PRB: /* port B */ viaD2ifr &= ~VIA_IM_CB1; if ((viaD2[VIA_PCR] & 0xa0) != 0x20) viaD2ifr &= ~VIA_IM_CB2; update_viaD2irq(); { BYTE byte; byte = (true1541_read_viad2_prb() & ~viaD2[VIA_DDRB]) | (viaD2[VIA_PRB] & viaD2[VIA_DDRB]); if (viaD2[VIA_ACR] & 0x80) { update_viaD2tal(rclk); byte = (byte & 0x7f) | (((viaD2pb7 ^ viaD2pb7x) | viaD2pb7o) ? 0x80 : 0); } return byte; } /* Timers */ case VIA_T1CL /*TIMER_AL */ : /* timer A low */ viaD2ifr &= ~VIA_IM_T1; update_viaD2irq(); return viaD2ta() & 0xff; case VIA_T1CH /*TIMER_AH */ : /* timer A high */ return (viaD2ta() >> 8) & 0xff; case VIA_T2CL /*TIMER_BL */ : /* timer B low */ viaD2ifr &= ~VIA_IM_T2; update_viaD2irq(); return viaD2tb() & 0xff; case VIA_T2CH /*TIMER_BH */ : /* timer B high */ return (viaD2tb() >> 8) & 0xff; case VIA_SR: /* Serial Port Shift Register */ return (viaD2[addr]); /* Interrupts */ case VIA_IFR: /* Interrupt Flag Register */ { BYTE t = viaD2ifr; if (viaD2ifr & viaD2ier /*[VIA_IER] */ ) t |= 0x80; return (t); } case VIA_IER: /* 6522 Interrupt Control Register */ return (viaD2ier /*[VIA_IER] */ | 0x80); } /* switch */ return (viaD2[addr]); } BYTE REGPARM1 peek_viaD2(ADDRESS addr) { CLOCK rclk = true1541_clk; addr &= 0xf; if (viaD2tai && (viaD2tai <= true1541_clk)) int_viaD2t1(true1541_clk - viaD2tai); if (viaD2tbi && (viaD2tbi <= true1541_clk)) int_viaD2t2(true1541_clk - viaD2tbi); switch (addr) { case VIA_PRA: return read_viaD2(VIA_PRA_NHS); case VIA_PRB: /* port B */ { BYTE byte; byte = (true1541_read_viad2_prb() & ~viaD2[VIA_DDRB]) | (viaD2[VIA_PRB] & viaD2[VIA_DDRB]); if (viaD2[VIA_ACR] & 0x80) { update_viaD2tal(rclk); byte = (byte & 0x7f) | (((viaD2pb7 ^ viaD2pb7x) | viaD2pb7o) ? 0x80 : 0); } return byte; } /* Timers */ case VIA_T1CL /*TIMER_AL */ : /* timer A low */ return viaD2ta() & 0xff; case VIA_T2CL /*TIMER_BL */ : /* timer B low */ return viaD2tb() & 0xff; default: break; } /* switch */ return read_viaD2(addr); } /* ------------------------------------------------------------------------- */ int int_viaD2t1(long offset) { /* CLOCK rclk = true1541_clk - offset; */ #ifdef VIAD2_TIMER_DEBUG if (app_resources.debugFlag) printf("viaD2 timer A interrupt\n"); #endif if (!(viaD2[VIA_ACR] & 0x40)) { /* one-shot mode */ #if 0 /* defined (VIAD2_TIMER_DEBUG) */ printf("VIAD2 Timer A interrupt -- one-shot mode: next int won't happen\n"); #endif true1541_unset_alarm(A_VIAD2T1); /*int_clk[I_VIAD2T1] = 0; */ viaD2tai = 0; } else { /* continuous mode */ /* load counter with latch value */ viaD2tai += viaD2tal + 2; true1541_set_alarm_clk(A_VIAD2T1, viaD2tai); } viaD2ifr |= VIA_IM_T1; update_viaD2irq(); /* TODO: toggle PB7? */ return 0; /*(viaier & VIA_IM_T1) ? 1:0; */ } /* * Timer B is always in one-shot mode */ int int_viaD2t2(long offset) { #ifdef VIAD2_TIMER_DEBUG if (app_resources.debugFlag) printf("VIAD2 timer B interrupt\n"); #endif true1541_unset_alarm(A_VIAD2T2); /*int_clk[I_VIAD2T2] = 0; */ viaD2ifr |= VIA_IM_T2; update_viaD2irq(); return 0; } void viaD2_prevent_clk_overflow(CLOCK sub) { unsigned int t; t = (viaD2tau - (true1541_clk + sub)) & 0xffff; viaD2tau = true1541_clk + t; t = (viaD2tbu - (true1541_clk + sub)) & 0xffff; viaD2tbu = true1541_clk + t; if (viaD2tai) viaD2tai -= sub; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.