mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
3113 lines
68 KiB
C++
3113 lines
68 KiB
C++
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* CIA chip support
|
|
*
|
|
* Copyright 1995 Bernd Schmidt, Alessandro Bissacco
|
|
* Copyright 1996, 1997 Stefan Reinauer, Christian Schmitt
|
|
* Copyright 2022 Toni Wilen
|
|
*/
|
|
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
#include <assert.h>
|
|
|
|
#include "options.h"
|
|
#include "events.h"
|
|
#include "memory.h"
|
|
#include "custom.h"
|
|
#include "newcpu.h"
|
|
#include "cia.h"
|
|
#ifdef SERIAL_PORT
|
|
#include "serial.h"
|
|
#endif
|
|
#include "disk.h"
|
|
#include "xwin.h"
|
|
#include "keybuf.h"
|
|
#include "gui.h"
|
|
#include "savestate.h"
|
|
#include "inputdevice.h"
|
|
#include "zfile.h"
|
|
#include "ar.h"
|
|
#include "parallel.h"
|
|
#include "akiko.h"
|
|
#include "cdtv.h"
|
|
#include "debug.h"
|
|
#include "arcadia.h"
|
|
#include "audio.h"
|
|
#include "keyboard.h"
|
|
#include "uae.h"
|
|
#include "amax.h"
|
|
#include "sampler.h"
|
|
#include "dongle.h"
|
|
#include "inputrecord.h"
|
|
#include "uae/ppc.h"
|
|
#include "rommgr.h"
|
|
#include "scsi.h"
|
|
#include "rtc.h"
|
|
#include "devices.h"
|
|
|
|
#define CIAA_DEBUG_R 0
|
|
#define CIAA_DEBUG_W 0
|
|
#define CIAA_DEBUG_IRQ 0
|
|
|
|
#define CIAB_DEBUG_R 0
|
|
#define CIAB_DEBUG_W 0
|
|
#define CIAB_DEBUG_IRQ 0
|
|
|
|
#define DONGLE_DEBUG 0
|
|
#define KB_DEBUG 0
|
|
#define CLOCK_DEBUG 0
|
|
#define CIA_EVERY_CYCLE_DEBUG 0
|
|
|
|
#define TOD_HACK
|
|
|
|
#define CIA_IRQ_PROCESS_DELAY 0
|
|
|
|
#define CR_START 1
|
|
#define CR_PBON 2
|
|
#define CR_OUTMODE 4
|
|
#define CR_RUNMODE 8
|
|
#define CR_LOAD 0x10
|
|
#define CR_INMODE 0x20
|
|
#define CR_INMODE1 0x40
|
|
#define CR_SPMODE 0x40
|
|
#define CR_ALARM 0x80
|
|
|
|
#define ICR_A 1
|
|
#define ICR_B 2
|
|
#define ICR_ALARM 4
|
|
#define ICR_SP 8
|
|
#define ICR_FLAG 0x10
|
|
#define ICR_MASK 0x1f
|
|
|
|
#define CIA_PIPE_ALL_BITS 2
|
|
#define CIA_PIPE_ALL_MASK ((1 << CIA_PIPE_ALL_BITS) - 1)
|
|
#define CIA_PIPE_INPUT 2
|
|
#define CIA_PIPE_CLR1 1
|
|
#define CIA_PIPE_CLR2 3
|
|
#define CIA_PIPE_OUTPUT 1
|
|
|
|
/* Akiko internal CIA differences:
|
|
|
|
- BFE101 and BFD100: reads 3F if data direction is in.
|
|
|
|
*/
|
|
|
|
#define E_CLOCK_SYNC_N 2
|
|
#define E_CLOCK_START_N 4
|
|
#define E_CLOCK_END_N 6
|
|
#define E_CLOCK_TOD_N -2
|
|
|
|
#define E_CLOCK_SYNC_N2 4
|
|
#define E_CLOCK_START_N2 6
|
|
#define E_CLOCK_END_N2 6
|
|
#define E_CLOCK_TOD_N2 0
|
|
|
|
#define E_CLOCK_SYNC_X 4
|
|
#define E_CLOCK_START_X 2
|
|
#define E_CLOCK_END_X 6
|
|
#define E_CLOCK_TOD_X 0
|
|
|
|
static int e_clock_sync = E_CLOCK_SYNC_N;
|
|
static int e_clock_start = E_CLOCK_START_N;
|
|
static int e_clock_end = E_CLOCK_END_N;
|
|
static int e_clock_tod = E_CLOCK_TOD_N;
|
|
|
|
#define E_CLOCK_LENGTH 10
|
|
#define E_CYCLE_UNIT (CYCLE_UNIT / 2)
|
|
#define DIV10 (E_CLOCK_LENGTH * E_CYCLE_UNIT) /* Yes, a bad identifier. */
|
|
|
|
|
|
struct CIATimer
|
|
{
|
|
uae_u32 timer;
|
|
uae_u32 latch;
|
|
uae_u32 passed;
|
|
uae_u16 inputpipe;
|
|
uae_u32 loaddelay;
|
|
uae_u8 preovfl;
|
|
uae_u8 cr;
|
|
};
|
|
|
|
struct CIA
|
|
{
|
|
uae_u8 pra, prb;
|
|
uae_u8 dra, drb;
|
|
struct CIATimer t[2];
|
|
uae_u32 tod;
|
|
uae_u32 tol;
|
|
uae_u32 alarm;
|
|
uae_u32 tlatch;
|
|
uae_u8 todon;
|
|
int tod_event_state;
|
|
int tod_offset;
|
|
uae_u8 icr1, icr2;
|
|
bool icr_change;
|
|
uae_u8 imask;
|
|
uae_u8 sdr;
|
|
uae_u8 sdr_buf;
|
|
uae_u8 sdr_load;
|
|
uae_u8 sdr_cnt;
|
|
};
|
|
|
|
static struct CIA cia[2];
|
|
|
|
static bool oldovl;
|
|
static int led;
|
|
static int led_old_brightness;
|
|
static evt_t led_cycle;
|
|
static evt_t cia_now_evt;
|
|
static int led_cycles_on, led_cycles_off;
|
|
|
|
static int kbstate, kblostsynccnt;
|
|
static evt_t kbhandshakestart;
|
|
static uae_u8 kbcode;
|
|
|
|
static uae_u8 serbits;
|
|
static int warned = 100;
|
|
|
|
static struct rtc_msm_data rtc_msm;
|
|
static struct rtc_ricoh_data rtc_ricoh;
|
|
|
|
static int internaleclockphase;
|
|
static bool cia_cycle_accurate;
|
|
|
|
static bool acc_mode(void)
|
|
{
|
|
return cia_cycle_accurate;
|
|
}
|
|
|
|
int blop, blop2;
|
|
|
|
void cia_adjust_eclock_phase(int diff)
|
|
{
|
|
internaleclockphase += diff;
|
|
if (internaleclockphase < 0) {
|
|
internaleclockphase += ((-internaleclockphase) / 20) * 20;
|
|
internaleclockphase += 20;
|
|
}
|
|
internaleclockphase %= 20;
|
|
//write_log("CIA E-clock phase %d\n", internaleclockphase);
|
|
}
|
|
|
|
static void set_eclockphase(void)
|
|
{
|
|
if (currprefs.cs_eclocksync == 3) {
|
|
e_clock_sync = E_CLOCK_SYNC_X;
|
|
e_clock_start = E_CLOCK_START_X;
|
|
e_clock_end = E_CLOCK_END_X;
|
|
e_clock_tod = E_CLOCK_TOD_X;
|
|
} else if (currprefs.cs_eclocksync == 2) {
|
|
e_clock_sync = E_CLOCK_SYNC_N2;
|
|
e_clock_start = E_CLOCK_START_N2;
|
|
e_clock_end = E_CLOCK_END_N2;
|
|
e_clock_tod = E_CLOCK_TOD_N2;
|
|
} else {
|
|
e_clock_sync = E_CLOCK_SYNC_N;
|
|
e_clock_start = E_CLOCK_START_N;
|
|
e_clock_end = E_CLOCK_END_N;
|
|
e_clock_tod = E_CLOCK_TOD_N;
|
|
}
|
|
}
|
|
|
|
static evt_t get_e_cycles(void)
|
|
{
|
|
// temporary e-clock phase shortcut
|
|
if (blop) {
|
|
cia_adjust_eclock_phase(1);
|
|
blop = 0;
|
|
}
|
|
if (blop2) {
|
|
if (currprefs.cs_eclocksync == 0) {
|
|
currprefs.cs_eclocksync = 1;
|
|
}
|
|
currprefs.cs_eclocksync += 1;
|
|
if (currprefs.cs_eclocksync >= 4) {
|
|
currprefs.cs_eclocksync = 1;
|
|
}
|
|
changed_prefs.cs_eclocksync = currprefs.cs_eclocksync;
|
|
set_eclockphase();
|
|
write_log("CIA elock timing mode %d\n", currprefs.cs_eclocksync);
|
|
blop2 = 0;
|
|
}
|
|
|
|
evt_t c = get_cycles();
|
|
c += currprefs.cs_eclockphase * E_CYCLE_UNIT;
|
|
c += internaleclockphase * 2 * E_CYCLE_UNIT;
|
|
return c;
|
|
}
|
|
|
|
static void setclr(uae_u8 *p, uae_u8 val)
|
|
{
|
|
if (val & 0x80) {
|
|
*p |= val & 0x7F;
|
|
} else {
|
|
*p &= ~val;
|
|
}
|
|
}
|
|
|
|
#if CIA_IRQ_PROCESS_DELAY
|
|
/* delay interrupt after current CIA register access if
|
|
* interrupt would have triggered mid access
|
|
*/
|
|
static int cia_interrupt_disabled;
|
|
static int cia_interrupt_delay;
|
|
#endif
|
|
|
|
static void ICRIRQ(uae_u32 data)
|
|
{
|
|
safe_interrupt_set(IRQ_SOURCE_CIA, 0, (data & 0x2000) != 0);
|
|
}
|
|
|
|
static void ICR(uae_u32 num)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
|
|
#if CIA_IRQ_PROCESS_DELAY
|
|
if (currprefs.cpu_memory_cycle_exact && !(c->icr & 0x20) && (cia_interrupt_disabled & (1 << num))) {
|
|
c->cia_interrupt_delay |= 1 << num;
|
|
#if CIAB_DEBUG_IRQ
|
|
write_log(_T("cia%c interrupt disabled ICR=%02X PC=%x\n"), num ? 'b' : 'a', c->icr, M68K_GETPC);
|
|
#endif
|
|
return;
|
|
}
|
|
#endif
|
|
c->icr1 |= 0x20;
|
|
if (num && currprefs.cs_compatible != CP_VELVET) {
|
|
ICRIRQ(0x2000);
|
|
} else {
|
|
ICRIRQ(0x0008);
|
|
}
|
|
}
|
|
|
|
static void RethinkICR(int num)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
|
|
if (c->icr1 & c->imask & ICR_MASK) {
|
|
#if CIAA_DEBUG_IRQ
|
|
write_log(_T("CIA%c IRQ %02X %02X\n"), num ? 'B' : 'A', c->icr1, c->icr2);
|
|
#endif
|
|
if (!(c->icr1 & 0x80)) {
|
|
c->icr1 |= 0x80 | 0x40;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(num ? DMA_EVENT_CIAB_IRQ : DMA_EVENT_CIAA_IRQ, current_hpos(), vpos);
|
|
}
|
|
#endif
|
|
ICR(num);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void rethink_cias(void)
|
|
{
|
|
if (cia[0].icr1 & 0x40) {
|
|
ICRIRQ(0x0008);
|
|
}
|
|
if (cia[1].icr1 & 0x40) {
|
|
if (currprefs.cs_compatible == CP_VELVET) {
|
|
ICRIRQ(0x0008);
|
|
} else {
|
|
ICRIRQ(0x2000);
|
|
}
|
|
}
|
|
}
|
|
|
|
static uae_u16 bitstodelay(uae_u16 v)
|
|
{
|
|
switch (v)
|
|
{
|
|
case 0:
|
|
return CIA_PIPE_ALL_BITS - 0;
|
|
case 1:
|
|
case 2:
|
|
#if CIA_PIPE_ALL_BITS > 2
|
|
case 4:
|
|
#endif
|
|
return CIA_PIPE_ALL_BITS - 1;
|
|
case 3:
|
|
#if CIA_PIPE_ALL_BITS > 2
|
|
case 5:
|
|
case 6:
|
|
#endif
|
|
return CIA_PIPE_ALL_BITS - 2;
|
|
#if CIA_PIPE_ALL_BITS > 2
|
|
case 7:
|
|
return CIA_PIPE_ALL_BITS - 3;
|
|
#endif
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Figure out how many CIA timer cycles have passed for each timer since the
|
|
last call of CIA_calctimers. */
|
|
|
|
static void compute_passed_time_cia(int num, uae_u32 ciaclocks)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
|
|
c->t[0].passed = 0;
|
|
c->t[1].passed = 0;
|
|
|
|
if ((c->t[0].cr & (CR_INMODE | CR_START)) == CR_START) {
|
|
uae_u32 cc = ciaclocks;
|
|
int pipe = bitstodelay(c->t[0].inputpipe);
|
|
if (cc > pipe) {
|
|
cc -= pipe;
|
|
} else {
|
|
cc = 0;
|
|
}
|
|
c->t[0].passed = cc;
|
|
assert(cc < 65536);
|
|
}
|
|
if ((c->t[1].cr & (CR_INMODE | CR_INMODE1 | CR_START)) == CR_START) {
|
|
uae_u32 cc = ciaclocks;
|
|
int pipe = bitstodelay(c->t[1].inputpipe);
|
|
if (cc > pipe) {
|
|
cc -= pipe;
|
|
} else {
|
|
cc = 0;
|
|
}
|
|
c->t[1].passed = cc;
|
|
assert(cc < 65536);
|
|
}
|
|
}
|
|
|
|
static void compute_passed_time(void)
|
|
{
|
|
evt_t ccount = get_cycles() - eventtab[ev_cia].oldcycles;
|
|
if (ccount > MAXINT) {
|
|
ccount = MAXINT;
|
|
}
|
|
uae_u32 ciaclocks = (uae_u32)ccount / DIV10;
|
|
|
|
compute_passed_time_cia(0, ciaclocks);
|
|
compute_passed_time_cia(1, ciaclocks);
|
|
}
|
|
|
|
static void timer_reset(struct CIATimer *t)
|
|
{
|
|
t->timer = t->latch;
|
|
if (acc_mode()) {
|
|
if (t->cr & CR_RUNMODE) {
|
|
t->inputpipe &= ~CIA_PIPE_CLR1;
|
|
} else {
|
|
t->inputpipe &= ~CIA_PIPE_CLR2;
|
|
}
|
|
}
|
|
}
|
|
|
|
static uae_u8 cia_inmode_cnt(int num)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
uae_u8 icr = 0;
|
|
bool decb = false;
|
|
|
|
// A INMODE=1 (count CNT pulses)
|
|
if ((c->t[0].cr & (CR_INMODE | CR_START)) == (CR_INMODE | CR_START)) {
|
|
if (c->t[0].timer == 0) {
|
|
icr |= ICR_A;
|
|
timer_reset(&c->t[0]);
|
|
if (c->t[0].cr & CR_RUNMODE) {
|
|
c->t[0].cr &= ~CR_START;
|
|
}
|
|
// B INMODE = 1x (count A undeflows)
|
|
if ((c->t[1].cr & (CR_INMODE1 | CR_START)) == (CR_INMODE1 | CR_START)) {
|
|
decb = true;
|
|
}
|
|
} else {
|
|
c->t[0].timer--;
|
|
}
|
|
}
|
|
// B INMODE=01 (count CNT pulses)
|
|
if ((c->t[1].cr & (CR_INMODE1 | CR_INMODE | CR_START)) == (CR_INMODE | CR_START)) {
|
|
decb = 1;
|
|
}
|
|
|
|
if (decb) {
|
|
if (c->t[1].timer == 0) {
|
|
icr |= ICR_B;
|
|
timer_reset(&c->t[1]);
|
|
if (c->t[1].cr & CR_RUNMODE) {
|
|
c->t[1].cr &= ~CR_START;
|
|
}
|
|
} else {
|
|
c->t[1].timer--;
|
|
}
|
|
}
|
|
return icr;
|
|
}
|
|
|
|
static int process_pipe(struct CIATimer *t, int cc, uae_u8 crmask, int *ovfl, int loadednow)
|
|
{
|
|
int ccout = cc;
|
|
|
|
if (cc == 1 && acc_mode()) {
|
|
int out = t->inputpipe & CIA_PIPE_OUTPUT;
|
|
t->inputpipe >>= 1;
|
|
if ((t->cr & crmask) == CR_START) {
|
|
t->inputpipe |= CIA_PIPE_INPUT;
|
|
}
|
|
// interrupt 1 cycle early if timer is already zero
|
|
if (t->timer == 0 && t->latch == 0 && (t->inputpipe & CIA_PIPE_OUTPUT)) {
|
|
*ovfl = loadednow ? 1 : 2;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
while (t->inputpipe != CIA_PIPE_ALL_MASK && cc > 0) {
|
|
if (!(t->inputpipe & CIA_PIPE_OUTPUT)) {
|
|
ccout--;
|
|
}
|
|
t->inputpipe >>= 1;
|
|
if ((t->cr & crmask) == CR_START) {
|
|
t->inputpipe |= CIA_PIPE_INPUT;
|
|
}
|
|
cc--;
|
|
}
|
|
|
|
return ccout;
|
|
}
|
|
|
|
/* 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_check(void)
|
|
{
|
|
evt_t ccount = get_cycles() - eventtab[ev_cia].oldcycles;
|
|
if (ccount > MAXINT) {
|
|
ccount = MAXINT;
|
|
}
|
|
int ciaclocks = (uae_u32)(ccount / DIV10);
|
|
if (!ciaclocks) {
|
|
return;
|
|
}
|
|
|
|
uae_u8 icr = 0;
|
|
for (int num = 0; num < 2; num++) {
|
|
struct CIA *c = &cia[num];
|
|
int ovfl[2], sp;
|
|
bool loaded[2], loaded2[2], loaded3[3];
|
|
|
|
c->icr1 |= c->icr2;
|
|
c->icr2 = 0;
|
|
c->icr_change = false;
|
|
|
|
ovfl[0] = 0;
|
|
ovfl[1] = 0;
|
|
sp = 0;
|
|
|
|
for (int tn = 0; tn < 2; tn++) {
|
|
struct CIATimer *t = &c->t[tn];
|
|
|
|
loaded[tn] = false;
|
|
loaded2[tn] = false;
|
|
loaded3[tn] = false;
|
|
|
|
// CIA special cases
|
|
if (t->loaddelay) {
|
|
if (ciaclocks > 1) {
|
|
abort();
|
|
}
|
|
|
|
if (t->loaddelay & 0x00000001) {
|
|
t->timer = t->latch;
|
|
t->inputpipe &= ~CIA_PIPE_CLR1;
|
|
}
|
|
|
|
// timer=0 special cases. TODO: better way to do this..
|
|
// delayed timer stop and interrupt (timer=0 condition)
|
|
if ((t->loaddelay & 0x00010000)) {
|
|
t->cr &= ~CR_START;
|
|
ovfl[tn] = 2;
|
|
}
|
|
// Do not set START=0 until timer has started (timer==0 special case)
|
|
if ((t->loaddelay & 0x00000100) && t->timer == 0) {
|
|
loaded2[tn] = true;
|
|
}
|
|
if ((t->loaddelay & 0x01000000)) {
|
|
loaded[tn] = true;
|
|
}
|
|
if ((t->loaddelay & 0x10000000)) {
|
|
loaded3[tn] = true;
|
|
}
|
|
|
|
t->loaddelay >>= 1;
|
|
t->loaddelay &= 0x77777777;
|
|
}
|
|
}
|
|
|
|
// Timer A
|
|
int cc = 0;
|
|
if ((c->t[0].cr & (CR_INMODE | CR_START)) == CR_START || c->t[0].inputpipe) {
|
|
cc = process_pipe(&c->t[0], ciaclocks, CR_INMODE | CR_START, &ovfl[0], loaded3[0]);
|
|
}
|
|
if (cc > 0) {
|
|
c->t[0].timer -= cc;
|
|
if (c->t[0].timer == 0) {
|
|
// SP in output mode (data sent can be ignored if CIA-A)
|
|
if ((c->t[0].cr & (CR_SPMODE | CR_RUNMODE)) == CR_SPMODE && c->sdr_cnt > 0) {
|
|
c->sdr_cnt--;
|
|
if (c->sdr_cnt == 0) {
|
|
sp = 1;
|
|
if (c->sdr_load) {
|
|
c->sdr_load = 0;
|
|
c->sdr_buf = c->sdr;
|
|
c->sdr_cnt = 8 * 2;
|
|
}
|
|
}
|
|
}
|
|
ovfl[0] = 2;
|
|
}
|
|
}
|
|
assert(c->t[0].timer < 0x10000);
|
|
|
|
// Timer B
|
|
cc = 0;
|
|
if ((c->t[1].cr & (CR_INMODE | CR_INMODE1 | CR_START)) == CR_START || c->t[1].inputpipe) {
|
|
cc = process_pipe(&c->t[1], ciaclocks, CR_INMODE | CR_INMODE1 | CR_START, &ovfl[1], loaded3[1]);
|
|
}
|
|
if (cc > 0) {
|
|
if ((c->t[1].timer == 0 && (c->t[1].cr & (CR_INMODE | CR_INMODE1)))) {
|
|
ovfl[1] = 2;
|
|
} else {
|
|
c->t[1].timer -= cc;
|
|
if ((c->t[1].timer == 0 && !(c->t[1].cr & (CR_INMODE | CR_INMODE1)))) {
|
|
ovfl[1] = 2;
|
|
}
|
|
}
|
|
}
|
|
assert(c->t[1].timer < 0x10000);
|
|
|
|
// B INMODE=10 or 11 (B counting A underflows)
|
|
if (ovfl[0] && ((c->t[1].cr & (CR_INMODE | CR_INMODE1 | CR_START)) == (CR_INMODE1 | CR_START) || (c->t[1].cr & (CR_INMODE | CR_INMODE1 | CR_START)) == (CR_INMODE | CR_INMODE1 | CR_START))) {
|
|
c->t[1].inputpipe |= CIA_PIPE_INPUT;
|
|
}
|
|
|
|
for (int tn = 0; tn < 2; tn++) {
|
|
struct CIATimer *t = &c->t[tn];
|
|
|
|
if (ovfl[tn] || t->preovfl) {
|
|
if (ovfl[tn]) {
|
|
if (ovfl[tn] > 1) {
|
|
c->icr2 |= tn ? ICR_B : ICR_A;
|
|
icr |= 1 << num;
|
|
}
|
|
t->timer = t->latch;
|
|
}
|
|
if (!loaded[tn]) {
|
|
if (t->cr & CR_RUNMODE) {
|
|
if (loaded2[tn]) {
|
|
t->loaddelay |= 0x00010000;
|
|
} else {
|
|
t->cr &= ~CR_START;
|
|
}
|
|
if (!acc_mode()) {
|
|
t->inputpipe = 0;
|
|
}
|
|
if (acc_mode()) {
|
|
t->inputpipe &= ~CIA_PIPE_CLR2;
|
|
}
|
|
} else {
|
|
if (acc_mode()) {
|
|
t->inputpipe &= ~CIA_PIPE_CLR1;
|
|
}
|
|
}
|
|
}
|
|
t->preovfl = false;
|
|
}
|
|
}
|
|
|
|
if (sp) {
|
|
c->icr2 |= ICR_SP;
|
|
icr |= 1 << num;
|
|
}
|
|
|
|
if (!acc_mode()) {
|
|
c->icr1 |= c->icr2;
|
|
c->icr2 = 0;
|
|
} else {
|
|
if (icr) {
|
|
c->icr_change = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CIA_check_ICR(void)
|
|
{
|
|
if (cia[0].icr1 & ICR_MASK) {
|
|
RethinkICR(0);
|
|
}
|
|
if (cia[1].icr1 & ICR_MASK) {
|
|
RethinkICR(1);
|
|
}
|
|
}
|
|
|
|
static void CIA_update(void)
|
|
{
|
|
CIA_update_check();
|
|
CIA_check_ICR();
|
|
}
|
|
|
|
/* Call this only after CIA_update has been called in the same cycle. */
|
|
static void CIA_calctimers(void)
|
|
{
|
|
uae_s32 timevals[4];
|
|
|
|
timevals[0] = -1;
|
|
timevals[1] = -1;
|
|
timevals[2] = -1;
|
|
timevals[3] = -1;
|
|
|
|
eventtab[ev_cia].oldcycles = get_cycles();
|
|
|
|
for (int num = 0; num < 2; num++) {
|
|
struct CIA *c = &cia[num];
|
|
int idx = num * 2;
|
|
bool counting[2] = { false, false };
|
|
|
|
if ((c->t[0].cr & (CR_INMODE | CR_START)) == CR_START) {
|
|
int pipe = bitstodelay(c->t[0].inputpipe);
|
|
timevals[idx + 0] = DIV10 * (c->t[0].timer + pipe);
|
|
if (!timevals[idx + 0]) {
|
|
timevals[idx + 0] = DIV10;
|
|
}
|
|
counting[0] = true;
|
|
}
|
|
|
|
if ((c->t[1].cr & (CR_INMODE | CR_INMODE1 | CR_START)) == CR_START) {
|
|
int pipe = bitstodelay(c->t[1].inputpipe);
|
|
timevals[idx + 1] = DIV10 * (c->t[1].timer + pipe);
|
|
if (!timevals[idx + 1]) {
|
|
timevals[idx + 1] = DIV10;
|
|
}
|
|
counting[1] = true;
|
|
}
|
|
|
|
for (int tn = 0; tn < 2; tn++) {
|
|
struct CIATimer *t = &c->t[tn];
|
|
bool timerspecial = t->loaddelay != 0;
|
|
int tnidx = idx + tn;
|
|
if (t->cr & CR_START) {
|
|
if (t->inputpipe != CIA_PIPE_ALL_MASK) {
|
|
if (counting[tn] || t->inputpipe != 0) {
|
|
timerspecial = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (t->inputpipe != 0) {
|
|
timerspecial = true;
|
|
}
|
|
}
|
|
if (timerspecial && (timevals[tnidx] < 0 || timevals[tnidx] > DIV10)) {
|
|
timevals[tnidx] = DIV10;
|
|
}
|
|
}
|
|
|
|
if (c->icr_change && (timevals[idx] < 0 || timevals[idx] > DIV10)) {
|
|
timevals[idx] = DIV10;
|
|
}
|
|
|
|
#if CIA_EVERY_CYCLE_DEBUG
|
|
timevals[idx] = DIV10;
|
|
#endif
|
|
|
|
}
|
|
|
|
uae_s32 ciatime = INT_MAX;
|
|
if (timevals[0] >= 0)
|
|
ciatime = timevals[0];
|
|
if (timevals[1] >= 0 && timevals[1] < ciatime)
|
|
ciatime = timevals[1];
|
|
if (timevals[2] >= 0 && timevals[2] < ciatime)
|
|
ciatime = timevals[2];
|
|
if (timevals[3] >= 0 && timevals[3] < ciatime)
|
|
ciatime = timevals[3];
|
|
if (ciatime < INT_MAX) {
|
|
eventtab[ev_cia].evtime = get_cycles() + ciatime;
|
|
eventtab[ev_cia].active = true;
|
|
} else {
|
|
eventtab[ev_cia].active = false;
|
|
}
|
|
|
|
events_schedule();
|
|
}
|
|
|
|
void CIA_handler(void)
|
|
{
|
|
CIA_update();
|
|
CIA_calctimers();
|
|
}
|
|
|
|
static int get_cia_sync_cycles(int *syncdelay)
|
|
{
|
|
evt_t c = get_e_cycles();
|
|
int div10 = c % DIV10;
|
|
int add = 0;
|
|
int synccycle = e_clock_sync * E_CYCLE_UNIT;
|
|
if (div10 < synccycle) {
|
|
add += synccycle - div10;
|
|
} else if (div10 > synccycle) {
|
|
add += DIV10 - div10;
|
|
add += synccycle;
|
|
}
|
|
*syncdelay = add;
|
|
// 4 first cycles of E-clock
|
|
add = e_clock_start * E_CYCLE_UNIT;
|
|
return add;
|
|
}
|
|
|
|
void event_CIA_synced_interrupt(uae_u32 v)
|
|
{
|
|
CIA_update();
|
|
CIA_calctimers();
|
|
}
|
|
|
|
static void CIA_sync_interrupt(int num, uae_u8 icr)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
|
|
if (acc_mode()) {
|
|
if (!(icr & c->imask)) {
|
|
c->icr1 |= icr;
|
|
return;
|
|
}
|
|
c->icr2 |= icr;
|
|
if ((c->icr1 & ICR_MASK) == (c->icr2 & ICR_MASK)) {
|
|
return;
|
|
}
|
|
int syncdelay = 0;
|
|
int delay = get_cia_sync_cycles(&syncdelay);
|
|
delay += syncdelay;
|
|
event2_newevent_xx(-1, DIV10 + delay, num, event_CIA_synced_interrupt);
|
|
} else {
|
|
c->icr1 |= icr;
|
|
CIA_check_ICR();
|
|
}
|
|
}
|
|
|
|
void cia_diskindex(void)
|
|
{
|
|
CIA_sync_interrupt(1, ICR_FLAG);
|
|
}
|
|
void cia_parallelack(void)
|
|
{
|
|
CIA_sync_interrupt(0, ICR_FLAG);
|
|
}
|
|
|
|
static bool checkalarm(uae_u32 tod, uae_u32 alarm, bool inc)
|
|
{
|
|
if (tod == alarm)
|
|
return true;
|
|
if (!currprefs.cs_ciatodbug)
|
|
return false;
|
|
if (!inc)
|
|
return false;
|
|
/* emulate buggy TODMED counter.
|
|
* it counts: .. 29 2A 2B 2C 2D 2E 2F 20 30 31 32 ..
|
|
* (2F->20->30 only takes couple of cycles but it will trigger alarm..
|
|
*/
|
|
if (tod & 0x000fff)
|
|
return false;
|
|
if (((tod - 1) & 0xfff000) == alarm)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool cia_checkalarm(bool inc, bool irq, int num)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
|
|
#if 0
|
|
// hack: do not trigger alarm interrupt if KS code and both
|
|
// tod and alarm == 0. This incorrectly triggers on non-cycle exact
|
|
// modes. Real hardware value written to ciabtod by KS is always
|
|
// at least 1 or larger due to bus cycle delays when reading
|
|
// old value.
|
|
if (num) {
|
|
if (!currprefs.cpu_compatible && (munge24(m68k_getpc()) & 0xFFF80000) != 0xF80000) {
|
|
if (c->tod == 0 && c->alarm == 0)
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
if (checkalarm(c->tod, c->alarm, inc)) {
|
|
#if CIAB_DEBUG_IRQ
|
|
write_log(_T("CIAB tod %08x %08x\n"), c->tod, c->alarm);
|
|
#endif
|
|
if (irq) {
|
|
CIA_sync_interrupt(num, ICR_ALARM);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifdef TOD_HACK
|
|
static uae_u64 tod_hack_tv, tod_hack_tod, tod_hack_tod_last;
|
|
static int tod_hack_enabled;
|
|
static int tod_hack_delay;
|
|
static int tod_diff_cnt;
|
|
#define TOD_HACK_DELAY 50
|
|
#define TOD_HACK_TIME 312 * 50 * 10
|
|
|
|
static void tod_hack_reset(void)
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
tod_hack_tv = (uae_u64)tv.tv_sec * 1000000 + tv.tv_usec;
|
|
tod_hack_tod = cia[0].tod;
|
|
tod_hack_tod_last = tod_hack_tod;
|
|
tod_diff_cnt = 0;
|
|
}
|
|
#endif
|
|
|
|
static int heartbeat_cnt;
|
|
void cia_heartbeat(void)
|
|
{
|
|
heartbeat_cnt = 10;
|
|
}
|
|
|
|
static void do_tod_hack(bool dotod)
|
|
{
|
|
struct timeval tv;
|
|
static int oldrate;
|
|
uae_u64 t;
|
|
int rate;
|
|
int docount = 0;
|
|
|
|
if (tod_hack_enabled == 0)
|
|
return;
|
|
if (!heartbeat_cnt) {
|
|
if (tod_hack_enabled > 0)
|
|
tod_hack_enabled = -1;
|
|
return;
|
|
}
|
|
if (tod_hack_enabled < 0) {
|
|
tod_hack_enabled = TOD_HACK_TIME;
|
|
return;
|
|
}
|
|
if (tod_hack_enabled > 1) {
|
|
tod_hack_enabled--;
|
|
if (tod_hack_enabled == 1) {
|
|
//write_log(_T("TOD HACK enabled\n"));
|
|
tod_hack_reset();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (currprefs.cs_ciaatod == 0) {
|
|
rate = (int)(vblank_hz + 0.5);
|
|
if (rate >= 59 && rate <= 61)
|
|
rate = 60;
|
|
if (rate >= 49 && rate <= 51)
|
|
rate = 50;
|
|
} else if (currprefs.cs_ciaatod == 1) {
|
|
rate = 50;
|
|
} else {
|
|
rate = 60;
|
|
}
|
|
if (rate <= 0)
|
|
return;
|
|
if (rate != oldrate || (cia[0].tod & 0xfff) != (tod_hack_tod_last & 0xfff)) {
|
|
write_log(_T("TOD HACK reset %d,%d %ld,%lld\n"), rate, oldrate, cia[0].tod, tod_hack_tod_last);
|
|
tod_hack_reset();
|
|
oldrate = rate;
|
|
docount = 1;
|
|
}
|
|
|
|
if (!dotod && currprefs.cs_ciaatod == 0)
|
|
return;
|
|
|
|
if (tod_hack_delay > 0) {
|
|
tod_hack_delay--;
|
|
if (tod_hack_delay > 0)
|
|
return;
|
|
tod_hack_delay = TOD_HACK_DELAY;
|
|
}
|
|
|
|
gettimeofday(&tv, NULL);
|
|
t = (uae_u64)tv.tv_sec * 1000000 + tv.tv_usec;
|
|
if (t - tod_hack_tv >= 1000000 / rate) {
|
|
tod_hack_tv += 1000000 / rate;
|
|
tod_diff_cnt += 1000000 - (1000000 / rate) * rate;
|
|
tod_hack_tv += tod_diff_cnt / rate;
|
|
tod_diff_cnt %= rate;
|
|
docount = 1;
|
|
}
|
|
if (docount) {
|
|
cia[0].tod++;
|
|
cia[0].tod &= 0x00ffffff;
|
|
tod_hack_tod_last = cia[0].tod;
|
|
cia_checkalarm(false, true, 0);
|
|
}
|
|
}
|
|
|
|
static int resetwarning_phase, resetwarning_timer;
|
|
|
|
static void setcode(uae_u8 keycode)
|
|
{
|
|
kbcode = ~((keycode << 1) | (keycode >> 7));
|
|
}
|
|
|
|
static void sendrw(void)
|
|
{
|
|
setcode(AK_RESETWARNING);
|
|
cia[0].sdr = kbcode;
|
|
kblostsynccnt = 8 * maxvpos * 8; // 8 frames * 8 bits.
|
|
CIA_sync_interrupt(0, ICR_SP);
|
|
write_log(_T("KB: sent reset warning code (phase=%d)\n"), resetwarning_phase);
|
|
}
|
|
|
|
int resetwarning_do(int canreset)
|
|
{
|
|
if (!currprefs.keyboard_connected)
|
|
return 0;
|
|
if (resetwarning_phase || regs.halted > 0) {
|
|
/* just force reset if second reset happens during resetwarning */
|
|
if (canreset) {
|
|
resetwarning_phase = 0;
|
|
resetwarning_timer = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
resetwarning_phase = 1;
|
|
resetwarning_timer = maxvpos_nom * 5;
|
|
write_log(_T("KB: reset warning triggered\n"));
|
|
sendrw();
|
|
return 1;
|
|
}
|
|
|
|
static void resetwarning_check(void)
|
|
{
|
|
if (resetwarning_timer > 0) {
|
|
resetwarning_timer--;
|
|
if (resetwarning_timer <= 0) {
|
|
write_log(_T("KB: reset warning forced reset. Phase=%d\n"), resetwarning_phase);
|
|
resetwarning_phase = -1;
|
|
kblostsynccnt = 0;
|
|
inputdevice_do_kb_reset();
|
|
}
|
|
}
|
|
if (resetwarning_phase == 1) {
|
|
if (!kblostsynccnt) { /* first AK_RESETWARNING handshake received */
|
|
write_log(_T("KB: reset warning second phase..\n"));
|
|
resetwarning_phase = 2;
|
|
resetwarning_timer = maxvpos_nom * 5;
|
|
sendrw();
|
|
}
|
|
} else if (resetwarning_phase == 2) {
|
|
if (cia[0].t[0].cr & CR_SPMODE) { /* second AK_RESETWARNING handshake active */
|
|
resetwarning_phase = 3;
|
|
write_log(_T("KB: reset warning SP = output\n"));
|
|
/* System won't reset until handshake signal becomes inactive or 10s has passed */
|
|
resetwarning_timer = (int)(10 * maxvpos_nom * vblank_hz);
|
|
}
|
|
} else if (resetwarning_phase == 3) {
|
|
if (!(cia[0].t[0].cr & CR_SPMODE)) { /* second AK_RESETWARNING handshake disabled */
|
|
write_log(_T("KB: reset warning end by software. reset.\n"));
|
|
resetwarning_phase = -1;
|
|
kblostsynccnt = 0;
|
|
inputdevice_do_kb_reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CIA_hsync_prehandler (void)
|
|
{
|
|
}
|
|
|
|
static void keyreq (void)
|
|
{
|
|
#if KB_DEBUG
|
|
write_log(_T("code=%02x (%02x)\n"), kbcode, (uae_u8)(~((kbcode >> 1) | (kbcode << 7))));
|
|
#endif
|
|
cia[0].sdr = kbcode;
|
|
kblostsynccnt = 8 * maxvpos * 8; // 8 frames * 8 bits.
|
|
CIA_sync_interrupt(0, ICR_SP);
|
|
}
|
|
|
|
/* All this complexity to lazy evaluate CIA-B TOD increase.
|
|
* Only increase it cycle-exactly if it is visible to running program:
|
|
* causes interrupt (ALARM) or program is reading or writing TOD registers
|
|
*/
|
|
|
|
// TOD increase has extra delay.
|
|
#define TOD_INC_DELAY (12 * E_CLOCK_LENGTH / 2)
|
|
|
|
static int tod_inc_delay(int hoffset)
|
|
{
|
|
int hoff = hoffset + 1; // 1 = HSYNC/VSYNC Agnus pin output is delayed by 1 CCK
|
|
evt_t c = get_e_cycles() + 6 * E_CYCLE_UNIT + hoff * CYCLE_UNIT;
|
|
int offset = hoff;
|
|
offset += TOD_INC_DELAY;
|
|
int unit = (E_CLOCK_LENGTH * 4) / 2; // 4 E-clocks
|
|
int div10 = (c / CYCLE_UNIT) % unit;
|
|
offset += unit - div10;
|
|
offset += e_clock_tod;
|
|
return offset;
|
|
}
|
|
|
|
static void CIA_tod_inc(bool irq, int num)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
c->tod_event_state = 3; // done
|
|
if (!c->todon) {
|
|
return;
|
|
}
|
|
c->tod++;
|
|
c->tod &= 0xFFFFFF;
|
|
cia_checkalarm(true, irq, num);
|
|
}
|
|
|
|
void event_CIA_tod_inc_event(uae_u32 num)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
if (c->tod_event_state != 2) {
|
|
return;
|
|
}
|
|
CIA_tod_inc(true, num);
|
|
}
|
|
|
|
static void CIA_tod_event_check(int num)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
if (c->tod_event_state == 1) {
|
|
int hpos = current_hpos();
|
|
if (hpos >= c->tod_offset) {
|
|
CIA_tod_inc(false, num);
|
|
c->tod_event_state = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Someone reads or writes TOD registers, sync TOD increase
|
|
static void CIA_tod_check(int num)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
|
|
CIA_tod_event_check(num);
|
|
if (!c->todon || c->tod_event_state != 1 || c->tod_offset < 0)
|
|
return;
|
|
int hpos = current_hpos();
|
|
hpos -= c->tod_offset;
|
|
if (hpos >= 0 || !acc_mode()) {
|
|
// Program should see the changed TOD
|
|
CIA_tod_inc(true, num);
|
|
return;
|
|
}
|
|
// Not yet, add event to guarantee exact TOD inc position
|
|
c->tod_event_state = 2; // event active
|
|
event2_newevent_xx(-1, -hpos * CYCLE_UNIT, num, event_CIA_tod_inc_event);
|
|
}
|
|
|
|
static void CIA_tod_handler(int hoffset, int num, bool delayedevent)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
c->tod_event_state = 0;
|
|
c->tod_offset = tod_inc_delay(hoffset);
|
|
if (c->tod_offset >= maxhpos) {
|
|
if (!delayedevent) {
|
|
return;
|
|
}
|
|
// crossed scanline, increase in next line
|
|
c->tod_offset -= maxhpos;
|
|
c->tod_event_state = 4;
|
|
return;
|
|
}
|
|
c->tod_event_state = 1; // TOD inc needed
|
|
if (checkalarm((c->tod + 1) & 0xffffff, c->alarm, true)) {
|
|
// causes interrupt on this line, add event
|
|
c->tod_event_state = 2; // event active
|
|
event2_newevent_xx(-1, c->tod_offset * CYCLE_UNIT, num, event_CIA_tod_inc_event);
|
|
}
|
|
}
|
|
|
|
void CIAA_tod_handler(int hoffset)
|
|
{
|
|
struct CIA *c = &cia[0];
|
|
#ifdef TOD_HACK
|
|
if (currprefs.tod_hack && tod_hack_enabled == 1) {
|
|
c->tod_event_state = 0;
|
|
return;
|
|
}
|
|
#endif
|
|
CIA_tod_handler(hoffset, 0, true);
|
|
}
|
|
void CIAB_tod_handler(int hoffset)
|
|
{
|
|
CIA_tod_handler(hoffset, 1, false);
|
|
}
|
|
|
|
void keyboard_connected(bool connect)
|
|
{
|
|
if (connect) {
|
|
write_log(_T("Keyboard connected\n"));
|
|
} else {
|
|
write_log(_T("Keyboard disconnected\n"));
|
|
}
|
|
kbstate = 0;
|
|
kblostsynccnt = 0;
|
|
resetwarning_phase = 0;
|
|
}
|
|
|
|
static void check_keyboard(void)
|
|
{
|
|
if (currprefs.keyboard_connected) {
|
|
if ((keys_available() || kbstate < 3) && !kblostsynccnt ) {
|
|
switch (kbstate)
|
|
{
|
|
case 0:
|
|
kbcode = 0; /* powerup resync */
|
|
kbstate++;
|
|
break;
|
|
case 1:
|
|
setcode(AK_INIT_POWERUP);
|
|
kbstate++;
|
|
break;
|
|
case 2:
|
|
setcode(AK_TERM_POWERUP);
|
|
kbstate++;
|
|
break;
|
|
case 3:
|
|
kbcode = ~get_next_key();
|
|
break;
|
|
}
|
|
keyreq();
|
|
}
|
|
} else {
|
|
while (keys_available()) {
|
|
get_next_key();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cia_delayed_tod(int num)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
if (c->tod_event_state == 4) {
|
|
c->tod_event_state = 1;
|
|
return;
|
|
}
|
|
if (c->tod_event_state == 1) {
|
|
CIA_tod_inc(false, num);
|
|
}
|
|
c->tod_event_state = 0;
|
|
c->tod_offset = -1;
|
|
}
|
|
|
|
void CIA_hsync_posthandler(bool ciahsync, bool dotod)
|
|
{
|
|
if (ciahsync) {
|
|
// CIA-B HSync pulse
|
|
// Delayed previous line TOD increase.
|
|
cia_delayed_tod(1);
|
|
if (currprefs.tod_hack && cia[0].todon) {
|
|
do_tod_hack(dotod);
|
|
}
|
|
} else if (currprefs.keyboard_connected) {
|
|
// custom hsync
|
|
if (resetwarning_phase) {
|
|
resetwarning_check();
|
|
while (keys_available()) {
|
|
get_next_key();
|
|
}
|
|
} else {
|
|
if ((hsync_counter & 15) == 0) {
|
|
check_keyboard();
|
|
}
|
|
}
|
|
} else {
|
|
while (keys_available()) {
|
|
get_next_key();
|
|
}
|
|
}
|
|
|
|
if (!ciahsync) {
|
|
// Increase CIA-A TOD if delayed from previous line
|
|
cia_delayed_tod(0);
|
|
}
|
|
|
|
}
|
|
|
|
static void calc_led(int old_led)
|
|
{
|
|
evt_t c = get_cycles();
|
|
int t = (int)((c - led_cycle) / CYCLE_UNIT);
|
|
if (old_led > 0)
|
|
led_cycles_on += t;
|
|
else
|
|
led_cycles_off += t;
|
|
led_cycle = c;
|
|
}
|
|
|
|
static void led_vsync(void)
|
|
{
|
|
int v;
|
|
bool ledonoff;
|
|
|
|
calc_led(led);
|
|
if (led_cycles_on && !led_cycles_off)
|
|
v = 255;
|
|
else if (led_cycles_off && !led_cycles_on)
|
|
v = 0;
|
|
else if (led_cycles_off)
|
|
v = (int)(led_cycles_on * 255 / (led_cycles_on + led_cycles_off));
|
|
else
|
|
v = 255;
|
|
if (v < 0)
|
|
v = 0;
|
|
ledonoff = v > 96;
|
|
if (currprefs.power_led_dim && v < currprefs.power_led_dim)
|
|
v = currprefs.power_led_dim;
|
|
if (v > 255)
|
|
v = 255;
|
|
gui_data.powerled_brightness = v;
|
|
led_cycles_on = 0;
|
|
led_cycles_off = 0;
|
|
if (led_old_brightness != gui_data.powerled_brightness || ledonoff != gui_data.powerled) {
|
|
gui_data.powerled = ledonoff;
|
|
gui_led (LED_POWER, gui_data.powerled, gui_data.powerled_brightness);
|
|
led_filter_audio();
|
|
}
|
|
led_old_brightness = gui_data.powerled_brightness;
|
|
led_cycle = get_cycles();
|
|
}
|
|
|
|
static void write_battclock(void);
|
|
void CIA_vsync_prehandler(void)
|
|
{
|
|
if (heartbeat_cnt > 0)
|
|
heartbeat_cnt--;
|
|
|
|
if (rtc_msm.delayed_write < 0) {
|
|
rtc_msm.delayed_write = 50;
|
|
} else if (rtc_msm.delayed_write > 0) {
|
|
rtc_msm.delayed_write--;
|
|
if (rtc_msm.delayed_write == 0)
|
|
write_battclock();
|
|
}
|
|
if (rtc_ricoh.delayed_write < 0) {
|
|
rtc_ricoh.delayed_write = 50;
|
|
} else if (rtc_ricoh.delayed_write > 0) {
|
|
rtc_ricoh.delayed_write--;
|
|
if (rtc_ricoh.delayed_write == 0)
|
|
write_battclock();
|
|
}
|
|
|
|
led_vsync();
|
|
keybuf_vsync();
|
|
if (kblostsynccnt > 0) {
|
|
kblostsynccnt -= maxvpos;
|
|
if (kblostsynccnt <= 0) {
|
|
kblostsynccnt = 0;
|
|
kbcode = 0;
|
|
keyreq();
|
|
#if KB_DEBUG
|
|
write_log(_T("lostsync\n"));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
cia_cycle_accurate = currprefs.m68k_speed >= 0 && currprefs.cpu_compatible;
|
|
}
|
|
|
|
static void check_led(void)
|
|
{
|
|
uae_u8 v = cia[0].pra;
|
|
int led2;
|
|
|
|
v |= ~cia[0].dra; /* output is high when pin's direction is input */
|
|
led2 = (v & 2) ? 0 : 1;
|
|
if (led2 != led) {
|
|
calc_led(led);
|
|
led = led2;
|
|
led_old_brightness = -1;
|
|
}
|
|
}
|
|
|
|
static void bfe001_change(void)
|
|
{
|
|
uae_u8 v = cia[0].pra;
|
|
v |= ~cia[0].dra; /* output is high when pin's direction is input */
|
|
check_led();
|
|
if (currprefs.cs_ciaoverlay && (v & 1) != oldovl) {
|
|
oldovl = v & 1;
|
|
if (!oldovl) {
|
|
map_overlay(1);
|
|
} else {
|
|
map_overlay(0);
|
|
}
|
|
}
|
|
akiko_mute((v & 1) == 0);
|
|
}
|
|
|
|
static uae_u32 getciatod(uae_u32 tod)
|
|
{
|
|
if (!currprefs.cs_cia6526)
|
|
return tod;
|
|
uae_u32 bcdtod = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
int val = tod % 10;
|
|
bcdtod *= 16;
|
|
bcdtod += val;
|
|
tod /= 10;
|
|
}
|
|
return bcdtod;
|
|
}
|
|
static void setciatod(uae_u32 *tod, uae_u32 v)
|
|
{
|
|
if (!currprefs.cs_cia6526) {
|
|
*tod = v;
|
|
return;
|
|
}
|
|
uae_u32 bintod = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
int val = v / 16;
|
|
bintod *= 10;
|
|
bintod += val;
|
|
v /= 16;
|
|
}
|
|
*tod = bintod;
|
|
}
|
|
|
|
// E-clock count mode?
|
|
static bool CIA_timer_02(int num, uae_u8 cr)
|
|
{
|
|
if (num) {
|
|
return (cr & (CR_INMODE | CR_INMODE1)) == 0;
|
|
}
|
|
return (cr & CR_INMODE) == 0;
|
|
}
|
|
|
|
static uae_u8 ReadCIAReg(int num, int reg)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
uae_u8 tmp;
|
|
int tnum = 0;
|
|
|
|
switch (reg)
|
|
{
|
|
case 6:
|
|
case 7:
|
|
case 15:
|
|
tnum = 1;
|
|
break;
|
|
}
|
|
|
|
struct CIATimer *t = &c->t[tnum];
|
|
|
|
switch (reg)
|
|
{
|
|
case 4:
|
|
case 6:
|
|
case 5:
|
|
case 7:
|
|
{
|
|
uae_u16 tval = t->timer - t->passed;
|
|
if (reg == 4 || reg == 6) {
|
|
return tval & 0xff;
|
|
}
|
|
return tval >> 8;
|
|
}
|
|
case 8:
|
|
CIA_tod_check(num);
|
|
if (c->tlatch) {
|
|
c->tlatch = 0;
|
|
return getciatod(c->tol);
|
|
} else {
|
|
return getciatod(c->tod);
|
|
}
|
|
case 9:
|
|
CIA_tod_check(num);
|
|
if (c->tlatch) {
|
|
return getciatod(c->tol) >> 8;
|
|
} else {
|
|
return getciatod(c->tod) >> 8;
|
|
}
|
|
case 10:
|
|
CIA_tod_check(num);
|
|
/* only if not already latched. A1200 confirmed. (TW) */
|
|
if (!currprefs.cs_cia6526) {
|
|
if (!c->tlatch) {
|
|
/* no latching if ALARM is set */
|
|
if (!(c->t[1].cr & CR_ALARM)) {
|
|
c->tlatch = 1;
|
|
}
|
|
c->tol = c->tod;
|
|
}
|
|
return getciatod(c->tol) >> 16;
|
|
} else {
|
|
if (c->tlatch)
|
|
return getciatod(c->tol) >> 16;
|
|
else
|
|
return getciatod(c->tod) >> 16;
|
|
}
|
|
break;
|
|
case 11:
|
|
CIA_tod_check(num);
|
|
if (currprefs.cs_cia6526) {
|
|
if (!c->tlatch) {
|
|
if (!(c->t[1].cr & CR_ALARM)) {
|
|
c->tlatch = 1;
|
|
}
|
|
c->tol = c->tod;
|
|
}
|
|
if (c->tlatch) {
|
|
return getciatod(c->tol) >> 24;
|
|
} else{
|
|
return getciatod(c->tod) >> 24;
|
|
}
|
|
} else {
|
|
return 0xff;
|
|
}
|
|
break;
|
|
case 12:
|
|
#if KB_DEBUG
|
|
write_log(_T("CIAA serial port: %02x %08x\n"), c->sdr, M68K_GETPC);
|
|
#endif
|
|
return c->sdr;
|
|
case 13:
|
|
tmp = c->icr1 & ~(0x40 | 0x20);
|
|
c->icr1 = 0;
|
|
return tmp;
|
|
case 14:
|
|
case 15:
|
|
return t->cr;
|
|
}
|
|
return 0xff;
|
|
}
|
|
|
|
static bool CIA_timer_inmode(int num, uae_u8 cr)
|
|
{
|
|
if (num) {
|
|
return (cr & (CR_INMODE | CR_INMODE1)) != 0;
|
|
} else {
|
|
return (cr & CR_INMODE) != 0;
|
|
}
|
|
}
|
|
|
|
static void CIA_thi_write(int num, int tnum, uae_u8 val)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
struct CIATimer *t = &c->t[tnum];
|
|
|
|
t->latch = (t->latch & 0xff) | (val << 8);
|
|
|
|
// If ONESHOT: Load and start timer.
|
|
// If CONTINUOUS: Load timer if not running.
|
|
|
|
if (!acc_mode()) {
|
|
// if inaccurate mode: do everything immediately
|
|
|
|
if (!(t->cr & CR_START)) {
|
|
t->timer = t->latch;
|
|
}
|
|
|
|
if (t->cr & CR_RUNMODE) {
|
|
t->cr |= CR_START;
|
|
if (!CIA_timer_inmode(tnum, t->cr)) {
|
|
t->inputpipe = CIA_PIPE_ALL_MASK;
|
|
}
|
|
}
|
|
|
|
if (t->cr & CR_START) {
|
|
if (!CIA_timer_inmode(tnum, t->cr)) {
|
|
if (t->timer <= 1) {
|
|
t->preovfl = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// if accurate mode: handle delays cycle-accurately
|
|
|
|
if (!(t->cr & CR_START)) {
|
|
t->loaddelay |= 0x00000001 << 1;
|
|
t->loaddelay |= 0x00000001 << 2;
|
|
}
|
|
|
|
if (t->cr & CR_RUNMODE) {
|
|
t->cr |= CR_START;
|
|
t->loaddelay |= 0x00000001 << 2;
|
|
// timer=0 special case
|
|
t->loaddelay |= 0x01000000 << 1;
|
|
t->loaddelay |= 0x10000000 << 1;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void CIA_cr_write(int num, int tnum, uae_u8 val)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
struct CIATimer *t = &c->t[tnum];
|
|
|
|
if (!tnum) {
|
|
val &= 0x7f; /* bit 7 is unused */
|
|
}
|
|
|
|
if (!acc_mode()) {
|
|
// if inaccurate mode: do everything immediately
|
|
|
|
if (val & CR_LOAD) {
|
|
val &= ~CR_LOAD;
|
|
t->timer = t->latch;
|
|
}
|
|
if (val & CR_START) {
|
|
if (!CIA_timer_inmode(tnum, val)) {
|
|
t->inputpipe = CIA_PIPE_ALL_MASK;
|
|
if (t->timer <= 1) {
|
|
t->preovfl = true;
|
|
}
|
|
}
|
|
} else {
|
|
t->inputpipe = 0;
|
|
}
|
|
|
|
} else {
|
|
// if accurate mode: handle delays cycle-accurately
|
|
|
|
if (val & CR_LOAD) {
|
|
val &= ~CR_LOAD;
|
|
t->loaddelay |= 0x00000001 << 2;
|
|
t->loaddelay |= 0x00000100 << 0;
|
|
t->loaddelay |= 0x00000100 << 1;
|
|
if (!(t->cr & CR_START)) {
|
|
t->loaddelay |= 0x00000001 << 1;
|
|
}
|
|
// timer=0 special case
|
|
t->loaddelay |= 0x10000000 << 1;
|
|
}
|
|
|
|
if (!(val & CR_START)) {
|
|
t->inputpipe &= ~CIA_PIPE_CLR1;
|
|
}
|
|
}
|
|
|
|
t->cr = val;
|
|
}
|
|
|
|
static void WriteCIAReg(int num, int reg, uae_u8 val)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
int tnum = 0;
|
|
|
|
switch (reg)
|
|
{
|
|
case 6:
|
|
case 7:
|
|
case 15:
|
|
tnum = 1;
|
|
break;
|
|
}
|
|
|
|
struct CIATimer *t = &c->t[tnum];
|
|
|
|
switch (reg) {
|
|
case 4:
|
|
case 6:
|
|
t->latch = (t->latch & 0xff00) | val;
|
|
break;
|
|
case 5:
|
|
case 7:
|
|
CIA_thi_write(num, tnum, val);
|
|
break;
|
|
case 8:
|
|
CIA_tod_check(num);
|
|
if (c->t[1].cr & CR_ALARM) {
|
|
setciatod(&c->alarm, (getciatod(c->alarm) & ~0xff) | val);
|
|
} else {
|
|
setciatod(&c->tod, (getciatod(c->tod) & ~0xff) | val);
|
|
c->todon = 1;
|
|
cia_checkalarm(false, true, num);
|
|
CIA_tod_check(num);
|
|
}
|
|
break;
|
|
case 9:
|
|
CIA_tod_check(num);
|
|
if (c->t[1].cr & CR_ALARM) {
|
|
setciatod(&c->alarm, (getciatod(c->alarm) & ~0xff00) | (val << 8));
|
|
} else {
|
|
setciatod(&c->tod, (getciatod(c->tod) & ~0xff00) | (val << 8));
|
|
}
|
|
break;
|
|
case 10:
|
|
CIA_tod_check(num);
|
|
if (c->t[1].cr & CR_ALARM) {
|
|
setciatod(&c->alarm, (getciatod(c->alarm) & ~0xff0000) | (val << 16));
|
|
} else {
|
|
setciatod(&c->tod, (getciatod(c->tod) & ~0xff0000) | (val << 16));
|
|
if (!currprefs.cs_cia6526) {
|
|
c->todon = 0;
|
|
}
|
|
}
|
|
break;
|
|
case 11:
|
|
CIA_tod_check(num);
|
|
if (currprefs.cs_cia6526) {
|
|
if (c->t[1].cr & CR_ALARM) {
|
|
setciatod(&c->alarm, (getciatod(c->alarm) & ~0xff000000) | (val << 24));
|
|
} else {
|
|
setciatod(&c->tod, (getciatod(c->tod) & ~0xff000000) | (val << 24));
|
|
c->todon = 0;
|
|
}
|
|
}
|
|
break;
|
|
case 12:
|
|
c->sdr = val;
|
|
if ((c->t[0].cr & (CR_INMODE1 | CR_START)) == (CR_INMODE1 | CR_START)) {
|
|
c->sdr_load = 1;
|
|
if (c->sdr_cnt == 0) {
|
|
c->sdr_cnt = 8 * 2;
|
|
c->sdr_load = 0;
|
|
c->sdr_buf = c->sdr;
|
|
}
|
|
}
|
|
break;
|
|
case 13:
|
|
setclr(&c->imask, val);
|
|
RethinkICR(num);
|
|
break;
|
|
case 14:
|
|
case 15:
|
|
CIA_cr_write(num, tnum, val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uae_u8 CIA_PBON(struct CIA *c, uae_u8 v)
|
|
{
|
|
// A PBON
|
|
if (c->t[0].cr & CR_PBON) {
|
|
int pb6 = 0;
|
|
if (c->t[0].cr & CR_OUTMODE)
|
|
pb6 = c->t[0].cr & CR_START;
|
|
v &= ~0x40;
|
|
v |= pb6 ? 0x40 : 00;
|
|
}
|
|
// B PBON
|
|
if (c->t[1].cr & CR_PBON) {
|
|
int pb7 = 0;
|
|
if (c->t[1].cr & CR_OUTMODE)
|
|
pb7 = c->t[1].cr & CR_START;
|
|
v &= ~0x80;
|
|
v |= pb7 ? 0x80 : 00;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static uae_u8 ReadCIAA(uae_u32 addr, uae_u32 *flags)
|
|
{
|
|
struct CIA *c = &cia[0];
|
|
uae_u32 tmp;
|
|
int reg = addr & 15;
|
|
|
|
compute_passed_time();
|
|
|
|
#if CIAA_DEBUG_R > 0
|
|
if (CIAA_DEBUG_R > 1 || (munge24 (M68K_GETPC) & 0xFFF80000) != 0xF80000)
|
|
write_log(_T("R_CIAA: bfe%x01 %08X\n"), reg, M68K_GETPC);
|
|
#endif
|
|
|
|
switch (reg) {
|
|
case 0:
|
|
{
|
|
*flags |= 1;
|
|
uae_u8 v = DISK_status_ciaa() & 0x3c;
|
|
v |= handle_joystick_buttons(c->pra, c->dra);
|
|
v |= (c->pra | (c->dra ^ 3)) & 0x03;
|
|
v = dongle_cia_read(0, reg, c->dra, v);
|
|
v = alg_joystick_buttons(c->pra, c->dra, v);
|
|
|
|
// 391078-01 CIA: output mode bits always return PRA contents
|
|
if (currprefs.cs_ciatype[0]) {
|
|
v &= ~c->dra;
|
|
v |= c->pra & c->dra;
|
|
}
|
|
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFE001 R %02X %s\n"), v, debuginfo(0));
|
|
#endif
|
|
|
|
if (inputrecord_debug & 2) {
|
|
if (input_record > 0)
|
|
inprec_recorddebug_cia(v, 0, m68k_getpc ());
|
|
else if (input_play > 0)
|
|
inprec_playdebug_cia(v, 0, m68k_getpc ());
|
|
}
|
|
|
|
return v;
|
|
}
|
|
case 1:
|
|
tmp = (c->prb & c->drb) | (c->drb ^ 0xff);
|
|
#ifdef PARALLEL_PORT
|
|
if (isprinter() > 0) {
|
|
tmp = c->prb;
|
|
} else if (isprinter() < 0) {
|
|
uae_u8 v;
|
|
parallel_direct_read_data(&v);
|
|
tmp = v;
|
|
#ifdef ARCADIA
|
|
} else if (arcadia_bios) {
|
|
tmp = arcadia_parport(0, c->prb, c->drb);
|
|
#endif
|
|
} else if (currprefs.win32_samplersoundcard >= 0) {
|
|
|
|
tmp = sampler_getsample((c->pra & 4) ? 1 : 0);
|
|
#endif
|
|
|
|
} else if (parallel_port_scsi) {
|
|
|
|
tmp = parallel_port_scsi_read(0, c->prb, c->drb);
|
|
|
|
} else {
|
|
tmp = handle_parport_joystick (0, tmp);
|
|
tmp = dongle_cia_read (1, reg, c->drb, tmp);
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFE101 R %02X %s\n"), tmp, debuginfo(0));
|
|
#endif
|
|
}
|
|
tmp = CIA_PBON(c, tmp);
|
|
if (currprefs.cs_ciatype[0]) {
|
|
tmp &= ~c->drb;
|
|
tmp |= c->prb & c->drb;
|
|
}
|
|
|
|
return tmp;
|
|
case 2:
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFE201 R %02X %s\n"), c->dra, debuginfo(0));
|
|
#endif
|
|
return c->dra;
|
|
case 3:
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFE301 R %02X %s\n"), c->drb, debuginfo(0));
|
|
#endif
|
|
return c->drb;
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
return ReadCIAReg(0, reg);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uae_u8 ReadCIAB(uae_u32 addr, uae_u32 *flags)
|
|
{
|
|
struct CIA *c = &cia[1];
|
|
uae_u32 tmp;
|
|
int reg = addr & 15;
|
|
|
|
#if CIAB_DEBUG_R > 0
|
|
if (CIAB_DEBUG_R > 1 || (munge24 (M68K_GETPC) & 0xFFF80000) != 0xF80000) {
|
|
if ((addr >= 8 && addr <= 10) || CIAB_DEBUG_R > 1)
|
|
write_log(_T("R_CIAB: bfd%x00 %08X\n"), reg, M68K_GETPC);
|
|
}
|
|
#endif
|
|
|
|
compute_passed_time();
|
|
|
|
switch (reg) {
|
|
case 0:
|
|
tmp = (c->pra & c->dra) | (c->dra ^ 0xff);
|
|
#ifdef PARALLEL_PORT
|
|
if (isprinter() > 0) {
|
|
tmp &= ~3; // clear BUSY and PAPEROUT
|
|
tmp |= 4; // set SELECT
|
|
} else if (isprinter() < 0) {
|
|
uae_u8 v;
|
|
tmp &= ~7;
|
|
parallel_direct_read_status(&v);
|
|
tmp |= v & 7;
|
|
} else if (parallel_port_scsi) {
|
|
tmp = parallel_port_scsi_read(1, c->pra, c->dra);
|
|
} else {
|
|
// serial port in output mode
|
|
if (c->t[0].cr & 0x40) {
|
|
tmp &= ~3;
|
|
tmp |= (c->sdr_cnt & 1) ? 2 : 0; // clock
|
|
tmp |= (c->sdr_buf & 0x80) ? 1 : 0; // data
|
|
}
|
|
tmp = handle_parport_joystick(1, tmp);
|
|
}
|
|
#endif
|
|
#ifdef SERIAL_PORT
|
|
tmp = serial_readstatus(tmp, c->dra);
|
|
#endif
|
|
tmp = dongle_cia_read(1, reg, c->pra, tmp);
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFD000 R %02X %s\n"), tmp, debuginfo(0));
|
|
#endif
|
|
|
|
if (currprefs.cs_ciatype[1]) {
|
|
tmp &= ~c->dra;
|
|
tmp |= c->pra & c->dra;
|
|
}
|
|
|
|
return tmp;
|
|
case 1:
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFD100 R %02X %s\n"), c->prb, debuginfo(0));
|
|
#endif
|
|
tmp = c->prb;
|
|
tmp = DISK_status_ciab(tmp);
|
|
tmp = dongle_cia_read(1, reg, c->prb, tmp);
|
|
tmp = CIA_PBON(c, tmp);
|
|
if (currprefs.cs_ciatype[1]) {
|
|
tmp &= ~c->drb;
|
|
tmp |= c->prb & c->drb;
|
|
}
|
|
|
|
return tmp;
|
|
case 2:
|
|
return c->dra;
|
|
case 3:
|
|
return c->drb;
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
return ReadCIAReg(1, reg);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void WriteCIAA(uae_u16 addr, uae_u8 val, uae_u32 *flags)
|
|
{
|
|
struct CIA *c = &cia[0];
|
|
int reg = addr & 15;
|
|
|
|
#if CIAA_DEBUG_W > 0
|
|
write_log(_T("W_CIAA: bfe%x01 %02X %08X\n"), reg, val, M68K_GETPC);
|
|
#endif
|
|
#ifdef ACTION_REPLAY
|
|
ar_ciaa[reg] = val;
|
|
#endif
|
|
if (!currprefs.cs_ciaoverlay && oldovl) {
|
|
map_overlay(1);
|
|
oldovl = 0;
|
|
}
|
|
switch (reg) {
|
|
case 0:
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom ())
|
|
write_log(_T("BFE001 W %02X %s\n"), val, debuginfo(0));
|
|
#endif
|
|
c->pra = (c->pra & ~0xc3) | (val & 0xc3);
|
|
bfe001_change();
|
|
handle_cd32_joystick_cia(c->pra, c->dra);
|
|
dongle_cia_write(0, reg, c->dra, val);
|
|
#ifdef AMAX
|
|
if (is_device_rom(&currprefs, ROMTYPE_AMAX, 0) > 0)
|
|
amax_bfe001_write(val, c->dra);
|
|
#endif
|
|
break;
|
|
case 1:
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom ())
|
|
write_log(_T("BFE101 W %02X %s\n"), val, debuginfo(0));
|
|
#endif
|
|
c->prb = val;
|
|
dongle_cia_write(0, reg, c->drb, val);
|
|
#ifdef PARALLEL_PORT
|
|
if (isprinter()) {
|
|
if (isprinter() > 0) {
|
|
doprinter(val);
|
|
cia_parallelack();
|
|
} else if (isprinter() < 0) {
|
|
parallel_direct_write_data(val, c->drb);
|
|
cia_parallelack();
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef ARCADIA
|
|
if (!isprinter() && arcadia_bios) {
|
|
arcadia_parport(1, c->prb, c->drb);
|
|
}
|
|
#endif
|
|
#ifdef PARALLEL_PORT
|
|
if (!isprinter() && parallel_port_scsi) {
|
|
parallel_port_scsi_write(0, c->prb, c->drb);
|
|
}
|
|
#endif
|
|
break;
|
|
case 2:
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom ())
|
|
write_log(_T("BFE201 W %02X %s\n"), val, debuginfo(0));
|
|
#endif
|
|
c->dra = val;
|
|
dongle_cia_write(0, reg, c->pra, val);
|
|
bfe001_change();
|
|
break;
|
|
case 3:
|
|
c->drb = val;
|
|
dongle_cia_write(0, reg, c->prb, val);
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom ())
|
|
write_log(_T("BFE301 W %02X %s\n"), val, debuginfo(0));
|
|
#endif
|
|
#ifdef ARCADIA
|
|
if (arcadia_bios)
|
|
arcadia_parport(1, c->prb, c->drb);
|
|
#endif
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 15:
|
|
CIA_update();
|
|
WriteCIAReg(0, reg, val);
|
|
CIA_calctimers();
|
|
break;
|
|
case 14:
|
|
CIA_update();
|
|
// keyboard handshake handling
|
|
if (currprefs.cpuboard_type != 0 && (val & 0x40) != (c->t[0].cr & 0x40)) {
|
|
/* bleh, Phase5 CPU timed early boot key check fix.. */
|
|
if (m68k_getpc() >= 0xf00000 && m68k_getpc() < 0xf80000)
|
|
check_keyboard();
|
|
}
|
|
if ((val & CR_INMODE1) != 0 && (c->t[0].cr & CR_INMODE1) == 0) {
|
|
// handshake start
|
|
if (kblostsynccnt > 0 && currprefs.cs_kbhandshake) {
|
|
kbhandshakestart = get_cycles();
|
|
}
|
|
#if KB_DEBUG
|
|
write_log(_T("KB_ACK_START %02x->%02x %08x\n"), c->t[0].cr, val, M68K_GETPC);
|
|
#endif
|
|
} else if ((val & CR_INMODE1) == 0 && (c->t[0].cr & CR_INMODE1) != 0) {
|
|
// handshake end
|
|
/* todo: check if low to high or high to low only */
|
|
if (kblostsynccnt > 0 && currprefs.cs_kbhandshake) {
|
|
evt_t len = get_cycles() - kbhandshakestart;
|
|
if (len < currprefs.cs_kbhandshake * CYCLE_UNIT) {
|
|
write_log(_T("Keyboard handshake pulse length %d < %d (CCKs)\n"), len / CYCLE_UNIT, currprefs.cs_kbhandshake);
|
|
}
|
|
}
|
|
kblostsynccnt = 0;
|
|
#if KB_DEBUG
|
|
write_log(_T("KB_ACK_END %02x->%02x %08x\n"), c->t[0].cr, val, M68K_GETPC);
|
|
#endif
|
|
}
|
|
WriteCIAReg(0, reg, val);
|
|
CIA_calctimers();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void write_ciab_serial(uae_u8 ndata, uae_u8 odata, uae_u8 ndir, uae_u8 odir)
|
|
{
|
|
struct CIA *c = &cia[1];
|
|
uae_u8 val = ndata & ndir;
|
|
uae_u8 icr = 0;
|
|
|
|
// CNT 0->1?
|
|
if ((val & 2) && !((odata & odir) & 2)) {
|
|
// CIA-B SP in input mode
|
|
if (!(c->t[0].cr & CR_SPMODE)) {
|
|
c->sdr_buf <<= 1;
|
|
c->sdr_buf |= (val & 1) ? 0x01 : 0x00;
|
|
c->sdr_cnt++;
|
|
if (c->sdr_cnt >= 8) {
|
|
// Data received
|
|
c->sdr = c->sdr_buf;
|
|
icr |= ICR_SP;
|
|
c->sdr_cnt = 0;
|
|
}
|
|
}
|
|
icr |= cia_inmode_cnt(1);
|
|
}
|
|
if (icr) {
|
|
CIA_sync_interrupt(1, icr);
|
|
}
|
|
}
|
|
|
|
static void WriteCIAB(uae_u16 addr, uae_u8 val, uae_u32 *flags)
|
|
{
|
|
struct CIA *c = &cia[1];
|
|
int reg = addr & 15;
|
|
|
|
#if CIAB_DEBUG_W > 0
|
|
if (((addr >= 8 && addr <= 10) || addr == 15) || CIAB_DEBUG_W > 1)
|
|
write_log(_T("W_CIAB: bfd%x00 %02X %08X\n"), reg, val, M68K_GETPC);
|
|
#endif
|
|
#ifdef ACTION_REPLAY
|
|
ar_ciab[reg] = val;
|
|
#endif
|
|
switch (reg) {
|
|
case 0:
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFD000 W %02X %s\n"), val, debuginfo(0));
|
|
#endif
|
|
dongle_cia_write(1, reg, c->dra, val);
|
|
write_ciab_serial(val, c->pra, c->dra, c->dra);
|
|
c->pra = val;
|
|
#ifdef SERIAL_PORT
|
|
serial_writestatus(c->pra, c->dra);
|
|
#endif
|
|
#ifdef PARALLEL_PORT
|
|
if (isprinter() < 0) {
|
|
parallel_direct_write_status(val, c->dra);
|
|
}
|
|
else if (parallel_port_scsi) {
|
|
parallel_port_scsi_write(1, c->pra, c->dra);
|
|
}
|
|
#endif
|
|
break;
|
|
case 1:
|
|
*flags |= 2;
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFD100 W %02X %s\n"), val, debuginfo(0));
|
|
#endif
|
|
dongle_cia_write(1, c->drb, reg, val);
|
|
c->prb = val;
|
|
val = CIA_PBON(c, val);
|
|
DISK_select(val);
|
|
break;
|
|
case 2:
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFD200 W %02X %s\n"), val, debuginfo(0));
|
|
#endif
|
|
dongle_cia_write(1, reg, c->pra, val);
|
|
write_ciab_serial(c->pra, c->pra, val, c->dra);
|
|
c->dra = val;
|
|
#ifdef SERIAL_PORT
|
|
if (currprefs.use_serial)
|
|
serial_writestatus(c->pra, c->dra);
|
|
#endif
|
|
break;
|
|
case 3:
|
|
#if DONGLE_DEBUG > 0
|
|
if (notinrom())
|
|
write_log(_T("BFD300 W %02X %s\n"), val, debuginfo(0));
|
|
#endif
|
|
dongle_cia_write(1, reg, c->prb, val);
|
|
c->drb = val;
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
CIA_update();
|
|
WriteCIAReg(1, reg, val);
|
|
CIA_calctimers();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void cia_set_overlay(bool overlay)
|
|
{
|
|
oldovl = overlay;
|
|
}
|
|
|
|
void CIA_reset(void)
|
|
{
|
|
#ifdef TOD_HACK
|
|
tod_hack_tv = 0;
|
|
tod_hack_tod = 0;
|
|
tod_hack_enabled = 0;
|
|
if (currprefs.tod_hack)
|
|
tod_hack_enabled = TOD_HACK_TIME;
|
|
#endif
|
|
|
|
kblostsynccnt = 0;
|
|
serbits = 0;
|
|
resetwarning_phase = resetwarning_timer = 0;
|
|
heartbeat_cnt = 0;
|
|
cia[0].tod_event_state = 0;
|
|
cia[1].tod_event_state = 0;
|
|
led_cycles_off = 0;
|
|
led_cycles_on = 0;
|
|
led_cycle = get_cycles();
|
|
led = 0;
|
|
|
|
if (!savestate_state) {
|
|
oldovl = true;
|
|
kbstate = 0;
|
|
memset(&cia, 0, sizeof(cia));
|
|
cia[0].t[0].timer = 0xffff;
|
|
cia[0].t[1].timer = 0xffff;
|
|
cia[1].t[0].timer = 0xffff;
|
|
cia[1].t[1].timer = 0xffff;
|
|
cia[0].t[0].latch = 0xffff;
|
|
cia[0].t[1].latch = 0xffff;
|
|
cia[1].t[0].latch = 0xffff;
|
|
cia[1].t[1].latch = 0xffff;
|
|
cia[1].pra = 0x8c;
|
|
internaleclockphase = 0;
|
|
CIA_calctimers();
|
|
DISK_select_set(cia[1].prb);
|
|
}
|
|
set_eclockphase();
|
|
map_overlay(0);
|
|
check_led();
|
|
#ifdef SERIAL_PORT
|
|
if (currprefs.use_serial && !savestate_state)
|
|
serial_dtr_off(); /* Drop DTR at reset */
|
|
#endif
|
|
if (savestate_state) {
|
|
if (currprefs.cs_ciaoverlay) {
|
|
oldovl = true;
|
|
}
|
|
led = -1;
|
|
bfe001_change();
|
|
if (!currprefs.cs_ciaoverlay) {
|
|
map_overlay(oldovl ? 0 : 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void dumpcia(void)
|
|
{
|
|
struct CIA *a = &cia[0];
|
|
struct CIA *b = &cia[1];
|
|
|
|
compute_passed_time();
|
|
|
|
console_out_f(_T("A: CRA %02x CRB %02x ICR %02x IM %02x TA %04x (%04x) TB %04x (%04x)\n"),
|
|
a->t[0].cr, a->t[1].cr, a->icr1, a->imask, a->t[0].timer - a->t[0].passed, a->t[0].latch, a->t[1].timer - a->t[1].passed, a->t[1].latch);
|
|
console_out_f(_T("TOD %06x (%06x) ALARM %06x %c%c CYC=%016llX\n"),
|
|
a->tod, a->tol, a->alarm, a->tlatch ? 'L' : '-', a->todon ? '-' : 'S', get_cycles());
|
|
console_out_f(_T("B: CRA %02x CRB %02x ICR %02x IM %02x TA %04x (%04x) TB %04x (%04x)\n"),
|
|
b->t[0].cr, b->t[1].cr, b->icr1, b->imask, b->t[0].timer - b->t[0].passed, b->t[0].latch, b->t[1].timer - b->t[1].passed, b->t[1].latch);
|
|
console_out_f(_T("TOD %06x (%06x) ALARM %06x %c%c\n"),
|
|
b->tod, b->tol, b->alarm, b->tlatch ? 'L' : '-', b->todon ? '-' : 'S');
|
|
}
|
|
|
|
/* CIA memory access */
|
|
|
|
DECLARE_MEMORY_FUNCTIONS(cia);
|
|
addrbank cia_bank = {
|
|
cia_lget, cia_wget, cia_bget,
|
|
cia_lput, cia_wput, cia_bput,
|
|
default_xlate, default_check, NULL, NULL, _T("CIA"),
|
|
cia_lgeti, cia_wgeti,
|
|
ABFLAG_IO | ABFLAG_CIA, S_READ, S_WRITE, NULL, 0x3f01, 0xbfc000
|
|
};
|
|
|
|
static int cia_cycles(int delay, int phase, int val, int post)
|
|
{
|
|
#ifdef DEBUGGER
|
|
if (currprefs.cpu_memory_cycle_exact && debug_dma) {
|
|
while (delay > 0) {
|
|
int hpos = current_hpos();
|
|
record_cia_access(0xfffff, 0, 0, 0, hpos, vpos, phase + 1);
|
|
phase += 2;
|
|
if (post) {
|
|
x_do_cycles_post(CYCLE_UNIT, val);
|
|
} else {
|
|
x_do_cycles_pre(CYCLE_UNIT);
|
|
}
|
|
delay -= CYCLE_UNIT;
|
|
}
|
|
} else {
|
|
#endif
|
|
if (delay > 0) {
|
|
if (post) {
|
|
x_do_cycles_post(delay, val);
|
|
} else {
|
|
x_do_cycles_pre(delay);
|
|
}
|
|
}
|
|
#ifdef DEBUGGER
|
|
}
|
|
#endif
|
|
return phase;
|
|
}
|
|
|
|
static void cia_wait_pre(int cianummask)
|
|
{
|
|
if (currprefs.cachesize || currprefs.cpu_thread)
|
|
return;
|
|
#ifdef WITH_PPC
|
|
if (ppc_state)
|
|
return;
|
|
#endif
|
|
|
|
#if CIA_IRQ_PROCESS_DELAY
|
|
if (currprefs.cpu_memory_cycle_exact) {
|
|
cia_interrupt_disabled |= cianummask;
|
|
}
|
|
#endif
|
|
|
|
#ifndef CUSTOM_SIMPLE
|
|
cia_now_evt = get_cycles();
|
|
int syncdelay = 0;
|
|
int delay = get_cia_sync_cycles(&syncdelay);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
cia_cycles(syncdelay, 100, 0, 0);
|
|
cia_cycles(delay, 0, 0, 0);
|
|
} else {
|
|
cia_cycles(syncdelay + delay, 0, 0, 0);
|
|
}
|
|
#else
|
|
cia_cycles(syncdelay + delay, 0, 0, 0);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static void cia_wait_post(int cianummask, uaecptr addr, uae_u32 value, bool rw)
|
|
{
|
|
#ifdef DEBUGGER
|
|
if (currprefs.cpu_memory_cycle_exact && debug_dma) {
|
|
int r = (addr & 0xf00) >> 8;
|
|
int hpos = current_hpos();
|
|
record_cia_access(r, cianummask, value, rw, hpos, vpos, -1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_PPC
|
|
if (ppc_state)
|
|
return;
|
|
#endif
|
|
if (currprefs.cpu_thread)
|
|
return;
|
|
if (currprefs.cachesize) {
|
|
do_cycles(12 * E_CYCLE_UNIT);
|
|
} else {
|
|
// Last 6 cycles of E-clock
|
|
// IPL fetch that got delayed by CIA access?
|
|
if (cia_now_evt == regs.ipl_evt && currprefs.cpu_model <= 68010) {
|
|
int phase = cia_cycles((e_clock_end - 2) * E_CYCLE_UNIT, 4, value, 1);
|
|
regs.ipl[0] = regs.ipl_pin;
|
|
cia_cycles(2 * E_CYCLE_UNIT, phase, value, 1);
|
|
} else {
|
|
cia_cycles(e_clock_end * E_CYCLE_UNIT, 4, value, 1);
|
|
}
|
|
#if CIA_IRQ_PROCESS_DELAY
|
|
if (currprefs.cpu_memory_cycle_exact) {
|
|
cia_interrupt_disabled &= ~cianummask;
|
|
if ((cia_interrupt_delay & cianummask) & 1) {
|
|
cia_interrupt_delay &= ~1;
|
|
ICR(0x0008);
|
|
}
|
|
if ((cia_interrupt_delay & cianummask) & 2) {
|
|
cia_interrupt_delay &= ~2;
|
|
ICR(0x2000);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#if CIA_IRQ_PROCESS_DELAY
|
|
if (!currprefs.cpu_memory_cycle_exact && cia_interrupt_delay) {
|
|
int v = cia_interrupt_delay;
|
|
cia_interrupt_delay = 0;
|
|
if (v & 1)
|
|
ICR(0x0008);
|
|
if (v & 2)
|
|
ICR(0x2000);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void validate_cia(uaecptr addr, int write, uae_u8 val)
|
|
{
|
|
bool err = false;
|
|
if (((addr >> 12) & 3) == 0 || ((addr >> 12) & 3) == 3)
|
|
err = true;
|
|
if (((addr & 0xf00) >> 8) == 11)
|
|
err = true;
|
|
int mask = addr & 0xf000;
|
|
if (mask != 0xe000 && mask != 0xd000)
|
|
err = true;
|
|
if (mask == 0xe000 && (addr & 1) == 0)
|
|
err = true;
|
|
if (mask == 0xd000 && (addr & 1) != 0)
|
|
err = true;
|
|
if (err) {
|
|
if (write) {
|
|
write_log(_T("Invalid CIA write %08x = %02x PC=%08x\n"), addr, val, M68K_GETPC);
|
|
} else {
|
|
write_log(_T("Invalid CIA read %08x PC=%08x\n"), addr, M68K_GETPC);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Gayle or Fat Gary does not enable CIA /CS lines if both CIAs are selected
|
|
// Old Gary based Amigas enable both CIAs in this situation
|
|
|
|
STATIC_INLINE bool issinglecia(void)
|
|
{
|
|
return currprefs.cs_ide || currprefs.cs_pcmcia || currprefs.cs_mbdmac || currprefs.cs_cd32cd;
|
|
}
|
|
STATIC_INLINE bool isgayle(void)
|
|
{
|
|
return currprefs.cs_ide || currprefs.cs_pcmcia;
|
|
}
|
|
|
|
// Gayle CIA select
|
|
STATIC_INLINE bool iscia(uaecptr addr)
|
|
{
|
|
uaecptr mask = addr & 0xf000;
|
|
return mask == 0xe000 || mask == 0xd000;
|
|
}
|
|
|
|
static bool isgaylenocia(uaecptr addr)
|
|
{
|
|
if (!isgayle ())
|
|
return true;
|
|
// gayle CIA region is only 4096 bytes at 0xbfd000 and 0xbfe000
|
|
return iscia(addr);
|
|
}
|
|
|
|
static bool isgarynocia(uaecptr addr)
|
|
{
|
|
return !iscia(addr) && currprefs.cs_fatgaryrev >= 0;
|
|
}
|
|
|
|
static int cia_chipselect(uaecptr addr)
|
|
{
|
|
int cs = (addr >> 12) & 3;
|
|
if (currprefs.cs_cd32cd) {
|
|
// Unexpected Akiko CIA select:
|
|
// 0,1 = CIA-A
|
|
if (cs == 0)
|
|
cs = 1;
|
|
// 2,3 = CIA-B
|
|
if (cs == 3)
|
|
cs = 2;
|
|
}
|
|
return cs;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 cia_bget(uaecptr addr)
|
|
{
|
|
int r = (addr & 0xf00) >> 8;
|
|
uae_u8 v = 0;
|
|
uae_u32 flags = 0;
|
|
|
|
if (isgarynocia(addr))
|
|
return dummy_get(addr, 1, false, 0);
|
|
|
|
if (!isgaylenocia (addr))
|
|
return dummy_get(addr, 1, false, 0);
|
|
|
|
#ifdef DEBUGGER
|
|
if (memwatch_access_validator) {
|
|
validate_cia(addr, 0, 0);
|
|
}
|
|
#endif
|
|
|
|
switch (cia_chipselect(addr))
|
|
{
|
|
case 0:
|
|
if (!issinglecia()) {
|
|
cia_wait_pre(1 | 2);
|
|
v = (addr & 1) ? ReadCIAA(r, &flags) : ReadCIAB(r, &flags);
|
|
cia_wait_post(1 | 2, addr, v, false);
|
|
}
|
|
break;
|
|
case 1:
|
|
cia_wait_pre(2);
|
|
if (currprefs.cpu_model == 68000 && currprefs.cpu_compatible) {
|
|
v = (addr & 1) ? regs.irc : ReadCIAB(r, &flags);
|
|
} else {
|
|
v = (addr & 1) ? dummy_get_safe(addr, 1, false, 0) : ReadCIAB(r, &flags);
|
|
}
|
|
cia_wait_post(2, addr, v, false);
|
|
break;
|
|
case 2:
|
|
cia_wait_pre(1);
|
|
if (currprefs.cpu_model == 68000 && currprefs.cpu_compatible)
|
|
v = (addr & 1) ? ReadCIAA(r, &flags) : regs.irc >> 8;
|
|
else
|
|
v = (addr & 1) ? ReadCIAA(r, &flags) : dummy_get_safe(addr, 1, false, 0);
|
|
cia_wait_post(1, addr, v, false);
|
|
break;
|
|
case 3:
|
|
if (currprefs.cpu_model == 68000 && currprefs.cpu_compatible) {
|
|
cia_wait_pre(0);
|
|
v = (addr & 1) ? regs.irc : regs.irc >> 8;
|
|
cia_wait_post(0, addr, v, false);
|
|
}
|
|
if (warned > 0 || currprefs.illegal_mem) {
|
|
write_log(_T("cia_bget: unknown CIA address %08X=%02X PC=%08X\n"), addr, v & 0xff, M68K_GETPC);
|
|
warned--;
|
|
}
|
|
break;
|
|
}
|
|
#ifdef ACTION_REPLAY
|
|
if (flags) {
|
|
action_replay_cia_access((flags & 2) != 0);
|
|
}
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 cia_wget(uaecptr addr)
|
|
{
|
|
int r = (addr & 0xf00) >> 8;
|
|
uae_u16 v = 0;
|
|
uae_u32 flags = 0;
|
|
|
|
if (isgarynocia(addr))
|
|
return dummy_get(addr, 2, false, 0);
|
|
|
|
if (!isgaylenocia (addr))
|
|
return dummy_get_safe(addr, 2, false, 0);
|
|
|
|
#ifdef DEBUGGER
|
|
if (memwatch_access_validator) {
|
|
write_log(_T("CIA word read %08x PC=%08x\n"), addr, M68K_GETPC);
|
|
}
|
|
#endif
|
|
|
|
switch (cia_chipselect(addr))
|
|
{
|
|
case 0:
|
|
if (!issinglecia())
|
|
{
|
|
cia_wait_pre(1 | 2);
|
|
v = ReadCIAB(r, &flags) << 8;
|
|
v |= ReadCIAA(r, &flags);
|
|
cia_wait_post(1 | 2, addr, v, false);
|
|
}
|
|
break;
|
|
case 1:
|
|
cia_wait_pre(2);
|
|
v = ReadCIAB(r, &flags) << 8;
|
|
v |= dummy_get_safe(addr + 1, 1, false, 0) & 0xff;
|
|
cia_wait_post(2, addr, v, false);
|
|
break;
|
|
case 2:
|
|
cia_wait_pre(1);
|
|
v = ReadCIAA(r, &flags);
|
|
v |= dummy_get_safe(addr, 1, false, 0) << 8;
|
|
cia_wait_post(1, addr, v, false);
|
|
break;
|
|
case 3:
|
|
if (currprefs.cpu_model == 68000 && currprefs.cpu_compatible) {
|
|
cia_wait_pre(0);
|
|
v = regs.irc;
|
|
cia_wait_post(0, addr, v, false);
|
|
}
|
|
if (warned > 0 || currprefs.illegal_mem) {
|
|
write_log(_T("cia_wget: unknown CIA address %08X=%04X PC=%08X\n"), addr, v & 0xffff, M68K_GETPC);
|
|
warned--;
|
|
}
|
|
break;
|
|
}
|
|
if (addr & 1)
|
|
v = (v << 8) | (v >> 8);
|
|
#ifdef ACTION_REPLAY
|
|
if (flags) {
|
|
action_replay_cia_access((flags & 2) != 0);
|
|
}
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 cia_lget(uaecptr addr)
|
|
{
|
|
uae_u32 v;
|
|
v = cia_wget(addr) << 16;
|
|
v |= cia_wget(addr + 2);
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 cia_wgeti(uaecptr addr)
|
|
{
|
|
if (currprefs.cpu_model >= 68020)
|
|
return dummy_wgeti(addr);
|
|
return cia_wget(addr);
|
|
}
|
|
static uae_u32 REGPARAM2 cia_lgeti(uaecptr addr)
|
|
{
|
|
if (currprefs.cpu_model >= 68020)
|
|
return dummy_lgeti(addr);
|
|
return cia_lget(addr);
|
|
}
|
|
|
|
|
|
static bool cia_debug(uaecptr addr, uae_u32 value, int size)
|
|
{
|
|
#ifdef DEBUGGER
|
|
if (addr == DEBUG_SPRINTF_ADDRESS || addr == DEBUG_SPRINTF_ADDRESS + 4 || addr == DEBUG_SPRINTF_ADDRESS + 8 ||
|
|
(addr == DEBUG_SPRINTF_ADDRESS + 2 && currprefs.cpu_model < 68020) ||
|
|
(addr == DEBUG_SPRINTF_ADDRESS + 6 && currprefs.cpu_model < 68020) ||
|
|
(addr == DEBUG_SPRINTF_ADDRESS + 10 && currprefs.cpu_model < 68020)) {
|
|
return debug_sprintf(addr, value, size);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
static void REGPARAM2 cia_bput(uaecptr addr, uae_u32 value)
|
|
{
|
|
int r = (addr & 0xf00) >> 8;
|
|
|
|
if (cia_debug(addr, value, sz_byte))
|
|
return;
|
|
|
|
if (isgarynocia(addr)) {
|
|
dummy_put(addr, 1, false);
|
|
return;
|
|
}
|
|
|
|
if (!isgaylenocia(addr))
|
|
return;
|
|
|
|
#ifdef DEBUGGER
|
|
if (memwatch_access_validator) {
|
|
validate_cia(addr, 1, value);
|
|
}
|
|
#endif
|
|
|
|
int cs = cia_chipselect(addr);
|
|
|
|
if (!issinglecia() || (cs & 3) != 0) {
|
|
uae_u32 flags = 0;
|
|
cia_wait_pre(((cs & 2) == 0 ? 1 : 0) | ((cs & 1) == 0 ? 2 : 0));
|
|
if ((cs & 2) == 0)
|
|
WriteCIAB(r, value, &flags);
|
|
if ((cs & 1) == 0)
|
|
WriteCIAA(r, value, &flags);
|
|
cia_wait_post(((cs & 2) == 0 ? 1 : 0) | ((cs & 1) == 0 ? 2 : 0), addr, value, true);
|
|
if (((cs & 3) == 3) && (warned > 0 || currprefs.illegal_mem)) {
|
|
write_log(_T("cia_bput: unknown CIA address %08X=%02X PC=%08X\n"), addr, value & 0xff, M68K_GETPC);
|
|
warned--;
|
|
}
|
|
#ifdef ACTION_REPLAY
|
|
if (flags) {
|
|
action_replay_cia_access((flags & 2) != 0);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void REGPARAM2 cia_wput(uaecptr addr, uae_u32 v)
|
|
{
|
|
int r = (addr & 0xf00) >> 8;
|
|
|
|
if (cia_debug(addr, v, sz_word))
|
|
return;
|
|
|
|
if (isgarynocia(addr)) {
|
|
dummy_put(addr, 2, false);
|
|
return;
|
|
}
|
|
|
|
if (!isgaylenocia(addr))
|
|
return;
|
|
|
|
#ifdef DEBUGGER
|
|
if (memwatch_access_validator) {
|
|
write_log(_T("CIA word write %08x = %04x PC=%08x\n"), addr, v & 0xffff, M68K_GETPC);
|
|
}
|
|
#endif
|
|
|
|
if (addr & 1)
|
|
v = (v << 8) | (v >> 8);
|
|
|
|
int cs = cia_chipselect(addr);
|
|
|
|
if (!issinglecia() || (cs & 3) != 0) {
|
|
uae_u32 flags = 0;
|
|
cia_wait_pre(((cs & 2) == 0 ? 1 : 0) | ((cs & 1) == 0 ? 2 : 0));
|
|
if ((cs & 2) == 0)
|
|
WriteCIAB(r, v >> 8, &flags);
|
|
if ((cs & 1) == 0)
|
|
WriteCIAA(r, v & 0xff, &flags);
|
|
cia_wait_post(((cs & 2) == 0 ? 1 : 0) | ((cs & 1) == 0 ? 2 : 0), addr, v, true);
|
|
if (((cs & 3) == 3) && (warned > 0 || currprefs.illegal_mem)) {
|
|
write_log(_T("cia_wput: unknown CIA address %08X=%04X %08X\n"), addr, v & 0xffff, M68K_GETPC);
|
|
warned--;
|
|
}
|
|
#ifdef ACTION_REPLAY
|
|
if (flags) {
|
|
action_replay_cia_access((flags & 2) != 0);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void REGPARAM2 cia_lput(uaecptr addr, uae_u32 value)
|
|
{
|
|
if (cia_debug(addr, value, sz_long))
|
|
return;
|
|
cia_wput(addr, value >> 16);
|
|
cia_wput(addr + 2, value & 0xffff);
|
|
}
|
|
|
|
/* battclock memory access */
|
|
|
|
static uae_u32 REGPARAM3 clock_lget(uaecptr) REGPARAM;
|
|
static uae_u32 REGPARAM3 clock_wget(uaecptr) REGPARAM;
|
|
static uae_u32 REGPARAM3 clock_bget(uaecptr) REGPARAM;
|
|
static void REGPARAM3 clock_lput(uaecptr, uae_u32) REGPARAM;
|
|
static void REGPARAM3 clock_wput(uaecptr, uae_u32) REGPARAM;
|
|
static void REGPARAM3 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, NULL, _T("Battery backed up clock (none)"),
|
|
dummy_lgeti, dummy_wgeti,
|
|
ABFLAG_IO, S_READ, S_WRITE, NULL, 0x3f, 0xdc0000
|
|
};
|
|
|
|
static uae_u8 getclockreg(int addr, struct tm *ct)
|
|
{
|
|
uae_u8 v = 0;
|
|
|
|
if (currprefs.cs_rtc == 1 || currprefs.cs_rtc == 3) { /* MSM6242B */
|
|
return get_clock_msm(&rtc_msm, addr, ct);
|
|
} else if (currprefs.cs_rtc == 2) { /* RF5C01A */
|
|
return get_clock_ricoh(&rtc_ricoh, addr, ct);
|
|
}
|
|
#if CLOCK_DEBUG
|
|
write_log(_T("CLOCK R: %X = %X, PC=%08x\n"), addr, v, M68K_GETPC);
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static void write_battclock(void)
|
|
{
|
|
if (!currprefs.rtcfile[0] || currprefs.cs_rtc == 0)
|
|
return;
|
|
TCHAR path[MAX_DPATH];
|
|
cfgfile_resolve_path_out_load(currprefs.rtcfile, path, MAX_DPATH, PATH_ROM);
|
|
struct zfile *f = zfile_fopen(path, _T("wb"));
|
|
if (f) {
|
|
struct tm *ct;
|
|
time_t t = time(0);
|
|
t += currprefs.cs_rtc_adjust;
|
|
ct = localtime(&t);
|
|
uae_u8 od;
|
|
if (currprefs.cs_rtc == 2) {
|
|
od = rtc_ricoh.clock_control_d;
|
|
rtc_ricoh.clock_control_d &= ~3;
|
|
} else {
|
|
od = rtc_msm.clock_control_d;
|
|
}
|
|
for (int i = 0; i < 13; i++) {
|
|
uae_u8 v = getclockreg(i, ct);
|
|
zfile_fwrite(&v, 1, 1, f);
|
|
}
|
|
if (currprefs.cs_rtc == 2) {
|
|
rtc_ricoh.clock_control_d = od;
|
|
zfile_fwrite(&rtc_ricoh.clock_control_d, 1, 1, f);
|
|
zfile_fwrite(&rtc_ricoh.clock_control_e, 1, 1, f);
|
|
zfile_fwrite(&rtc_ricoh.clock_control_f, 1, 1, f);
|
|
} else {
|
|
rtc_msm.clock_control_d = od;
|
|
zfile_fwrite(&rtc_msm.clock_control_d, 1, 1, f);
|
|
zfile_fwrite(&rtc_msm.clock_control_e, 1, 1, f);
|
|
zfile_fwrite(&rtc_msm.clock_control_f, 1, 1, f);
|
|
}
|
|
if (currprefs.cs_rtc == 2) {
|
|
zfile_fwrite(rtc_ricoh.rtc_alarm, RF5C01A_RAM_SIZE, 1, f);
|
|
zfile_fwrite(rtc_ricoh.rtc_memory, RF5C01A_RAM_SIZE, 1, f);
|
|
}
|
|
zfile_fclose(f);
|
|
}
|
|
}
|
|
|
|
void rtc_hardreset(void)
|
|
{
|
|
if (currprefs.cs_rtc == 1 || currprefs.cs_rtc == 3) { /* MSM6242B */
|
|
clock_bank.name = currprefs.cs_rtc == 1 ? _T("Battery backed up clock (MSM6242B)") : _T("Battery backed up clock A2000 (MSM6242B)");
|
|
rtc_msm.clock_control_d = 0x1;
|
|
rtc_msm.clock_control_e = 0;
|
|
rtc_msm.clock_control_f = 0x4; /* 24/12 */
|
|
rtc_msm.delayed_write = 0;
|
|
} else if (currprefs.cs_rtc == 2) { /* RF5C01A */
|
|
clock_bank.name = _T("Battery backed up clock (RF5C01A)");
|
|
rtc_ricoh.clock_control_d = 0x8; /* Timer EN */
|
|
rtc_ricoh.clock_control_e = 0;
|
|
rtc_ricoh.clock_control_f = 0;
|
|
memset(rtc_ricoh.rtc_memory, 0, RF5C01A_RAM_SIZE);
|
|
memset(rtc_ricoh.rtc_alarm, 0, RF5C01A_RAM_SIZE);
|
|
rtc_ricoh.rtc_alarm[10] = 1; /* 24H mode */
|
|
rtc_ricoh.delayed_write = 0;
|
|
}
|
|
if (currprefs.rtcfile[0]) {
|
|
TCHAR path[MAX_DPATH];
|
|
cfgfile_resolve_path_out_load(currprefs.rtcfile, path, MAX_DPATH, PATH_ROM);
|
|
struct zfile *f = zfile_fopen(path, _T("rb"));
|
|
if (f) {
|
|
int size = zfile_size32(f);
|
|
uae_u8 empty[16];
|
|
zfile_fread(empty, sizeof(empty), 1, f);
|
|
if (size > 16) {
|
|
rtc_ricoh.clock_control_d = empty[13];
|
|
rtc_ricoh.clock_control_e = empty[14];
|
|
rtc_ricoh.clock_control_f = empty[15];
|
|
zfile_fread(rtc_ricoh.rtc_alarm, RF5C01A_RAM_SIZE, 1, f);
|
|
zfile_fread(rtc_ricoh.rtc_memory, RF5C01A_RAM_SIZE, 1, f);
|
|
} else if (size == 16) {
|
|
rtc_msm.clock_control_d = empty[13];
|
|
rtc_msm.clock_control_e = empty[14];
|
|
rtc_msm.clock_control_f = empty[15];
|
|
}
|
|
zfile_fclose(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 clock_lget(uaecptr addr)
|
|
{
|
|
if ((addr & 0xffff) >= 0x8000 && currprefs.cs_fatgaryrev >= 0)
|
|
return dummy_get(addr, 4, false, 0);
|
|
|
|
return (clock_wget(addr) << 16) | clock_wget(addr + 2);
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 clock_wget(uaecptr addr)
|
|
{
|
|
if ((addr & 0xffff) >= 0x8000 && currprefs.cs_fatgaryrev >= 0)
|
|
return dummy_get(addr, 2, false, 0);
|
|
|
|
return (clock_bget(addr) << 8) | clock_bget(addr + 1);
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 clock_bget(uaecptr addr)
|
|
{
|
|
struct tm *ct;
|
|
uae_u8 v = 0;
|
|
|
|
if ((addr & 0xffff) >= 0x8000 && currprefs.cs_fatgaryrev >= 0)
|
|
return dummy_get(addr, 1, false, 0);
|
|
|
|
#ifdef CDTV
|
|
if (currprefs.cs_cdtvram && (addr & 0xffff) >= 0x8000)
|
|
return cdtv_battram_read(addr);
|
|
#endif
|
|
|
|
addr &= 0x3f;
|
|
if ((addr & 3) == 2 || (addr & 3) == 0 || currprefs.cs_rtc == 0) {
|
|
return dummy_get_safe(addr, 1, false, v);
|
|
}
|
|
time_t t = time(0);
|
|
t += currprefs.cs_rtc_adjust;
|
|
ct = localtime(&t);
|
|
addr >>= 2;
|
|
return getclockreg(addr, ct);
|
|
}
|
|
|
|
static void cputester_event(uae_u32 v)
|
|
{
|
|
IRQ_forced(4, 28 / 2);
|
|
}
|
|
|
|
static void REGPARAM2 clock_lput(uaecptr addr, uae_u32 value)
|
|
{
|
|
if ((addr & 0xffff) >= 0x8000 && currprefs.cs_fatgaryrev >= 0) {
|
|
dummy_put(addr, 4, value);
|
|
return;
|
|
}
|
|
|
|
clock_wput(addr, value >> 16);
|
|
clock_wput(addr + 2, value);
|
|
}
|
|
|
|
static void REGPARAM2 clock_wput(uaecptr addr, uae_u32 value)
|
|
{
|
|
if ((addr & 0xffff) >= 0x8000 && currprefs.cs_fatgaryrev >= 0) {
|
|
dummy_put(addr, 2, value);
|
|
return;
|
|
}
|
|
|
|
clock_bput(addr, value >> 8);
|
|
clock_bput(addr + 1, value);
|
|
}
|
|
|
|
static void REGPARAM2 clock_bput(uaecptr addr, uae_u32 value)
|
|
{
|
|
// write_log(_T("W: %x (%x): %x, PC=%08x\n"), addr, (addr & 0xff) >> 2, value & 0xff, M68K_GETPC);
|
|
|
|
if (currprefs.cputester && (addr & 65535) == 0) {
|
|
event2_newevent_xx(-1, CYCLE_UNIT * (64 / 2 - 1), 0, cputester_event);
|
|
}
|
|
|
|
if ((addr & 0xffff) >= 0x8000 && currprefs.cs_fatgaryrev >= 0) {
|
|
dummy_put(addr, 1, value);
|
|
return;
|
|
}
|
|
|
|
#ifdef CDTV
|
|
if (currprefs.cs_cdtvram && (addr & 0xffff) >= 0x8000) {
|
|
cdtv_battram_write(addr, value);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
addr &= 0x3f;
|
|
if ((addr & 1) != 1 || currprefs.cs_rtc == 0)
|
|
return;
|
|
addr >>= 2;
|
|
value &= 0x0f;
|
|
if (currprefs.cs_rtc == 1 || currprefs.cs_rtc == 3) { /* MSM6242B */
|
|
#if CLOCK_DEBUG
|
|
write_log(_T("CLOCK W %X: %X\n"), addr, value);
|
|
#endif
|
|
put_clock_msm(&rtc_msm, addr, value);
|
|
} else if (currprefs.cs_rtc == 2) { /* RF5C01A */
|
|
put_clock_ricoh(&rtc_ricoh, addr, value);
|
|
}
|
|
}
|
|
|
|
#ifdef SAVESTATE
|
|
|
|
/* CIA-A and CIA-B save/restore code */
|
|
|
|
static void save_cia_prepare(void)
|
|
{
|
|
CIA_update_check();
|
|
CIA_calctimers();
|
|
compute_passed_time();
|
|
}
|
|
|
|
void restore_cia_start(void)
|
|
{
|
|
/* Fixes very old statefiles without keyboard state */
|
|
kbstate = 3;
|
|
setcapslockstate(0);
|
|
kblostsynccnt = 0;
|
|
}
|
|
|
|
void restore_cia_finish(void)
|
|
{
|
|
eventtab[ev_cia].oldcycles = get_cycles();
|
|
CIA_update();
|
|
CIA_calctimers();
|
|
compute_passed_time();
|
|
//dumpcia ();
|
|
DISK_select_set(cia[1].prb);
|
|
}
|
|
|
|
uae_u8 *restore_cia(int num, uae_u8 *src)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
uae_u8 b;
|
|
uae_u16 w;
|
|
uae_u32 l;
|
|
|
|
/* CIA registers */
|
|
c->pra = restore_u8(); /* 0 PRA */
|
|
c->prb = restore_u8(); /* 1 PRB */
|
|
c->dra = restore_u8(); /* 2 DDRA */
|
|
c->drb = restore_u8(); /* 3 DDRB */
|
|
c->t[0].timer = restore_u16(); /* 4 TA */
|
|
c->t[1].timer = restore_u16(); /* 6 TB */
|
|
l = restore_u8(); /* 8/9/A TOD */
|
|
l |= restore_u8() << 8;
|
|
l |= restore_u8() << 16;
|
|
c->tod = l;
|
|
restore_u8(); /* B unused */
|
|
c->sdr = restore_u8(); /* C SDR */
|
|
c->icr2 = c->icr1 = restore_u8(); /* D ICR INFORMATION (not mask!) */
|
|
c->t[0].cr = restore_u8(); /* E CRA */
|
|
c->t[1].cr = restore_u8(); /* F CRB */
|
|
|
|
/* CIA internal data */
|
|
|
|
c->imask = restore_u8(); /* ICR MASK */
|
|
w = restore_u8(); /* timer A latch */
|
|
w |= restore_u8() << 8;
|
|
c->t[0].latch = w;
|
|
w = restore_u8(); /* timer B latch */
|
|
w |= restore_u8() << 8;
|
|
c->t[1].latch = w;
|
|
l = restore_u8(); /* TOD latched value */
|
|
l |= restore_u8() << 8;
|
|
l |= restore_u8() << 16;
|
|
c->tol = w;
|
|
l = restore_u8(); /* alarm */
|
|
l |= restore_u8() << 8;
|
|
l |= restore_u8() << 16;
|
|
c->alarm = l;
|
|
b = restore_u8();
|
|
c->tlatch = (b & 1) != 0; /* flags */
|
|
c->todon = (b & 2) != 0;
|
|
c->sdr_load = (b & 4) != 0;
|
|
b = restore_u8();
|
|
// if (num)
|
|
// div10 = CYCLE_UNIT * b;
|
|
c->sdr_cnt = restore_u8();
|
|
c->sdr_buf = restore_u8();
|
|
|
|
// 4.9.2+
|
|
c->icr1 = c->icr2;
|
|
c->t[0].loaddelay = 0;
|
|
c->t[0].inputpipe = 0;
|
|
if (c->t[0].cr & CR_START) {
|
|
c->t[0].inputpipe = CIA_PIPE_ALL_MASK;
|
|
}
|
|
c->t[1].loaddelay = 0;
|
|
c->t[1].inputpipe = 0;
|
|
if (c->t[1].cr & CR_START) {
|
|
c->t[1].inputpipe = CIA_PIPE_ALL_MASK;
|
|
}
|
|
if (restore_u8() & 1) {
|
|
c->icr1 = restore_u8();
|
|
c->t[0].inputpipe = restore_u16();
|
|
c->t[1].inputpipe = restore_u16();
|
|
c->t[0].loaddelay = restore_u32();
|
|
c->t[1].loaddelay = restore_u32();
|
|
changed_prefs.cs_eclockphase = currprefs.cs_eclockphase = restore_u16();
|
|
internaleclockphase = restore_u16();
|
|
|
|
}
|
|
|
|
return src;
|
|
}
|
|
|
|
uae_u8 *save_cia(int num, size_t *len, uae_u8 *dstptr)
|
|
{
|
|
struct CIA *c = &cia[num];
|
|
uae_u8 *dstbak,*dst, b;
|
|
uae_u16 t;
|
|
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc(uae_u8, 1000);
|
|
|
|
save_cia_prepare();
|
|
|
|
/* CIA registers */
|
|
|
|
save_u8(c->pra); /* 0 PRA */
|
|
save_u8(c->prb); /* 1 PRB */
|
|
save_u8(c->dra); /* 2 DDRA */
|
|
save_u8(c->drb); /* 3 DDRB */
|
|
t = c->t[0].timer - c->t[0].passed; /* 4 TA */
|
|
save_u16(t);
|
|
t = c->t[1].timer - c->t[1].passed; /* 6 TB */
|
|
save_u16(t);
|
|
save_u8((uae_u8)c->tod); /* 8 TODL */
|
|
save_u8((uae_u8)(c->tod >> 8)); /* 9 TODM */
|
|
save_u8((uae_u8)(c->tod >> 16)); /* A TODH */
|
|
save_u8(0); /* B unused */
|
|
save_u8(c->sdr); /* C SDR */
|
|
save_u8(c->icr2); /* D ICR INFORMATION (not mask!) */
|
|
save_u8(c->t[0].cr); /* E CRA */
|
|
save_u8(c->t[1].cr); /* F CRB */
|
|
|
|
/* CIA internal data */
|
|
|
|
save_u8(c->imask); /* ICR */
|
|
save_u8((uae_u8)c->t[0].latch); /* timer A latch LO */
|
|
save_u8(c->t[0].latch >> 8); /* timer A latch HI */
|
|
save_u8((uae_u8)c->t[1].latch); /* timer B latch LO */
|
|
save_u8(c->t[1].latch >> 8); /* timer B latch HI */
|
|
save_u8(c->tol); /* latched TOD LO */
|
|
save_u8(c->tol >> 8); /* latched TOD MED */
|
|
save_u8(c->tol >> 16); /* latched TOD HI */
|
|
save_u8(c->alarm); /* alarm LO */
|
|
save_u8(c->alarm >> 8); /* alarm MED */
|
|
save_u8(c->alarm >> 16); /* alarm HI */
|
|
b = 0;
|
|
b |= c->tlatch ? 1 : 0; /* is TOD latched? */
|
|
b |= c->todon ? 2 : 0; /* TOD stopped? */
|
|
b |= c->sdr_load ? 4 : 0; /* SDR loaded */
|
|
save_u8(b);
|
|
save_u8(0); // save_u8(num ? div10 / CYCLE_UNIT : 0);
|
|
save_u8(c->sdr_cnt);
|
|
save_u8(c->sdr_buf);
|
|
|
|
// 4.9.2+
|
|
save_u8(1);
|
|
save_u8(c->icr1);
|
|
save_u16(c->t[0].inputpipe);
|
|
save_u16(c->t[1].inputpipe);
|
|
save_u32(c->t[0].loaddelay);
|
|
save_u32(c->t[1].loaddelay);
|
|
save_u16(currprefs.cs_eclockphase);
|
|
save_u16(internaleclockphase);
|
|
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *save_keyboard(size_t *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dst, *dstbak;
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc(uae_u8, 4 + 4 + 1 + 1 + 1 + 1 + 1 + 2);
|
|
save_u32(getcapslockstate() ? 1 : 0);
|
|
save_u32(1);
|
|
save_u8(kbstate);
|
|
save_u8(0);
|
|
save_u8(0);
|
|
save_u8(0);
|
|
save_u8(kbcode);
|
|
save_u16(kblostsynccnt);
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *restore_keyboard(uae_u8 *src)
|
|
{
|
|
setcapslockstate(restore_u32() & 1);
|
|
uae_u32 v = restore_u32();
|
|
kbstate = restore_u8();
|
|
restore_u8();
|
|
restore_u8();
|
|
restore_u8();
|
|
kbcode = restore_u8();
|
|
kblostsynccnt = restore_u16();
|
|
if (!(v & 1)) {
|
|
kbstate = 3;
|
|
kblostsynccnt = 0;
|
|
}
|
|
return src;
|
|
}
|
|
|
|
#endif /* SAVESTATE */
|