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)
|
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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user