/* * UAE - The Un*x Amiga Emulator * * CIA chip support * * Copyright 1995 Bernd Schmidt, Alessandro Bissacco * Copyright 1996, 1997 Stefan Reinauer, Christian Schmitt */ #include "sysconfig.h" #include "sysdeps.h" #include #include "options.h" #include "events.h" #include "memory.h" #include "custom.h" #include "cia.h" #include "serial.h" #include "disk.h" #include "xwin.h" #include "keybuf.h" #include "gui.h" #include "savestate.h" #include "inputdevice.h" #include "zfile.h" #include "ar.h" #ifdef PARALLEL_PORT # include "parallel.h" #endif #ifdef CD32 # include "akiko.h" #endif #include "debug.h" #ifdef ARCADIA # include "arcadia.h" #endif #ifdef GEKKO # include "ogc/lwp_watchdog.h" #endif //#define CIA_DEBUG_R //#define CIA_DEBUG_W //#define DONGLE_DEBUG /* FIXME: Add configure support to enable this. */ #ifdef HAVE_GETTIMEOFDAY # define TOD_HACK #endif #define DIV10 (10 * CYCLE_UNIT / 2) /* Yes, a bad identifier. */ /* battclock stuff */ #define RTC_D_ADJ 8 #define RTC_D_IRQ 4 #define RTC_D_BUSY 2 #define RTC_D_HOLD 1 #define RTC_E_t1 8 #define RTC_E_t0 4 #define RTC_E_INTR 2 #define RTC_E_MASK 1 #define RTC_F_TEST 8 #define RTC_F_24_12 4 #define RTC_F_STOP 2 #define RTC_F_RSET 1 static unsigned int clock_control_d = RTC_D_ADJ + RTC_D_HOLD; static unsigned int clock_control_e = 0; static unsigned int clock_control_f = RTC_F_24_12; static unsigned int ciaaicr, ciaaimask, ciabicr, ciabimask; static unsigned int ciaacra, ciaacrb, ciabcra, ciabcrb; /* Values of the CIA timers. */ static unsigned long ciaata, ciaatb, ciabta, ciabtb; /* Computed by compute_passed_time. */ static unsigned long ciaata_passed, ciaatb_passed, ciabta_passed, ciabtb_passed; static unsigned long ciaatod, ciabtod, ciaatol, ciabtol, ciaaalarm, ciabalarm; static int ciaatlatch, ciabtlatch; static int oldled, oldovl; unsigned int gui_ledstate; static unsigned long ciaala, ciaalb, ciabla, ciablb; static int ciaatodon, ciabtodon; static unsigned int ciaapra, ciaaprb, ciaadra, ciaadrb, ciaasdr, ciaasdr_cnt; static unsigned int ciabpra, ciabprb, ciabdra, ciabdrb, ciabsdr, ciabsdr_cnt; static unsigned int div10; static int kbstate, kback, ciaasdr_unread; static unsigned int sleepyhead = 0; #ifdef TOD_HACK static int tod_hack, tod_hack_delay; #endif static uae_u8 serbits; static int warned = 10; static void setclr (unsigned int *p, unsigned int val) { if (val & 0x80) { *p |= val & 0x7F; } else { *p &= ~val; } } #include "newcpu.h" static void RethinkICRA (void) { if (ciaaimask & ciaaicr) { ciaaicr |= 0x80; INTREQ_0 (0x8000 | 0x0008); } else { ciaaicr &= 0x7F; } } static void RethinkICRB (void) { if (ciabimask & ciabicr) { ciabicr |= 0x80; INTREQ_0 (0x8000 | 0x2000); } else { ciabicr &= 0x7F; } } void rethink_cias (void) { RethinkICRA (); RethinkICRB (); } /* Figure out how many CIA timer cycles have passed for each timer since the last call of CIA_calctimers. */ static void compute_passed_time (void) { unsigned long int ccount = (get_cycles () - eventtab[ev_cia].oldcycles + div10); unsigned long int ciaclocks = ccount / DIV10; ciaata_passed = ciaatb_passed = ciabta_passed = ciabtb_passed = 0; /* CIA A timers */ if ((ciaacra & 0x21) == 0x01) { assert ((ciaata+1) >= ciaclocks); ciaata_passed = ciaclocks; } if ((ciaacrb & 0x61) == 0x01) { assert ((ciaatb+1) >= ciaclocks); ciaatb_passed = ciaclocks; } /* CIA B timers */ if ((ciabcra & 0x21) == 0x01) { assert ((ciabta+1) >= ciaclocks); ciabta_passed = ciaclocks; } if ((ciabcrb & 0x61) == 0x01) { assert ((ciabtb+1) >= ciaclocks); ciabtb_passed = ciaclocks; } } /* Called to advance all CIA timers to the current time. This expects that one of the timer values will be modified, and CIA_calctimers will be called in the same cycle. */ static void CIA_update (void) { unsigned long int ccount = (get_cycles () - eventtab[ev_cia].oldcycles + div10); unsigned long int ciaclocks = ccount / DIV10; int aovfla = 0, aovflb = 0, asp = 0, bovfla = 0, bovflb = 0, bsp = 0; div10 = ccount % DIV10; /* CIA A timers */ if ((ciaacra & 0x21) == 0x01) { assert ((ciaata+1) >= ciaclocks); if ((ciaata+1) == ciaclocks) { if ((ciaacra & 0x48) == 0x40 && ciaasdr_cnt > 0 && --ciaasdr_cnt == 0) asp = 1; aovfla = 1; if ((ciaacrb & 0x61) == 0x41 || (ciaacrb & 0x61) == 0x61) { if (ciaatb-- == 0) aovflb = 1; } } ciaata -= ciaclocks; } if ((ciaacrb & 0x61) == 0x01) { assert ((ciaatb+1) >= ciaclocks); if ((ciaatb+1) == ciaclocks) aovflb = 1; ciaatb -= ciaclocks; } /* CIA B timers */ if ((ciabcra & 0x21) == 0x01) { assert ((ciabta+1) >= ciaclocks); if ((ciabta+1) == ciaclocks) { if ((ciabcra & 0x48) == 0x40 && ciabsdr_cnt > 0 && --ciabsdr_cnt == 0) bsp = 1; bovfla = 1; if ((ciabcrb & 0x61) == 0x41 || (ciabcrb & 0x61) == 0x61) { if (ciabtb-- == 0) bovflb = 1; } } ciabta -= ciaclocks; } if ((ciabcrb & 0x61) == 0x01) { assert ((ciabtb+1) >= ciaclocks); if ((ciabtb+1) == ciaclocks) bovflb = 1; ciabtb -= ciaclocks; } if (aovfla) { ciaaicr |= 1; RethinkICRA(); ciaata = ciaala; if (ciaacra & 0x8) ciaacra &= ~1; } if (aovflb) { ciaaicr |= 2; RethinkICRA(); ciaatb = ciaalb; if (ciaacrb & 0x8) ciaacrb &= ~1; } if (asp) { ciaaicr |= 8; RethinkICRA(); } if (bovfla) { ciabicr |= 1; RethinkICRB(); ciabta = ciabla; if (ciabcra & 0x8) ciabcra &= ~1; } if (bovflb) { ciabicr |= 2; RethinkICRB(); ciabtb = ciablb; if (ciabcrb & 0x8) ciabcrb &= ~1; } if (bsp) { ciabicr |= 8; RethinkICRB(); } } /* Call this only after CIA_update has been called in the same cycle. */ static void CIA_calctimers (void) { unsigned long int ciaatimea = ~0L, ciaatimeb = ~0L, ciabtimea = ~0L, ciabtimeb = ~0L; eventtab[ev_cia].oldcycles = get_cycles (); if ((ciaacra & 0x21) == 0x01) { ciaatimea = (DIV10 - div10) + DIV10 * ciaata; } if ((ciaacrb & 0x61) == 0x41) { /* Timer B will not get any pulses if Timer A is off. */ if (ciaatimea != (unsigned long int)-1L) { /* If Timer A is in one-shot mode, and Timer B needs more than * one pulse, it will not underflow. */ if (ciaatb == 0 || (ciaacra & 0x8) == 0) { /* Otherwise, we can determine the time of the underflow. */ /* This may overflow, however. So just ignore this timer and use the fact that we'll call CIA_handler for the A timer. */ #if 0 ciaatimeb = ciaatimea + ciaala * DIV10 * ciaatb; #endif } } } if ((ciaacrb & 0x61) == 0x01) { ciaatimeb = (DIV10 - div10) + DIV10 * ciaatb; } if ((ciabcra & 0x21) == 0x01) { ciabtimea = (DIV10 - div10) + DIV10 * ciabta; } if ((ciabcrb & 0x61) == 0x41) { /* Timer B will not get any pulses if Timer A is off. */ if (ciabtimea != (unsigned long int)-1L) { /* If Timer A is in one-shot mode, and Timer B needs more than * one pulse, it will not underflow. */ if (ciabtb == 0 || (ciabcra & 0x8) == 0) { /* Otherwise, we can determine the time of the underflow. */ #if 0 ciabtimeb = ciabtimea + ciabla * DIV10 * ciabtb; #endif } } } if ((ciabcrb & 0x61) == 0x01) { ciabtimeb = (DIV10 - div10) + DIV10 * ciabtb; } eventtab[ev_cia].active = (ciaatimea != (unsigned long int)-1 || ciaatimeb != (unsigned long int)-1 || ciabtimea != (unsigned long int)-1 || ciabtimeb != (unsigned long int)-1); if (eventtab[ev_cia].active) { unsigned long int ciatime = ~0L; if (ciaatimea != (unsigned long int)-1) ciatime = ciaatimea; if (ciaatimeb != (unsigned long int)-1 && ciaatimeb < ciatime) ciatime = ciaatimeb; if (ciabtimea != (unsigned long int)-1 && ciabtimea < ciatime) ciatime = ciabtimea; if (ciabtimeb != (unsigned long int)-1 && ciabtimeb < ciatime) ciatime = ciabtimeb; eventtab[ev_cia].evtime = ciatime + get_cycles (); } events_schedule(); } void CIA_handler (void) { CIA_update (); CIA_calctimers (); } void cia_diskindex (void) { ciabicr |= 0x10; RethinkICRB(); } static void cia_parallelack (void) { ciaaicr |= 0x10; RethinkICRA (); } static int checkalarm (unsigned long tod, unsigned long alarm, int inc) { if (tod == alarm) return 1; if (!inc) return 0; /* emulate buggy TODMED counter. * it counts: .. 29 2A 2B 2C 2D 2E 2F 20 30 31 32 .. * (0F->00->10 only takes couple of cycles but it will trigger alarm.. */ if (tod & 0x000fff) return 0; if (((tod - 1) & 0xfff000) == alarm) return 1; return 0; } STATIC_INLINE void ciab_checkalarm (int inc) { if (checkalarm (ciabtod, ciabalarm, inc)) { ciabicr |= 4; RethinkICRB (); } } STATIC_INLINE void ciaa_checkalarm (int inc) { if (checkalarm (ciaatod, ciaaalarm, inc)) { ciaaicr |= 4; RethinkICRA (); } } void CIA_hsync_handler (void) { if (ciabtodon) { ciabtod++; ciabtod &= 0xFFFFFF; ciab_checkalarm (1); } if (keys_available() && kback && (ciaacra & 0x40) == 0 && (hsync_counter & 15) == 0) { /* * This hack lets one possible ciaaicr cycle go by without any key * being read, for every cycle in which a key is pulled out of the * queue. If no hack is used, a lot of key events just get lost * when you type fast. With a simple hack that waits for ciaasdr * to be read before feeding it another, it will keep up until the * queue gets about 14 characters ahead and then lose events, and * the mouse pointer will freeze while typing is being taken in. * With this hack, you can type 30 or 40 characters ahead with little * or no lossage, and the mouse doesn't get stuck. The tradeoff is * that the total slowness of typing appearing on screen is worse. */ if (ciaasdr_unread == 2) { ciaasdr_unread = 0; } else if (ciaasdr_unread == 0) { switch (kbstate) { case 0: ciaasdr = (uae_s8)~0xFB; /* aaarghh... stupid compiler */ kbstate++; break; case 1: kbstate++; ciaasdr = (uae_s8)~0xFD; break; case 2: ciaasdr = ~get_next_key(); ciaasdr_unread = 1; /* interlock to prevent lost keystrokes */ break; } ciaaicr |= 8; RethinkICRA(); sleepyhead = 0; } else if (!(++sleepyhead & 15)) { ciaasdr_unread = 0; /* give up on this key event after unread for a long time */ } } } #ifdef TOD_HACK static void tod_hack_reset (void) { struct timeval tv; uae_u32 rate = currprefs.ntscmode ? 60 : 50; #ifdef GEKKO //gettimeofday is buggy in libogc tod_hack = (uae_u32) ((ticks_to_microsecs(gettick())/(1000000 / rate))); #else gettimeofday (&tv, NULL); tod_hack = (uae_u32)(((uae_u64)tv.tv_sec) * rate + tv.tv_usec / (1000000 / rate)); #endif tod_hack -= ciaatod; tod_hack_delay = 10 * 50; } #endif void CIA_vsync_handler () { #ifdef TOD_HACK if (currprefs.tod_hack && ciaatodon) { struct timeval tv; uae_u32 t, nt, rate = currprefs.ntscmode ? 60 : 50; if (tod_hack_delay > 0) { tod_hack_delay--; if (tod_hack_delay == 0) { tod_hack_reset (); tod_hack_delay = 0; write_log ("TOD_HACK re-initialized CIATOD=%#6.6lX\n", ciaatod); } } if (tod_hack_delay == 0) { #ifdef GEKKO //gettimeofday is buggy in libogc t = (uae_u32) ((ticks_to_microsecs(gettick())/(1000000 / rate))); #else gettimeofday (&tv, NULL); t = (uae_u32)(((uae_u64)tv.tv_sec) * rate + tv.tv_usec / (1000000 / rate)); #endif nt = t - tod_hack; if ((nt < ciaatod && ciaatod - nt < 10) || nt == ciaatod) return; /* try not to count backwards */ ciaatod = nt; ciaatod &= 0xffffff; ciaa_checkalarm (0); return; } } #endif if (ciaatodon) { ciaatod++; ciaatod &= 0xFFFFFF; ciaa_checkalarm (1); } } static void bfe001_change (void) { uae_u8 v = ciaapra; v |= ~ciaadra; /* output is high when pin's direction is input */ if ((v & 2) != oldled) { int led = (v & 2) ? 0 : 1; oldled = v & 2; gui_led (0, led); gui_ledstate &= ~1; gui_data.powerled = led; gui_ledstate |= led; } if ((v & 1) != oldovl) { oldovl = v & 1; if (!oldovl || ersatzkickfile) { map_overlay (1); } else if (!(currprefs.chipset_mask & CSMASK_AGA)) { /* pin disconnected in AGA chipset, CD audio mute on/off on CD32 */ map_overlay (0); } } } static uae_u8 ReadCIAA (unsigned int addr) { unsigned int tmp; compute_passed_time (); #ifdef CIA_DEBUG_R write_log ("R_CIAA: bfe%x01 %08.8X\n", addr, m68k_getpc (®s)); #endif switch (addr & 0xf) { case 0: #ifdef ACTION_REPLAY action_replay_ciaread(); #endif tmp = DISK_status() & 0x3c; tmp |= handle_joystick_buttons (ciaadra); tmp |= (ciaapra | (ciaadra ^ 3)) & 0x03; if (ciaadra & 0x40) tmp = (tmp & ~0x40) | (ciaapra & 0x40); if (ciaadra & 0x80) tmp = (tmp & ~0x80) | (ciaapra & 0x80); #ifdef DONGLE_DEBUG if (notinrom()) write_log ("BFE001 R %02.2X %s\n", tmp, debuginfo(0)); #endif return tmp; case 1: #ifdef PARALLEL_PORT if (isprinter () > 0) { tmp = ciaaprb; } else if (isprinter () < 0) { uae_u8 v; parallel_direct_read_data (&v); tmp = v; #ifdef ARCADIA } else if (arcadia_rom) { tmp = arcadia_parport (0, ciaaprb, ciaadrb); #endif } else { tmp = handle_parport_joystick (0, ciaaprb, ciaadrb); } #else tmp = handle_parport_joystick (0, ciaaprb, ciaadrb); #ifdef DONGLE_DEBUG if (notinrom()) write_log ("BFE101 R %02.2X %s\n", tmp, debuginfo(0)); #endif #endif if (ciaacrb & 2) { int pb7 = 0; if (ciaacrb & 4) pb7 = ciaacrb & 1; tmp &= ~0x80; tmp |= pb7 ? 0x80 : 00; } if (ciaacra & 2) { int pb6 = 0; if (ciaacra & 4) pb6 = ciaacra & 1; tmp &= ~0x40; tmp |= pb6 ? 0x40 : 00; } return tmp; case 2: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFE201 R %02.2X %s\n", ciaadra, debuginfo(0)); #endif return ciaadra; case 3: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFE301 R %02.2X %s\n", ciaadrb, debuginfo(0)); #endif return ciaadrb; case 4: return (uae_u8)((ciaata - ciaata_passed) & 0xff); case 5: return (uae_u8)((ciaata - ciaata_passed) >> 8); case 6: return (uae_u8)((ciaatb - ciaatb_passed) & 0xff); case 7: return (uae_u8)((ciaatb - ciaatb_passed) >> 8); case 8: if (ciaatlatch) { ciaatlatch = 0; return (uae_u8)ciaatol; } else return (uae_u8)ciaatod; case 9: if (ciaatlatch) return (uae_u8)(ciaatol >> 8); else return (uae_u8)(ciaatod >> 8); case 10: if (!ciaatlatch) { /* only if not already latched. A1200 confirmed. (TW) */ ciaatlatch = 1; ciaatol = ciaatod; } return (uae_u8)(ciaatol >> 16); case 12: if (ciaasdr_unread == 1) ciaasdr_unread = 2; return ciaasdr; case 13: tmp = ciaaicr; ciaaicr = 0; RethinkICRA(); return tmp; case 14: return ciaacra; case 15: return ciaacrb; } return 0; } static uae_u8 ReadCIAB (unsigned int addr) { unsigned int tmp; #ifdef CIA_DEBUG_R write_log ("R_CIAB: bfd%x00 %08.8X\n", addr, m68k_getpc (®s)); #endif compute_passed_time (); switch (addr & 0xf) { case 0: tmp = 0; #ifdef SERIAL_PORT if (currprefs.use_serial) tmp = serial_readstatus(ciabdra); #endif #ifdef PARALLEL_PORT if (isprinter () > 0) { tmp |= ciabpra & (0x04 | 0x02 | 0x01); } else if (isprinter () < 0) { uae_u8 v; parallel_direct_read_status (&v); tmp |= v & 7; } else { tmp |= handle_parport_joystick (1, ciabpra, ciabdra); } #endif #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFD000 R %02.2X %s\n", tmp, debuginfo(0)); #endif return tmp; case 1: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFD100 R %02.2X %s\n", ciabprb, debuginfo(0)); #endif tmp = ciabprb; if (ciabcrb & 2) { int pb7 = 0; if (ciabcrb & 4) pb7 = ciabcrb & 1; tmp &= ~0x80; tmp |= pb7 ? 0x80 : 00; } if (ciabcra & 2) { int pb6 = 0; if (ciabcra & 4) pb6 = ciabcra & 1; tmp &= ~0x40; tmp |= pb6 ? 0x40 : 00; } return tmp; case 2: return ciabdra; case 3: return ciabdrb; case 4: return (uae_u8)((ciabta - ciabta_passed) & 0xff); case 5: return (uae_u8)((ciabta - ciabta_passed) >> 8); case 6: return (uae_u8)((ciabtb - ciabtb_passed) & 0xff); case 7: return (uae_u8)((ciabtb - ciabtb_passed) >> 8); case 8: if (ciabtlatch) { ciabtlatch = 0; return (uae_u8)ciabtol; } else return (uae_u8)ciabtod; case 9: if (ciabtlatch) return (uae_u8)(ciabtol >> 8); else return (uae_u8)(ciabtod >> 8); case 10: if (!ciabtlatch) { ciabtlatch = 1; ciabtol = ciabtod; } return (uae_u8)(ciabtol >> 16); case 12: return ciabsdr; case 13: tmp = ciabicr; ciabicr = 0; RethinkICRB(); return tmp; case 14: return ciabcra; case 15: return ciabcrb; } return 0; } static void WriteCIAA (uae_u16 addr,uae_u8 val) { #ifdef CIA_DEBUG_W write_log ("W_CIAA: bfe%x01 %02.2X %08.8X\n", addr, val, m68k_getpc (®s)); #endif switch (addr & 0xf) { case 0: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFE001 W %02.2X %s\n", val, debuginfo(0)); #endif ciaapra = (ciaapra & ~0xc3) | (val & 0xc3); bfe001_change (); handle_cd32_joystick_cia (ciaapra, ciaadra); break; case 1: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFE101 W %02.2X %s\n", val, debuginfo(0)); #endif ciaaprb = val; #ifdef PARALLEL_PORT if (isprinter() > 0) { doprinter (val); cia_parallelack (); } else if (isprinter() < 0) { parallel_direct_write_data (val, ciaadrb); cia_parallelack (); #ifdef ARCADIA } else if (arcadia_rom) { arcadia_parport (1, ciaaprb, ciaadrb); #endif } #endif break; case 2: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFE201 W %02.2X %s\n", val, debuginfo(0)); #endif ciaadra = val; bfe001_change (); break; case 3: ciaadrb = val; #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFE301 W %02.2X %s\n", val, debuginfo(0)); #endif #ifdef ARCADIA if (arcadia_rom) arcadia_parport (1, ciaaprb, ciaadrb); #endif break; case 4: CIA_update (); ciaala = (ciaala & 0xff00) | val; CIA_calctimers (); break; case 5: CIA_update (); ciaala = (ciaala & 0xff) | (val << 8); if ((ciaacra & 1) == 0) ciaata = ciaala; if (ciaacra & 8) { ciaata = ciaala; ciaacra |= 1; } CIA_calctimers (); break; case 6: CIA_update (); ciaalb = (ciaalb & 0xff00) | val; CIA_calctimers (); break; case 7: CIA_update (); ciaalb = (ciaalb & 0xff) | (val << 8); if ((ciaacrb & 1) == 0) ciaatb = ciaalb; if (ciaacrb & 8) { ciaatb = ciaalb; ciaacrb |= 1; } CIA_calctimers (); break; case 8: if (ciaacrb & 0x80) { ciaaalarm = (ciaaalarm & ~0xff) | val; } else { ciaatod = (ciaatod & ~0xff) | val; ciaatodon = 1; ciaa_checkalarm (0); #ifdef TOD_HACK if (currprefs.tod_hack) tod_hack_reset (); #endif } break; case 9: if (ciaacrb & 0x80) { ciaaalarm = (ciaaalarm & ~0xff00) | (val << 8); } else { ciaatod = (ciaatod & ~0xff00) | (val << 8); } break; case 10: if (ciaacrb & 0x80) { ciaaalarm = (ciaaalarm & ~0xff0000) | (val << 16); } else { ciaatod = (ciaatod & ~0xff0000) | (val << 16); ciaatodon = 0; } break; case 12: CIA_update (); ciaasdr = val; if (ciaacra & 0x40) { kback = 1; } else { ciaasdr_cnt = 0; } if ((ciaacra & 0x41) == 0x41 && ciaasdr_cnt == 0) ciaasdr_cnt = 8 * 2; CIA_calctimers (); break; case 13: setclr(&ciaaimask,val); break; case 14: CIA_update (); val &= 0x7f; /* bit 7 is unused */ ciaacra = val; if (ciaacra & 0x10) { ciaacra &= ~0x10; ciaata = ciaala; } if (ciaacra & 0x40) kback = 1; CIA_calctimers (); break; case 15: CIA_update (); ciaacrb = val; if (ciaacrb & 0x10) { ciaacrb &= ~0x10; ciaatb = ciaalb; } CIA_calctimers (); break; } } static void WriteCIAB (uae_u16 addr,uae_u8 val) { #ifdef CIA_DEBUG_W write_log ("W_CIAB: bfd%x00 %02.2X %08.8X\n", addr, val, m68k_getpc (®s)); #endif switch (addr & 0xf) { case 0: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFD000 W %02.2X %s\n", val, debuginfo(0)); #endif ciabpra = val; #ifdef SERIAL_PORT if (currprefs.use_serial) serial_writestatus(ciabpra, ciabdra); #endif #ifdef PARALLEL_PORT if (isprinter () < 0) parallel_direct_write_status (val, ciabdra); #endif break; case 1: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFD100 W %02.2X %s\n", val, debuginfo(0)); #endif ciabprb = val; DISK_select(val); break; case 2: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFD200 W %02.2X %s\n", val, debuginfo(0)); #endif ciabdra = val; #ifdef SERIAL_PORT if (currprefs.use_serial) serial_writestatus (ciabpra, ciabdra); #endif break; case 3: #ifdef DONGLE_DEBUG if (notinrom ()) write_log ("BFD300 W %02.2X %s\n", val, debuginfo(0)); #endif ciabdrb = val; break; case 4: CIA_update (); ciabla = (ciabla & 0xff00) | val; CIA_calctimers (); break; case 5: CIA_update (); ciabla = (ciabla & 0xff) | (val << 8); if ((ciabcra & 1) == 0) ciabta = ciabla; if (ciabcra & 8) { ciabta = ciabla; ciabcra |= 1; } CIA_calctimers (); break; case 6: CIA_update (); ciablb = (ciablb & 0xff00) | val; CIA_calctimers (); break; case 7: CIA_update (); ciablb = (ciablb & 0xff) | (val << 8); if ((ciabcrb & 1) == 0) ciabtb = ciablb; if (ciabcrb & 8) { ciabtb = ciablb; ciabcrb |= 1; } CIA_calctimers (); break; case 8: if (ciabcrb & 0x80) { ciabalarm = (ciabalarm & ~0xff) | val; } else { ciabtod = (ciabtod & ~0xff) | val; ciabtodon = 1; ciab_checkalarm (0); } break; case 9: if (ciabcrb & 0x80) { ciabalarm = (ciabalarm & ~0xff00) | (val << 8); } else { ciabtod = (ciabtod & ~0xff00) | (val << 8); } break; case 10: if (ciabcrb & 0x80) { ciabalarm = (ciabalarm & ~0xff0000) | (val << 16); } else { ciabtod = (ciabtod & ~0xff0000) | (val << 16); ciabtodon = 0; } break; case 12: CIA_update (); ciabsdr = val; if ((ciabcra & 0x40) == 0) ciabsdr_cnt = 0; if ((ciabcra & 0x41) == 0x41 && ciabsdr_cnt == 0) ciabsdr_cnt = 8 * 2; CIA_calctimers (); break; case 13: setclr(&ciabimask,val); break; case 14: CIA_update (); val &= 0x7f; /* bit 7 is unused */ ciabcra = val; if (ciabcra & 0x10) { ciabcra &= ~0x10; ciabta = ciabla; } CIA_calctimers (); break; case 15: CIA_update (); ciabcrb = val; if (ciabcrb & 0x10) { ciabcrb &= ~0x10; ciabtb = ciablb; } CIA_calctimers (); break; } } void CIA_inprec_prepare (void) { sleepyhead = 0; } void CIA_reset (void) { #ifdef TOD_HACK tod_hack = 0; if (currprefs.tod_hack) tod_hack_reset (); #endif kback = 1; kbstate = 0; ciaasdr_unread = 0; serbits = 0; oldovl = -1; oldled = -1; if (!savestate_state) { ciaatlatch = ciabtlatch = 0; ciaapra = 0; ciaadra = 0; ciaatod = ciabtod = 0; ciaatodon = ciabtodon = 0; ciaaicr = ciabicr = ciaaimask = ciabimask = 0; ciaacra = ciaacrb = ciabcra = ciabcrb = 0x4; /* outmode = toggle; */ ciaala = ciaalb = ciabla = ciablb = ciaata = ciaatb = ciabta = ciabtb = 0xFFFF; ciaaalarm = ciabalarm = 0; ciabpra = 0x8C; ciabdra = 0; div10 = 0; ciaasdr_cnt = 0; ciaasdr = 0; ciabsdr_cnt = 0; ciabsdr = 0; ciaata_passed = ciaatb_passed = ciabta_passed = ciabtb_passed = 0; } CIA_calctimers (); if (! ersatzkickfile) map_overlay (0); #ifdef SERIAL_PORT if (currprefs.use_serial && !savestate_state) serial_dtr_off (); /* Drop DTR at reset */ #endif if (savestate_state) { bfe001_change (); /* select drives */ DISK_select (ciabprb); } #ifdef CD32 if (cd32_enabled) { akiko_reset (); if (!akiko_init ()) cd32_enabled = 0; } #endif } void dumpcia (void) { write_log ("A: CRA %02x CRB %02x ICR %02x IM %02x TA %04lx (%04lx) TB %04lx (%04lx)\n", ciaacra, ciaacrb, ciaaicr, ciaaimask, ciaata, ciaala, ciaatb, ciaalb); write_log ("TOD %06lx ALARM %06lx %c%c\n", ciaatod, ciaaalarm, ciaatlatch ? 'L' : ' ', ciaatodon ? ' ' : 'S'); write_log ("B: CRA %02x CRB %02x ICR %02x IM %02x TA %04lx (%04lx) TB %04lx (%04lx)\n", ciabcra, ciabcrb, ciaaicr, ciabimask, ciabta, ciabla, ciabtb, ciablb); write_log ("TOD %06lx ALARM %06lx %c%c\n", ciabtod, ciabalarm, ciabtlatch ? 'L' : ' ', ciabtodon ? ' ' : 'S'); } /* CIA memory access */ static uae_u32 cia_lget (uaecptr) REGPARAM; static uae_u32 cia_wget (uaecptr) REGPARAM; static uae_u32 cia_bget (uaecptr) REGPARAM; static void cia_lput (uaecptr, uae_u32) REGPARAM; static void cia_wput (uaecptr, uae_u32) REGPARAM; static void cia_bput (uaecptr, uae_u32) REGPARAM; addrbank cia_bank = { cia_lget, cia_wget, cia_bget, cia_lput, cia_wput, cia_bput, default_xlate, default_check, NULL }; #ifdef CD32 extern uae_u32 akiko_lget (uaecptr addr) REGPARAM; extern uae_u32 akiko_wget (uaecptr addr) REGPARAM; extern uae_u32 akiko_bget (uaecptr addr) REGPARAM; extern void akiko_bput (uaecptr addr, uae_u32 value) REGPARAM; extern void akiko_wput (uaecptr addr, uae_u32 value) REGPARAM; extern void akiko_lput (uaecptr addr, uae_u32 value) REGPARAM; #endif /* e-clock is 10 CPU cycles, 6 cycles low, 4 high * data transfer happens during 4 high cycles */ #define ECLOCK_DATA_CYCLE 4 static void cia_wait_pre (void) { #ifndef CUSTOM_SIMPLE int div10 = (get_cycles () - eventtab[ev_cia].oldcycles) % DIV10; int cycles; cycles = 5 * CYCLE_UNIT / 2; if (div10 > DIV10 * ECLOCK_DATA_CYCLE / 10) { cycles += DIV10 - div10; cycles += DIV10 * ECLOCK_DATA_CYCLE / 10; } else { cycles += DIV10 * ECLOCK_DATA_CYCLE / 10 - div10; } do_cycles (cycles); #endif } static void cia_wait_post (void) { do_cycles (2 * CYCLE_UNIT / 2); } uae_u32 REGPARAM2 cia_bget (uaecptr addr) { int r = (addr & 0xf00) >> 8; uae_u8 v; #ifdef JIT special_mem |= SPECIAL_MEM_READ; #endif #ifdef CD32 if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END) return akiko_bget (addr); #endif cia_wait_pre (); v = 0xff; switch ((addr >> 12) & 3) { case 0: v = (addr & 1) ? ReadCIAA (r) : ReadCIAB (r); break; case 1: v = (addr & 1) ? 0xff : ReadCIAB (r); break; case 2: v = (addr & 1) ? ReadCIAA (r) : 0xff; break; case 3: if (currprefs.cpu_level == 0 && currprefs.cpu_compatible) v = (addr & 1) ? regs.irc : regs.irc >> 8; if (warned > 0) { write_log ("cia_bget: unknown CIA address %x PC=%x\n", addr, m68k_getpc (®s)); warned--; } break; } cia_wait_post (); return v; } uae_u32 REGPARAM2 cia_wget (uaecptr addr) { int r = (addr & 0xf00) >> 8; uae_u16 v; #ifdef JIT special_mem |= SPECIAL_MEM_READ; #endif #ifdef CD32 if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END) return akiko_wget (addr); #endif cia_wait_pre (); v = 0xffff; switch ((addr >> 12) & 3) { case 0: v = (ReadCIAB (r) << 8) | ReadCIAA (r); break; case 1: v = (ReadCIAB (r) << 8) | 0xff; break; case 2: v = (0xff << 8) | ReadCIAA (r); break; case 3: if (currprefs.cpu_level == 0 && currprefs.cpu_compatible) v = regs.irc; if (warned > 0) { write_log ("cia_wget: unknown CIA address %x PC=%x\n", addr, m68k_getpc (®s)); warned--; } break; } cia_wait_post (); return v; } uae_u32 REGPARAM2 cia_lget (uaecptr addr) { uae_u32 v; #ifdef JIT special_mem |= SPECIAL_MEM_READ; #endif #ifdef CD32 if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END) return akiko_lget (addr); #endif v = cia_wget (addr) << 16; v |= cia_wget (addr + 2); return v; } void REGPARAM2 cia_bput (uaecptr addr, uae_u32 value) { int r = (addr & 0xf00) >> 8; #ifdef JIT special_mem |= SPECIAL_MEM_WRITE; #endif #ifdef CD32 if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END) { akiko_bput (addr, value); return; } #endif cia_wait_pre (); if ((addr & 0x2000) == 0) WriteCIAB (r, value); if ((addr & 0x1000) == 0) WriteCIAA (r, value); if (((addr & 0x3000) == 0x3000) && warned > 0) { write_log ("cia_bput: unknown CIA address %x %x\n", addr, value); warned--; } cia_wait_post (); } void REGPARAM2 cia_wput (uaecptr addr, uae_u32 value) { int r = (addr & 0xf00) >> 8; #ifdef JIT special_mem |= SPECIAL_MEM_WRITE; #endif #ifdef CD32 if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END) { akiko_wput (addr, value); return; } #endif cia_wait_pre (); if ((addr & 0x2000) == 0) WriteCIAB (r, value >> 8); if ((addr & 0x1000) == 0) WriteCIAA (r, value & 0xff); if (((addr & 0x3000) == 0x3000) && warned > 0) { write_log ("cia_wput: unknown CIA address %x %x\n", addr, value); warned--; } cia_wait_post (); } void REGPARAM2 cia_lput (uaecptr addr, uae_u32 value) { #ifdef JIT special_mem |= SPECIAL_MEM_WRITE; #endif #ifdef CD32 if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END) { akiko_lput (addr, value); return; } #endif cia_wput (addr, value >> 16); cia_wput (addr + 2, value & 0xffff); } #ifdef CDTV /* CDTV batterybacked RAM emulation */ static uae_u8 cdtv_battram[4096]; #define CDTV_NVRAM_MASK 4095 #define CDTV_NVRAM_SIZE 2048 int cdtv_enabled; static void cdtv_battram_reset (void) { struct zfile *f = zfile_fopen (currprefs.flashfile,"rb"); if (!f) return; zfile_fread (cdtv_battram, CDTV_NVRAM_SIZE,1 ,f); zfile_fclose (f); } static void cdtv_battram_write (int addr, int v) { struct zfile *f; int offset = addr & CDTV_NVRAM_MASK; if (offset >= CDTV_NVRAM_SIZE) return; if (cdtv_battram[offset] == v) return; cdtv_battram[offset] = v; f = zfile_fopen (currprefs.flashfile,"rb+"); if (!f) return; zfile_fseek (f, offset, SEEK_SET); zfile_fwrite (cdtv_battram + offset, 1, 1, f); zfile_fclose (f); } static uae_u8 cdtv_battram_read (int addr) { uae_u8 v; int offset; offset = addr & CDTV_NVRAM_MASK; if (offset >= CDTV_NVRAM_SIZE) return 0; v = cdtv_battram[offset]; return v; } #endif /* battclock memory access */ static uae_u32 clock_lget (uaecptr) REGPARAM; static uae_u32 clock_wget (uaecptr) REGPARAM; static uae_u32 clock_bget (uaecptr) REGPARAM; static void clock_lput (uaecptr, uae_u32) REGPARAM; static void clock_wput (uaecptr, uae_u32) REGPARAM; static void clock_bput (uaecptr, uae_u32) REGPARAM; addrbank clock_bank = { clock_lget, clock_wget, clock_bget, clock_lput, clock_wput, clock_bput, default_xlate, default_check, NULL }; uae_u32 REGPARAM2 clock_lget (uaecptr addr) { return (clock_wget (addr) << 16) | clock_wget (addr + 2); } uae_u32 REGPARAM2 clock_wget (uaecptr addr) { return (clock_bget (addr) << 8) | clock_bget (addr + 1); } uae_u32 REGPARAM2 clock_bget (uaecptr addr) { time_t t = time(0); struct tm *ct; #ifdef CDTV if (cdtv_enabled && addr >= 0xdc8000) return cdtv_battram_read (addr); #endif ct = localtime (&t); #ifdef JIT special_mem |= SPECIAL_MEM_READ; #endif switch (addr & 0x3f) { case 0x03: return ct->tm_sec % 10; case 0x07: return ct->tm_sec / 10; case 0x0b: return ct->tm_min % 10; case 0x0f: return ct->tm_min / 10; case 0x13: return ct->tm_hour % 10; case 0x17: return ct->tm_hour / 10; case 0x1b: return ct->tm_mday % 10; case 0x1f: return ct->tm_mday / 10; case 0x23: return (ct->tm_mon+1) % 10; case 0x27: return (ct->tm_mon+1) / 10; case 0x2b: return ct->tm_year % 10; case 0x2f: return ct->tm_year / 10; case 0x33: return ct->tm_wday; /*Hack by -=SR=- */ case 0x37: return clock_control_d; case 0x3b: return clock_control_e; case 0x3f: return clock_control_f; } return 0; } void REGPARAM2 clock_lput (uaecptr addr, uae_u32 value) { clock_wput (addr, value >> 16); clock_wput (addr + 2, value); } void REGPARAM2 clock_wput (uaecptr addr, uae_u32 value) { clock_bput (addr, value >> 8); clock_bput (addr + 1, value); } void REGPARAM2 clock_bput (uaecptr addr, uae_u32 value) { #ifdef JIT special_mem |= SPECIAL_MEM_WRITE; #endif #ifdef CDTV if (cdtv_enabled && addr >= 0xdc8000) { cdtv_battram_write (addr, value); return; } #endif switch (addr & 0x3f) { case 0x37: clock_control_d = value; break; case 0x3b: clock_control_e = value; break; case 0x3f: clock_control_f = value; break; } } #ifdef SAVESTATE /* CIA-A and CIA-B save/restore code */ const uae_u8 *restore_cia (unsigned int num, const uae_u8 *src) { uae_u8 b; uae_u16 w; uae_u32 l; /* CIA registers */ b = restore_u8 (); /* 0 PRA */ if (num) ciabpra = b; else ciaapra = b; b = restore_u8 (); /* 1 PRB */ if (num) ciabprb = b; else ciaaprb = b; b = restore_u8 (); /* 2 DDRA */ if (num) ciabdra = b; else ciaadra = b; b = restore_u8 (); /* 3 DDRB */ if (num) ciabdrb = b; else ciaadrb = b; w = restore_u16 (); /* 4 TA */ if (num) ciabta = w; else ciaata = w; w = restore_u16 (); /* 6 TB */ if (num) ciabtb = w; else ciaatb = w; l = restore_u8 (); /* 8/9/A TOD */ l |= restore_u8 () << 8; l |= restore_u8 () << 16; if (num) ciabtod = l; else ciaatod = l; restore_u8 (); /* B unused */ b = restore_u8 (); /* C SDR */ if (num) ciabsdr = b; else ciaasdr = b; b = restore_u8 (); /* D ICR INFORMATION (not mask!) */ if (num) ciabicr = b; else ciaaicr = b; b = restore_u8 (); /* E CRA */ if (num) ciabcra = b; else ciaacra = b; b = restore_u8 (); /* F CRB */ if (num) ciabcrb = b; else ciaacrb = b; /* CIA internal data */ b = restore_u8 (); /* ICR MASK */ if (num) ciabimask = b; else ciaaimask = b; w = restore_u8 (); /* timer A latch */ w |= restore_u8 () << 8; if (num) ciabla = w; else ciaala = w; w = restore_u8 (); /* timer B latch */ w |= restore_u8 () << 8; if (num) ciablb = w; else ciaalb = w; w = restore_u8 (); /* TOD latched value */ w |= restore_u8 () << 8; w |= restore_u8 () << 16; if (num) ciabtol = w; else ciaatol = w; l = restore_u8 (); /* alarm */ l |= restore_u8 () << 8; l |= restore_u8 () << 16; if (num) ciabalarm = l; else ciaaalarm = l; b = restore_u8 (); if (num) ciabtlatch = b & 1; else ciaatlatch = b & 1; /* is TOD latched? */ if (num) ciabtodon = b & 2; else ciaatodon = b & 2; /* is TOD stopped? */ b = restore_u8 (); if (num) div10 = CYCLE_UNIT * b; b = restore_u8 (); if (num) ciabsdr_cnt = b; else ciaasdr_cnt = b; return src; } uae_u8 *save_cia (unsigned int num, uae_u32 *len, uae_u8 *dstptr) { uae_u8 *dstbak,*dst, b; uae_u16 t; if (dstptr) dstbak = dst = dstptr; else dstbak = dst = malloc (16 + 12 + 1 + 1); compute_passed_time (); /* CIA registers */ b = num ? ciabpra : ciaapra; /* 0 PRA */ save_u8 (b); b = num ? ciabprb : ciaaprb; /* 1 PRB */ save_u8 (b); b = num ? ciabdra : ciaadra; /* 2 DDRA */ save_u8 (b); b = num ? ciabdrb : ciaadrb; /* 3 DDRB */ save_u8 (b); t = (num ? ciabta - ciabta_passed : ciaata - ciaata_passed);/* 4 TA */ save_u16 (t); t = (num ? ciabtb - ciabtb_passed : ciaatb - ciaatb_passed);/* 8 TB */ save_u16 (t); b = (num ? ciabtod : ciaatod); /* 8 TODL */ save_u8 (b); b = (num ? ciabtod >> 8 : ciaatod >> 8); /* 9 TODM */ save_u8 (b); b = (num ? ciabtod >> 16 : ciaatod >> 16); /* A TODH */ save_u8 (b); save_u8 (0); /* B unused */ b = num ? ciabsdr : ciaasdr; /* C SDR */ save_u8 (b); b = num ? ciabicr : ciaaicr; /* D ICR INFORMATION (not mask!) */ save_u8 (b); b = num ? ciabcra : ciaacra; /* E CRA */ save_u8 (b); b = num ? ciabcrb : ciaacrb; /* F CRB */ save_u8 (b); /* CIA internal data */ save_u8 (num ? ciabimask : ciaaimask); /* ICR */ b = (num ? ciabla : ciaala); /* timer A latch LO */ save_u8 (b); b = (num ? ciabla >> 8 : ciaala >> 8); /* timer A latch HI */ save_u8 (b); b = (num ? ciablb : ciaalb); /* timer B latch LO */ save_u8 (b); b = (num ? ciablb >> 8 : ciaalb >> 8); /* timer B latch HI */ save_u8 (b); b = (num ? ciabtol : ciaatol); /* latched TOD LO */ save_u8 (b); b = (num ? ciabtol >> 8 : ciaatol >> 8); /* latched TOD MED */ save_u8 (b); b = (num ? ciabtol >> 16 : ciaatol >> 16); /* latched TOD HI */ save_u8 (b); b = (num ? ciabalarm : ciaaalarm); /* alarm LO */ save_u8 (b); b = (num ? ciabalarm >> 8 : ciaaalarm >> 8); /* alarm MED */ save_u8 (b); b = (num ? ciabalarm >> 16 : ciaaalarm >> 16); /* alarm HI */ save_u8 (b); b = 0; if (num) b |= ciabtlatch ? 1 : 0; else b |= ciaatlatch ? 1 : 0; /* is TOD latched? */ if (num) b |= ciabtodon ? 2 : 0; else b |= ciaatodon ? 2 : 0; /* TOD stopped? */ save_u8 (b); save_u8 (div10 / CYCLE_UNIT); save_u8 (num ? ciabsdr_cnt : ciaasdr_cnt); *len = dst - dstbak; return dstbak; } #endif /* SAVESTATE */