diff --git a/source/cd_hw/scd.c b/source/cd_hw/scd.c index 9fe24a8..bbbc8fa 100644 --- a/source/cd_hw/scd.c +++ b/source/cd_hw/scd.c @@ -285,7 +285,7 @@ static unsigned int scd_read_byte(unsigned int address) if (address == 0xff8000) { /* register $00 is reserved for MAIN-CPU, we use $06 instead */ - return scd.regs[0x06 >> 1].byte.h; + return scd.regs[0x06>>1].byte.h; } /* RESET status */ @@ -299,10 +299,10 @@ static unsigned int scd_read_byte(unsigned int address) if ((address >= 0xff8050) && (address <= 0xff8056)) { /* shifted 4-bit input (xxxx00) */ - uint8 bits = (scd.regs[0x4e >> 1].w >> (((address & 6) ^ 6) << 1)) << 2; + uint8 bits = (scd.regs[0x4e>>1].w >> (((address & 6) ^ 6) << 1)) << 2; /* color code */ - uint8 code = scd.regs[0x4c >> 1].byte.l; + uint8 code = scd.regs[0x4c>>1].byte.l; /* 16-bit font data (4 pixels = 16 bits) */ uint16 data = (code >> (bits & 4)) & 0x0f; @@ -320,7 +320,7 @@ static unsigned int scd_read_byte(unsigned int address) } /* MAIN-CPU communication words */ - if ((address & 0xf0) == 0x10) + if ((address & 0x1f0) == 0x10) { s68k_poll_detect(address & 0x1f); } @@ -366,17 +366,24 @@ static unsigned int scd_read_word(unsigned int address) if (address == 0xff8000) { /* register $00 is reserved for MAIN-CPU, we use $06 instead */ - return scd.regs[0x06 >> 1].w; + return scd.regs[0x06>>1].w; + } + + /* Stopwatch counter (word access only ?) */ + if (address == 0xff800c) + { + /* cycle-accurate counter value */ + return (scd.regs[0x0c>>1].w + ((s68k.cycles - scd.stopwatch) / TIMERS_SCYCLES_RATIO)) & 0xfff; } /* Font data */ if ((address >= 0xff8050) && (address <= 0xff8056)) { /* shifted 4-bit input (xxxx00) */ - uint8 bits = (scd.regs[0x4e >> 1].w >> (((address & 6) ^ 6) << 1)) << 2; + uint8 bits = (scd.regs[0x4e>>1].w >> (((address & 6) ^ 6) << 1)) << 2; /* color code */ - uint8 code = scd.regs[0x4c >> 1].byte.l; + uint8 code = scd.regs[0x4c>>1].byte.l; /* 16-bit font data (4 pixels = 16 bits) */ uint16 data = (code >> (bits & 4)) & 0x0f; @@ -394,7 +401,7 @@ static unsigned int scd_read_word(unsigned int address) } /* MAIN-CPU communication words */ - if ((address & 0xf0) == 0x10) + if ((address & 0x1f0) == 0x10) { /* relative MAIN-CPU cycle counter */ unsigned int cycles = (s68k.cycles * MCYCLES_PER_LINE) / SCYCLES_PER_LINE; @@ -682,7 +689,7 @@ static void scd_write_byte(unsigned int address, unsigned int data) case 0x31: /* Timer */ { /* reload timer (one timer clock = 384 CPU cycles) */ - scd.timer = data * 384 * 4; + scd.timer = data * TIMERS_SCYCLES_RATIO; /* only non-zero data starts timer, writing zero stops it */ if (data) @@ -939,8 +946,12 @@ static void scd_write_word(unsigned int address, unsigned int data) return; } - case 0x0c: /* Stopwatch */ + case 0x0c: /* Stopwatch (word access only) */ { + /* synchronize the counter with SUB-CPU */ + int ticks = (s68k.cycles - scd.stopwatch) / TIMERS_SCYCLES_RATIO; + scd.stopwatch += (ticks * TIMERS_SCYCLES_RATIO); + /* any writes clear the counter */ scd.regs[0x0c>>1].w = 0; return; @@ -961,7 +972,7 @@ static void scd_write_word(unsigned int address, unsigned int data) data &= 0xff; /* reload timer (one timer clock = 384 CPU cycles) */ - scd.timer = data * 384 * 4; + scd.timer = data * TIMERS_SCYCLES_RATIO; /* only non-zero data starts timer, writing zero stops it */ if (data) @@ -1187,8 +1198,9 @@ void scd_reset(int hard) /* RESET register always return 1 (register $06 is unused by both sides, it is used for SUB-CPU first register) */ scd.regs[0x06>>1].byte.l = 0x01; - /* Reset TIMER counter */ + /* Reset TIMER & STOPWATCH counters */ scd.timer = 0; + scd.stopwatch = 0; /* Reset frame cycle counter */ scd.cycles = 0; @@ -1254,9 +1266,6 @@ void scd_update(unsigned int cycles) } } - /* Stop Watch (TODO: improve timing accuracy, one unit = 384 CPU cycles) */ - scd.regs[0x0c>>1].w = (scd.regs[0x0c>>1].w + 2) & 0xfff; - /* Timer */ if (scd.timer) { @@ -1265,7 +1274,7 @@ void scd_update(unsigned int cycles) if (scd.timer <= 0) { /* reload timer (one timer clock = 384 CPU cycles) */ - scd.timer += (scd.regs[0x30>>1].byte.l * 384 * 4); + scd.timer += (scd.regs[0x30>>1].byte.l * TIMERS_SCYCLES_RATIO); /* level 3 interrupt enabled ? */ if (scd.regs[0x32>>1].byte.l & 0x08) @@ -1287,6 +1296,24 @@ void scd_update(unsigned int cycles) } } +void scd_end_frame(unsigned int cycles) +{ + /* run Stopwatch until end of frame */ + int ticks = (cycles - scd.stopwatch) / TIMERS_SCYCLES_RATIO; + scd.regs[0x0c>>1].w = (scd.regs[0x0c>>1].w + ticks) & 0xfff; + + /* adjust Stopwatch counter for next frame (can be negative) */ + scd.stopwatch += (ticks * TIMERS_SCYCLES_RATIO) - cycles; + + /* adjust SUB-CPU & GPU cycle counters for next frame */ + s68k.cycles -= cycles; + gfx.cycles -= cycles; + + /* reset CPU registers polling */ + m68k.poll.cycle = 0; + s68k.poll.cycle = 0; +} + int scd_context_save(uint8 *state) { uint16 tmp16; diff --git a/source/cd_hw/scd.h b/source/cd_hw/scd.h index 1e1bc11..29d26d2 100644 --- a/source/cd_hw/scd.h +++ b/source/cd_hw/scd.h @@ -46,11 +46,15 @@ #define scd ext.cd_hw -/* 5000000 clocks/s = approx. 3184 clocks/line with a master clock of 53.693175 Mhz */ -/* TODO: use emulated master clock as reference ? */ +/* 5000000 SCD clocks/s = ~3184 clocks/line with a Master Clock of 53.693175 MHz */ +/* This would be slightly (~30 clocks) more on PAL systems because of the slower */ +/* Master Clock (53.203424 MHz) but not enough to really care about since clocks */ +/* are not running in sync anyway. */ #define SCD_CLOCK 50000000 #define SCYCLES_PER_LINE 3184 +/* Timer & Stopwatch clocks divider */ +#define TIMERS_SCYCLES_RATIO (384 * 4) /* CD hardware */ typedef struct @@ -63,6 +67,7 @@ typedef struct uint8 bram[0x2000]; /* 8K Backup RAM */ reg16_t regs[0x100]; /* 256 x 16-bit ASIC registers */ uint32 cycles; /* Master clock counter */ + int32 stopwatch; /* Clockwatch counter */ int32 timer; /* Timer counter */ uint8 pending; /* Pending interrupts */ uint8 dmna; /* Pending DMNA write status */ @@ -76,6 +81,7 @@ typedef struct extern void scd_init(void); extern void scd_reset(int hard); extern void scd_update(unsigned int cycles); +extern void scd_end_frame(unsigned int cycles); extern int scd_context_load(uint8 *state); extern int scd_context_save(uint8 *state); extern int scd_68k_irq_ack(int level); diff --git a/source/mem68k.c b/source/mem68k.c index c970e0e..90bdc62 100644 --- a/source/mem68k.c +++ b/source/mem68k.c @@ -466,6 +466,16 @@ unsigned int ctrl_io_read_word(unsigned int address) return *(uint16 *)(m68k.memory_map[0].base + 0x72); } + /* Stopwatch counter (word read access only ?) */ + if (index == 0x0c) + { + /* relative SUB-CPU cycle counter */ + unsigned int cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE; + + /* cycle-accurate counter value */ + return (scd.regs[0x0c>>1].w + ((cycles - scd.stopwatch) / TIMERS_SCYCLES_RATIO)) & 0xfff; + } + /* default registers */ if (index < 0x30) { diff --git a/source/system.c b/source/system.c index e291630..bc7b45d 100644 --- a/source/system.c +++ b/source/system.c @@ -992,15 +992,12 @@ void system_frame_scd(int do_skip) } while (++line < (lines_per_frame - 1)); - /* reset CPU registers polling */ - m68k.poll.cycle = 0; - s68k.poll.cycle = 0; + /* prepare for next SCD frame */ + scd_end_frame(scd.cycles); /* adjust CPU cycle counters for next frame */ Z80.cycles -= mcycles_vdp; m68k.cycles -= mcycles_vdp; - s68k.cycles -= scd.cycles; - gfx.cycles -= scd.cycles; } void system_frame_sms(int do_skip)