Convert div counter to the SM mechanism

This commit is contained in:
Lior Halphon 2018-02-23 13:16:05 +02:00
parent 5974092c94
commit c48097a484
5 changed files with 79 additions and 55 deletions

View File

@ -289,8 +289,8 @@ void GB_apu_div_event(GB_gameboy_t *gb)
void GB_apu_run(GB_gameboy_t *gb) void GB_apu_run(GB_gameboy_t *gb)
{ {
/* Convert 4MHZ to 2MHz. apu_cycles is always even. */ /* Convert 4MHZ to 2MHz. apu_cycles is always divisable by 4. */
uint8_t cycles = gb->apu.apu_cycles >> 1; uint8_t cycles = gb->apu.apu_cycles >> 2;
gb->apu.apu_cycles = 0; gb->apu.apu_cycles = 0;
if (!cycles) return; if (!cycles) return;

View File

@ -333,7 +333,8 @@ struct GB_gameboy_internal_s {
/* Timing */ /* Timing */
GB_SECTION(timing, GB_SECTION(timing,
uint32_t display_cycles; // In 8 MHz units uint32_t display_cycles; // In 8 MHz units
uint32_t div_cycles; GB_UNIT(div);
uint32_t div_counter;
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */ uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
uint16_t serial_cycles; uint16_t serial_cycles;
uint16_t serial_length; uint16_t serial_length;

View File

@ -176,7 +176,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
} }
return gb->io_registers[GB_IO_TIMA]; return gb->io_registers[GB_IO_TIMA];
case GB_IO_DIV: case GB_IO_DIV:
return gb->div_cycles >> 8; return gb->div_counter >> 8;
case GB_IO_HDMA5: case GB_IO_HDMA5:
if (!gb->cgb_mode) return 0xFF; if (!gb->cgb_mode) return 0xFF;
return ((gb->hdma_on || gb->hdma_on_hblank)? 0 : 0x80) | ((gb->hdma_steps_left - 1) & 0x7F); return ((gb->hdma_on || gb->hdma_on_hblank)? 0 : 0x80) | ((gb->hdma_steps_left - 1) & 0x7F);
@ -484,7 +484,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
return; return;
case GB_IO_DIV: case GB_IO_DIV:
GB_set_internal_div_counter(gb, 0); gb->div_state = 0; // Reset the div state machine
return; return;
case GB_IO_JOYP: case GB_IO_JOYP:

View File

@ -6,6 +6,8 @@
#include <sys/time.h> #include <sys/time.h>
#endif #endif
static const unsigned int GB_TAC_RATIOS[] = {1024, 16, 64, 256};
#ifndef DISABLE_TIMEKEEPING #ifndef DISABLE_TIMEKEEPING
static int64_t get_nanoseconds(void) static int64_t get_nanoseconds(void)
{ {
@ -105,25 +107,56 @@ static void advance_tima_state_machine(GB_gameboy_t *gb)
} }
} }
static bool counter_overflow_check(uint32_t old, uint32_t new, uint32_t max)
{
return (old & (max >> 1)) && !(new & (max >> 1));
}
static void increase_tima(GB_gameboy_t *gb)
{
gb->io_registers[GB_IO_TIMA]++;
if (gb->io_registers[GB_IO_TIMA] == 0) {
gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA];
gb->tima_reload_state = GB_TIMA_RELOADING;
}
}
static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value)
{
/* TIMA increases when a specific high-bit becomes a low-bit. */
value &= INTERNAL_DIV_CYCLES - 1;
if ((gb->io_registers[GB_IO_TAC] & 4) &&
counter_overflow_check(gb->div_counter, value, GB_TAC_RATIOS[gb->io_registers[GB_IO_TAC] & 3])) {
increase_tima(gb);
}
if (counter_overflow_check(gb->div_counter, value, gb->cgb_double_speed? 0x4000 : 0x2000)) {
GB_apu_run(gb);
GB_apu_div_event(gb);
}
gb->div_counter = value;
}
static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles)
{
GB_STATE_MACHINE(gb, div, cycles) {
GB_STATE(gb, div, 1);
}
GB_set_internal_div_counter(gb, 0);
while (true) {
advance_tima_state_machine(gb);
GB_set_internal_div_counter(gb, gb->div_counter + 4);
gb->apu.apu_cycles += 4 << !gb->cgb_double_speed;
GB_SLEEP(gb, div, 1, 4);
}
}
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
{ {
// Affected by speed boost // Affected by speed boost
gb->dma_cycles += cycles; gb->dma_cycles += cycles;
advance_tima_state_machine(gb); GB_timers_run(gb, cycles);
for (int i = 0; i < cycles; i += 4) {
/* This is a bit tricky. The DIV and APU are tightly coupled, but DIV is affected
by the speed boost while the APU is not */
GB_set_internal_div_counter(gb, gb->div_cycles + 4);
gb->apu.apu_cycles += 4 >> gb->cgb_double_speed;
}
if (cycles > 4) {
advance_tima_state_machine(gb);
if (cycles > 8) {
advance_tima_state_machine(gb);
}
}
uint16_t previous_serial_cycles = gb->serial_cycles; uint16_t previous_serial_cycles = gb->serial_cycles;
gb->serial_cycles += cycles; gb->serial_cycles += cycles;
@ -161,38 +194,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
GB_ir_run(gb); GB_ir_run(gb);
} }
/* Standard Timers */
static const unsigned int GB_TAC_RATIOS[] = {1024, 16, 64, 256};
static void increase_tima(GB_gameboy_t *gb)
{
gb->io_registers[GB_IO_TIMA]++;
if (gb->io_registers[GB_IO_TIMA] == 0) {
gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA];
gb->tima_reload_state = GB_TIMA_RELOADING;
}
}
static bool counter_overflow_check(uint32_t old, uint32_t new, uint32_t max)
{
return (old & (max >> 1)) && !(new & (max >> 1));
}
void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value)
{
/* TIMA increases when a specific high-bit becomes a low-bit. */
value &= INTERNAL_DIV_CYCLES - 1;
if ((gb->io_registers[GB_IO_TAC] & 4) &&
counter_overflow_check(gb->div_cycles, value, GB_TAC_RATIOS[gb->io_registers[GB_IO_TAC] & 3])) {
increase_tima(gb);
}
if (counter_overflow_check(gb->div_cycles, value, gb->cgb_double_speed? 0x4000 : 0x2000)) {
GB_apu_run(gb);
GB_apu_div_event(gb);
}
gb->div_cycles = value;
}
/* /*
This glitch is based on the expected results of mooneye-gb rapid_toggle test. This glitch is based on the expected results of mooneye-gb rapid_toggle test.
This glitch happens because how TIMA is increased, see GB_set_internal_div_counter. This glitch happens because how TIMA is increased, see GB_set_internal_div_counter.
@ -207,9 +208,9 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
unsigned int new_clocks = GB_TAC_RATIOS[new_tac & 3]; unsigned int new_clocks = GB_TAC_RATIOS[new_tac & 3];
/* The bit used for overflow testing must have been 1 */ /* The bit used for overflow testing must have been 1 */
if (gb->div_cycles & (old_clocks >> 1)) { if (gb->div_counter & (old_clocks >> 1)) {
/* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */ /* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */
if (!(new_tac & 4) || gb->div_cycles & (new_clocks >> 1)) { if (!(new_tac & 4) || gb->div_counter & (new_clocks >> 1)) {
increase_tima(gb); increase_tima(gb);
} }
} }

View File

@ -4,18 +4,40 @@
#ifdef GB_INTERNAL #ifdef GB_INTERNAL
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles); void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value);
void GB_rtc_run(GB_gameboy_t *gb); void GB_rtc_run(GB_gameboy_t *gb);
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac); void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac);
bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */ bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */
void GB_timing_sync(GB_gameboy_t *gb); void GB_timing_sync(GB_gameboy_t *gb);
enum { enum {
GB_TIMA_RUNNING = 0, GB_TIMA_RUNNING = 0,
GB_TIMA_RELOADING = 1, GB_TIMA_RELOADING = 1,
GB_TIMA_RELOADED = 2 GB_TIMA_RELOADED = 2
}; };
#define GB_HALT_VALUE (0xFFFF)
#define GB_SLEEP(gb, unit, state, cycles) do {\
(gb)->unit##_cycles -= cycles; \
if ((gb)->unit##_cycles <= 0) {\
(gb)->unit##_state = state;\
return;\
unit##state:; \
}\
} while (0)
#define GB_HALT(gb, unit) (gb)->unit##_cycles = GB_HALT_VALUE
#define GB_STATE_MACHINE(gb, unit, cycles) \
(gb)->unit##_cycles += cycles; \
if ((gb)->unit##_cycles <= 0 || (gb)->unit##_cycles == GB_HALT_VALUE) {\
return;\
}\
switch ((gb)->unit##_state)
#endif #endif
#define GB_STATE(gb, unit, state) case state: goto unit##state
#define GB_UNIT(unit) uint32_t unit##_cycles, unit##_state
#endif /* timing_h */ #endif /* timing_h */