[SCD] implemented cycle-accurate stopwatch counter

This commit is contained in:
EkeEke 2012-11-22 22:04:29 +01:00
parent b28c86d826
commit 46e1894d7a
4 changed files with 63 additions and 23 deletions

View File

@ -285,7 +285,7 @@ static unsigned int scd_read_byte(unsigned int address)
if (address == 0xff8000) if (address == 0xff8000)
{ {
/* register $00 is reserved for MAIN-CPU, we use $06 instead */ /* 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 */ /* RESET status */
@ -299,10 +299,10 @@ static unsigned int scd_read_byte(unsigned int address)
if ((address >= 0xff8050) && (address <= 0xff8056)) if ((address >= 0xff8050) && (address <= 0xff8056))
{ {
/* shifted 4-bit input (xxxx00) */ /* 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 */ /* 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) */ /* 16-bit font data (4 pixels = 16 bits) */
uint16 data = (code >> (bits & 4)) & 0x0f; uint16 data = (code >> (bits & 4)) & 0x0f;
@ -320,7 +320,7 @@ static unsigned int scd_read_byte(unsigned int address)
} }
/* MAIN-CPU communication words */ /* MAIN-CPU communication words */
if ((address & 0xf0) == 0x10) if ((address & 0x1f0) == 0x10)
{ {
s68k_poll_detect(address & 0x1f); s68k_poll_detect(address & 0x1f);
} }
@ -366,17 +366,24 @@ static unsigned int scd_read_word(unsigned int address)
if (address == 0xff8000) if (address == 0xff8000)
{ {
/* register $00 is reserved for MAIN-CPU, we use $06 instead */ /* 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 */ /* Font data */
if ((address >= 0xff8050) && (address <= 0xff8056)) if ((address >= 0xff8050) && (address <= 0xff8056))
{ {
/* shifted 4-bit input (xxxx00) */ /* 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 */ /* 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) */ /* 16-bit font data (4 pixels = 16 bits) */
uint16 data = (code >> (bits & 4)) & 0x0f; uint16 data = (code >> (bits & 4)) & 0x0f;
@ -394,7 +401,7 @@ static unsigned int scd_read_word(unsigned int address)
} }
/* MAIN-CPU communication words */ /* MAIN-CPU communication words */
if ((address & 0xf0) == 0x10) if ((address & 0x1f0) == 0x10)
{ {
/* relative MAIN-CPU cycle counter */ /* relative MAIN-CPU cycle counter */
unsigned int cycles = (s68k.cycles * MCYCLES_PER_LINE) / SCYCLES_PER_LINE; 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 */ case 0x31: /* Timer */
{ {
/* reload timer (one timer clock = 384 CPU cycles) */ /* 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 */ /* only non-zero data starts timer, writing zero stops it */
if (data) if (data)
@ -939,8 +946,12 @@ static void scd_write_word(unsigned int address, unsigned int data)
return; 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 */ /* any writes clear the counter */
scd.regs[0x0c>>1].w = 0; scd.regs[0x0c>>1].w = 0;
return; return;
@ -961,7 +972,7 @@ static void scd_write_word(unsigned int address, unsigned int data)
data &= 0xff; data &= 0xff;
/* reload timer (one timer clock = 384 CPU cycles) */ /* 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 */ /* only non-zero data starts timer, writing zero stops it */
if (data) 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) */ /* 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; scd.regs[0x06>>1].byte.l = 0x01;
/* Reset TIMER counter */ /* Reset TIMER & STOPWATCH counters */
scd.timer = 0; scd.timer = 0;
scd.stopwatch = 0;
/* Reset frame cycle counter */ /* Reset frame cycle counter */
scd.cycles = 0; 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 */ /* Timer */
if (scd.timer) if (scd.timer)
{ {
@ -1265,7 +1274,7 @@ void scd_update(unsigned int cycles)
if (scd.timer <= 0) if (scd.timer <= 0)
{ {
/* reload timer (one timer clock = 384 CPU cycles) */ /* 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 ? */ /* level 3 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x08) 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) int scd_context_save(uint8 *state)
{ {
uint16 tmp16; uint16 tmp16;

View File

@ -46,11 +46,15 @@
#define scd ext.cd_hw #define scd ext.cd_hw
/* 5000000 clocks/s = approx. 3184 clocks/line with a master clock of 53.693175 Mhz */ /* 5000000 SCD clocks/s = ~3184 clocks/line with a Master Clock of 53.693175 MHz */
/* TODO: use emulated master clock as reference ? */ /* 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 SCD_CLOCK 50000000
#define SCYCLES_PER_LINE 3184 #define SCYCLES_PER_LINE 3184
/* Timer & Stopwatch clocks divider */
#define TIMERS_SCYCLES_RATIO (384 * 4)
/* CD hardware */ /* CD hardware */
typedef struct typedef struct
@ -63,6 +67,7 @@ typedef struct
uint8 bram[0x2000]; /* 8K Backup RAM */ uint8 bram[0x2000]; /* 8K Backup RAM */
reg16_t regs[0x100]; /* 256 x 16-bit ASIC registers */ reg16_t regs[0x100]; /* 256 x 16-bit ASIC registers */
uint32 cycles; /* Master clock counter */ uint32 cycles; /* Master clock counter */
int32 stopwatch; /* Clockwatch counter */
int32 timer; /* Timer counter */ int32 timer; /* Timer counter */
uint8 pending; /* Pending interrupts */ uint8 pending; /* Pending interrupts */
uint8 dmna; /* Pending DMNA write status */ uint8 dmna; /* Pending DMNA write status */
@ -76,6 +81,7 @@ typedef struct
extern void scd_init(void); extern void scd_init(void);
extern void scd_reset(int hard); extern void scd_reset(int hard);
extern void scd_update(unsigned int cycles); 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_load(uint8 *state);
extern int scd_context_save(uint8 *state); extern int scd_context_save(uint8 *state);
extern int scd_68k_irq_ack(int level); extern int scd_68k_irq_ack(int level);

View File

@ -466,6 +466,16 @@ unsigned int ctrl_io_read_word(unsigned int address)
return *(uint16 *)(m68k.memory_map[0].base + 0x72); 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 */ /* default registers */
if (index < 0x30) if (index < 0x30)
{ {

View File

@ -992,15 +992,12 @@ void system_frame_scd(int do_skip)
} }
while (++line < (lines_per_frame - 1)); while (++line < (lines_per_frame - 1));
/* reset CPU registers polling */ /* prepare for next SCD frame */
m68k.poll.cycle = 0; scd_end_frame(scd.cycles);
s68k.poll.cycle = 0;
/* adjust CPU cycle counters for next frame */ /* adjust CPU cycle counters for next frame */
Z80.cycles -= mcycles_vdp; Z80.cycles -= mcycles_vdp;
m68k.cycles -= mcycles_vdp; m68k.cycles -= mcycles_vdp;
s68k.cycles -= scd.cycles;
gfx.cycles -= scd.cycles;
} }
void system_frame_sms(int do_skip) void system_frame_sms(int do_skip)