Improve serial accuracy

This commit is contained in:
Lior Halphon 2022-04-17 23:41:05 +03:00
parent 019f262531
commit b2edcc9543
5 changed files with 65 additions and 65 deletions

View File

@ -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 */

View File

@ -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;

View File

@ -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: {

View File

@ -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;

View File

@ -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,