Convert div counter to the SM mechanism
This commit is contained in:
parent
5974092c94
commit
c48097a484
@ -289,8 +289,8 @@ void GB_apu_div_event(GB_gameboy_t *gb)
|
||||
|
||||
void GB_apu_run(GB_gameboy_t *gb)
|
||||
{
|
||||
/* Convert 4MHZ to 2MHz. apu_cycles is always even. */
|
||||
uint8_t cycles = gb->apu.apu_cycles >> 1;
|
||||
/* Convert 4MHZ to 2MHz. apu_cycles is always divisable by 4. */
|
||||
uint8_t cycles = gb->apu.apu_cycles >> 2;
|
||||
gb->apu.apu_cycles = 0;
|
||||
if (!cycles) return;
|
||||
|
||||
|
@ -333,7 +333,8 @@ struct GB_gameboy_internal_s {
|
||||
/* Timing */
|
||||
GB_SECTION(timing,
|
||||
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. */
|
||||
uint16_t serial_cycles;
|
||||
uint16_t serial_length;
|
||||
|
@ -176,7 +176,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
}
|
||||
return gb->io_registers[GB_IO_TIMA];
|
||||
case GB_IO_DIV:
|
||||
return gb->div_cycles >> 8;
|
||||
return gb->div_counter >> 8;
|
||||
case GB_IO_HDMA5:
|
||||
if (!gb->cgb_mode) return 0xFF;
|
||||
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;
|
||||
|
||||
case GB_IO_DIV:
|
||||
GB_set_internal_div_counter(gb, 0);
|
||||
gb->div_state = 0; // Reset the div state machine
|
||||
return;
|
||||
|
||||
case GB_IO_JOYP:
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
static const unsigned int GB_TAC_RATIOS[] = {1024, 16, 64, 256};
|
||||
|
||||
#ifndef DISABLE_TIMEKEEPING
|
||||
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)
|
||||
{
|
||||
// Affected by speed boost
|
||||
gb->dma_cycles += cycles;
|
||||
|
||||
advance_tima_state_machine(gb);
|
||||
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);
|
||||
}
|
||||
}
|
||||
GB_timers_run(gb, cycles);
|
||||
|
||||
uint16_t previous_serial_cycles = gb->serial_cycles;
|
||||
gb->serial_cycles += cycles;
|
||||
@ -161,38 +194,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
||||
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 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];
|
||||
|
||||
/* 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. */
|
||||
if (!(new_tac & 4) || gb->div_cycles & (new_clocks >> 1)) {
|
||||
if (!(new_tac & 4) || gb->div_counter & (new_clocks >> 1)) {
|
||||
increase_tima(gb);
|
||||
}
|
||||
}
|
||||
|
@ -4,18 +4,40 @@
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
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_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 */
|
||||
void GB_timing_sync(GB_gameboy_t *gb);
|
||||
|
||||
|
||||
enum {
|
||||
GB_TIMA_RUNNING = 0,
|
||||
GB_TIMA_RELOADING = 1,
|
||||
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
|
||||
|
||||
#define GB_STATE(gb, unit, state) case state: goto unit##state
|
||||
|
||||
#define GB_UNIT(unit) uint32_t unit##_cycles, unit##_state
|
||||
|
||||
#endif /* timing_h */
|
||||
|
Loading…
Reference in New Issue
Block a user