Accuracy improvements to timers
This commit is contained in:
parent
d098458ee4
commit
55cbe5d4d0
@ -695,7 +695,6 @@ static bool registers(GB_gameboy_t *gb, char *arguments)
|
|||||||
GB_log(gb, "SP = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_SP], false));
|
GB_log(gb, "SP = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_SP], false));
|
||||||
GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false));
|
GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false));
|
||||||
|
|
||||||
GB_log(gb, "TIMA = %d/%u\n", gb->io_registers[GB_IO_TIMA], gb->tima_cycles);
|
|
||||||
GB_log(gb, "Display Controller: LY = %d/%u\n", gb->io_registers[GB_IO_LY], gb->display_cycles % 456);
|
GB_log(gb, "Display Controller: LY = %d/%u\n", gb->io_registers[GB_IO_LY], gb->display_cycles % 456);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -142,6 +142,7 @@ enum {
|
|||||||
#define LCDC_PERIOD 70224
|
#define LCDC_PERIOD 70224
|
||||||
#define CPU_FREQUENCY 0x400000
|
#define CPU_FREQUENCY 0x400000
|
||||||
#define DIV_CYCLES (0x100)
|
#define DIV_CYCLES (0x100)
|
||||||
|
#define INTERNAL_DIV_CYCLES (0x400)
|
||||||
#define FRAME_LENGTH 16742706 // in nanoseconds
|
#define FRAME_LENGTH 16742706 // in nanoseconds
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -283,7 +284,7 @@ typedef struct GB_gameboy_s {
|
|||||||
int64_t last_vblank;
|
int64_t last_vblank;
|
||||||
uint32_t display_cycles;
|
uint32_t display_cycles;
|
||||||
uint32_t div_cycles;
|
uint32_t div_cycles;
|
||||||
uint32_t tima_cycles;
|
GB_PADDING(uint32_t, tima_cycles);
|
||||||
GB_PADDING(uint32_t, dma_cycles);
|
GB_PADDING(uint32_t, dma_cycles);
|
||||||
GB_aligned_double apu_cycles;
|
GB_aligned_double apu_cycles;
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "debugger.h"
|
#include "debugger.h"
|
||||||
#include "mbc.h"
|
#include "mbc.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr);
|
typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr);
|
||||||
typedef void GB_write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
typedef void GB_write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
||||||
@ -353,7 +354,6 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
/* Hardware registers */
|
/* Hardware registers */
|
||||||
switch (addr & 0xFF) {
|
switch (addr & 0xFF) {
|
||||||
|
|
||||||
case GB_IO_TAC:
|
|
||||||
case GB_IO_SCX:
|
case GB_IO_SCX:
|
||||||
case GB_IO_IF:
|
case GB_IO_IF:
|
||||||
case GB_IO_TIMA:
|
case GB_IO_TIMA:
|
||||||
@ -374,6 +374,12 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
gb->io_registers[addr & 0xFF] = value;
|
gb->io_registers[addr & 0xFF] = value;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case GB_IO_TAC:
|
||||||
|
GB_emulate_timer_glitch(gb, gb->io_registers[GB_IO_TAC], value);
|
||||||
|
gb->io_registers[GB_IO_TAC] = value;
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
case GB_IO_LCDC:
|
case GB_IO_LCDC:
|
||||||
if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
||||||
gb->display_cycles = 0;
|
gb->display_cycles = 0;
|
||||||
@ -391,7 +397,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->div_cycles = 0;
|
GB_set_internal_div_counter(gb, 0);
|
||||||
gb->io_registers[GB_IO_DIV] = 0;
|
gb->io_registers[GB_IO_DIV] = 0;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -18,8 +18,10 @@ 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;
|
||||||
gb->div_cycles += cycles;
|
|
||||||
gb->tima_cycles += cycles;
|
for (int i = 0; i < cycles; i += 4) {
|
||||||
|
GB_set_internal_div_counter(gb, gb->div_cycles + 4);
|
||||||
|
}
|
||||||
|
|
||||||
if (gb->cgb_double_speed) {
|
if (gb->cgb_double_speed) {
|
||||||
cycles >>=1;
|
cycles >>=1;
|
||||||
@ -33,31 +35,61 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
gb->cycles_since_input_ir_change += cycles;
|
gb->cycles_since_input_ir_change += cycles;
|
||||||
GB_dma_run(gb);
|
GB_dma_run(gb);
|
||||||
GB_hdma_run(gb);
|
GB_hdma_run(gb);
|
||||||
GB_timers_run(gb);
|
|
||||||
GB_apu_run(gb);
|
GB_apu_run(gb);
|
||||||
GB_display_run(gb);
|
GB_display_run(gb);
|
||||||
GB_ir_run(gb);
|
GB_ir_run(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_timers_run(GB_gameboy_t *gb)
|
|
||||||
{
|
|
||||||
/* Standard Timers */
|
/* Standard Timers */
|
||||||
static const unsigned int GB_TAC_RATIOS[] = {1024, 16, 64, 256};
|
static const unsigned int GB_TAC_RATIOS[] = {1024, 16, 64, 256};
|
||||||
|
|
||||||
if (gb->div_cycles >= DIV_CYCLES) {
|
static void increase_tima(GB_gameboy_t *gb)
|
||||||
gb->div_cycles -= DIV_CYCLES;
|
{
|
||||||
gb->io_registers[GB_IO_DIV]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (gb->tima_cycles >= GB_TAC_RATIOS[gb->io_registers[GB_IO_TAC] & 3]) {
|
|
||||||
gb->tima_cycles -= GB_TAC_RATIOS[gb->io_registers[GB_IO_TAC] & 3];
|
|
||||||
if (gb->io_registers[GB_IO_TAC] & 4) {
|
|
||||||
gb->io_registers[GB_IO_TIMA]++;
|
gb->io_registers[GB_IO_TIMA]++;
|
||||||
if (gb->io_registers[GB_IO_TIMA] == 0) {
|
if (gb->io_registers[GB_IO_TIMA] == 0) {
|
||||||
gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA];
|
gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA];
|
||||||
gb->io_registers[GB_IO_IF] |= 4;
|
gb->io_registers[GB_IO_IF] |= 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
/* DIV and TIMA increase when a specific high-bit becomes a low-bit. */
|
||||||
|
value &= INTERNAL_DIV_CYCLES - 1;
|
||||||
|
if (counter_overflow_check(gb->div_cycles, value, DIV_CYCLES)) {
|
||||||
|
gb->io_registers[GB_IO_DIV]++;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
According to GiiBiiAdvance, GBC's behavior is different, but this was not tested or implemented.
|
||||||
|
*/
|
||||||
|
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
|
||||||
|
{
|
||||||
|
/* Glitch only happens when old_tac is enabled. */
|
||||||
|
if (!(old_tac & 4)) return;
|
||||||
|
|
||||||
|
unsigned int old_clocks = GB_TAC_RATIOS[old_tac & 3];
|
||||||
|
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)) {
|
||||||
|
/* 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)) {
|
||||||
|
increase_tima(gb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
|
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
|
||||||
void GB_timers_run(GB_gameboy_t *gb);
|
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);
|
||||||
#endif /* timing_h */
|
#endif /* timing_h */
|
||||||
|
Loading…
Reference in New Issue
Block a user