mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-09-10 16:53:15 +02:00
[SCD] implemented cycle-accurate stopwatch counter
This commit is contained in:
parent
b28c86d826
commit
46e1894d7a
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user