From b2edcc9543d6eeae38a2c185783c18c92f6e65ba Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 17 Apr 2022 23:41:05 +0300 Subject: [PATCH] Improve serial accuracy --- Core/gb.c | 17 ++++++++-- Core/gb.h | 4 +-- Core/memory.c | 18 +++++------ Core/timing.c | 90 +++++++++++++++++++++++---------------------------- Core/timing.h | 1 + 5 files changed, 65 insertions(+), 65 deletions(-) diff --git a/Core/gb.c b/Core/gb.c index 3c230bd..65f2d2c 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1293,26 +1293,38 @@ void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfe bool GB_serial_get_data_bit(GB_gameboy_t *gb) { + if (!(gb->io_registers[GB_IO_SC] & 0x80)) { + /* Disabled serial returns 0 bits */ + return false; + } + if (gb->io_registers[GB_IO_SC] & 1) { /* Internal Clock */ GB_log(gb, "Serial read request while using internal clock. \n"); - return 0xFF; + return true; } return gb->io_registers[GB_IO_SB] & 0x80; } void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data) { + if (!(gb->io_registers[GB_IO_SC] & 0x80)) { + /* Serial disabled */ + return; + } + if (gb->io_registers[GB_IO_SC] & 1) { /* Internal Clock */ GB_log(gb, "Serial write request while using internal clock. \n"); return; } + gb->io_registers[GB_IO_SB] <<= 1; gb->io_registers[GB_IO_SB] |= data; gb->serial_count++; if (gb->serial_count == 8) { gb->io_registers[GB_IO_IF] |= 8; + gb->io_registers[GB_IO_SC] &= ~0x80; gb->serial_count = 0; } } @@ -1632,8 +1644,7 @@ void GB_reset(GB_gameboy_t *gb) } reset_ram(gb); - /* The serial interrupt always occur on the 0xF7th cycle of every 0x100 cycle since boot. */ - gb->serial_cycles = 0x100-0xF7; + gb->serial_mask = 0x80; gb->io_registers[GB_IO_SC] = 0x7E; /* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */ diff --git a/Core/gb.h b/Core/gb.h index e47212c..11a22bb 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -545,8 +545,8 @@ struct GB_gameboy_internal_s { GB_UNIT(div); uint16_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; + bool serial_master_clock; + uint8_t serial_mask; uint8_t double_speed_alignment; uint8_t serial_count; int32_t speed_switch_halt_countdown; diff --git a/Core/memory.c b/Core/memory.c index 1838e59..9d6d452 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -1606,26 +1606,24 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) gb->hdma_steps_left = (gb->io_registers[GB_IO_HDMA5] & 0x7F) + 1; return; - /* Todo: what happens when starting a transfer during a transfer? - What happens when starting a transfer during external clock? - */ + /* TODO: What happens when starting a transfer during external clock? + TODO: When a cable is connected, the clock of the other side affects "zombie" serial clocking */ case GB_IO_SC: + gb->serial_count = 0; if (!gb->cgb_mode) { value |= 2; } + if (gb->serial_master_clock) { + GB_serial_master_edge(gb); + } gb->io_registers[GB_IO_SC] = value | (~0x83); + gb->serial_mask = gb->cgb_mode && (value & 2)? 4 : 0x80; if ((value & 0x80) && (value & 0x1) ) { - gb->serial_length = gb->cgb_mode && (value & 2)? 16 : 512; - gb->serial_count = 0; - /* Todo: This is probably incorrect for CGB's faster clock mode. */ - gb->serial_cycles &= 0xFF; if (gb->serial_transfer_bit_start_callback) { gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80); } } - else { - gb->serial_length = 0; - } + return; case GB_IO_RP: { diff --git a/Core/timing.c b/Core/timing.c index 3ff0106..41c6752 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -163,6 +163,42 @@ static void increase_tima(GB_gameboy_t *gb) } } +void GB_serial_master_edge(GB_gameboy_t *gb) +{ + if (unlikely(gb->printer_callback && (gb->printer.command_state || gb->printer.bits_received))) { + gb->printer.idle_time += 1 << gb->serial_mask; + } + + gb->serial_master_clock ^= true; + + if (!gb->serial_master_clock && (gb->io_registers[GB_IO_SC] & 0x81) == 0x81) { + gb->serial_count++; + if (gb->serial_count == 8) { + gb->serial_count = 0; + gb->io_registers[GB_IO_SC] &= ~0x80; + gb->io_registers[GB_IO_IF] |= 8; + } + + gb->io_registers[GB_IO_SB] <<= 1; + + if (gb->serial_transfer_bit_end_callback) { + gb->io_registers[GB_IO_SB] |= gb->serial_transfer_bit_end_callback(gb); + } + else { + gb->io_registers[GB_IO_SB] |= 1; + } + + if (gb->serial_count) { + /* Still more bits to send */ + if (gb->serial_transfer_bit_start_callback) { + gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80); + } + } + + } +} + + void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value) { /* TIMA increases when a specific high-bit becomes a low-bit. */ @@ -171,6 +207,10 @@ void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value) increase_tima(gb); } + if (triggers & gb->serial_mask) { + GB_serial_master_edge(gb); + } + /* TODO: Can switching to double speed mode trigger an event? */ uint16_t apu_bit = gb->cgb_double_speed? 0x2000 : 0x1000; if (triggers & apu_bit) { @@ -208,53 +248,6 @@ static void timers_run(GB_gameboy_t *gb, uint8_t cycles) } } -static void advance_serial(GB_gameboy_t *gb, uint8_t cycles) -{ - if (unlikely(gb->printer_callback && (gb->printer.command_state || gb->printer.bits_received))) { - gb->printer.idle_time += cycles; - } - if (likely(gb->serial_length == 0)) { - gb->serial_cycles += cycles; - return; - } - - while (cycles > gb->serial_length) { - advance_serial(gb, gb->serial_length); - cycles -= gb->serial_length; - } - - uint16_t previous_serial_cycles = gb->serial_cycles; - gb->serial_cycles += cycles; - if ((gb->serial_cycles & gb->serial_length) != (previous_serial_cycles & gb->serial_length)) { - gb->serial_count++; - if (gb->serial_count == 8) { - gb->serial_length = 0; - gb->serial_count = 0; - gb->io_registers[GB_IO_SC] &= ~0x80; - gb->io_registers[GB_IO_IF] |= 8; - } - - gb->io_registers[GB_IO_SB] <<= 1; - - if (gb->serial_transfer_bit_end_callback) { - gb->io_registers[GB_IO_SB] |= gb->serial_transfer_bit_end_callback(gb); - } - else { - gb->io_registers[GB_IO_SB] |= 1; - } - - if (gb->serial_length) { - /* Still more bits to send */ - if (gb->serial_transfer_bit_start_callback) { - gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80); - } - } - - } - return; - -} - void GB_set_rtc_mode(GB_gameboy_t *gb, GB_rtc_mode_t mode) { if (gb->rtc_mode != mode) { @@ -396,9 +389,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) gb->dma_cycles = cycles; timers_run(gb, cycles); - if (unlikely(!gb->stopped)) { - advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode - } if (unlikely(gb->speed_switch_halt_countdown)) { gb->speed_switch_halt_countdown -= cycles; diff --git a/Core/timing.h b/Core/timing.h index ee817d1..8cc82e3 100644 --- a/Core/timing.h +++ b/Core/timing.h @@ -19,6 +19,7 @@ internal void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t internal bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */ internal void GB_timing_sync(GB_gameboy_t *gb); internal void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value); +internal void GB_serial_master_edge(GB_gameboy_t *gb); enum { GB_TIMA_RUNNING = 0, GB_TIMA_RELOADING = 1,