Improve serial accuracy
This commit is contained in:
parent
019f262531
commit
b2edcc9543
17
Core/gb.c
17
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)
|
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) {
|
if (gb->io_registers[GB_IO_SC] & 1) {
|
||||||
/* Internal Clock */
|
/* Internal Clock */
|
||||||
GB_log(gb, "Serial read request while using internal clock. \n");
|
GB_log(gb, "Serial read request while using internal clock. \n");
|
||||||
return 0xFF;
|
return true;
|
||||||
}
|
}
|
||||||
return gb->io_registers[GB_IO_SB] & 0x80;
|
return gb->io_registers[GB_IO_SB] & 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data)
|
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) {
|
if (gb->io_registers[GB_IO_SC] & 1) {
|
||||||
/* Internal Clock */
|
/* Internal Clock */
|
||||||
GB_log(gb, "Serial write request while using internal clock. \n");
|
GB_log(gb, "Serial write request while using internal clock. \n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->io_registers[GB_IO_SB] <<= 1;
|
gb->io_registers[GB_IO_SB] <<= 1;
|
||||||
gb->io_registers[GB_IO_SB] |= data;
|
gb->io_registers[GB_IO_SB] |= data;
|
||||||
gb->serial_count++;
|
gb->serial_count++;
|
||||||
if (gb->serial_count == 8) {
|
if (gb->serial_count == 8) {
|
||||||
gb->io_registers[GB_IO_IF] |= 8;
|
gb->io_registers[GB_IO_IF] |= 8;
|
||||||
|
gb->io_registers[GB_IO_SC] &= ~0x80;
|
||||||
gb->serial_count = 0;
|
gb->serial_count = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1632,8 +1644,7 @@ void GB_reset(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
reset_ram(gb);
|
reset_ram(gb);
|
||||||
|
|
||||||
/* The serial interrupt always occur on the 0xF7th cycle of every 0x100 cycle since boot. */
|
gb->serial_mask = 0x80;
|
||||||
gb->serial_cycles = 0x100-0xF7;
|
|
||||||
gb->io_registers[GB_IO_SC] = 0x7E;
|
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 */
|
/* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */
|
||||||
|
@ -545,8 +545,8 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_UNIT(div);
|
GB_UNIT(div);
|
||||||
uint16_t div_counter;
|
uint16_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;
|
bool serial_master_clock;
|
||||||
uint16_t serial_length;
|
uint8_t serial_mask;
|
||||||
uint8_t double_speed_alignment;
|
uint8_t double_speed_alignment;
|
||||||
uint8_t serial_count;
|
uint8_t serial_count;
|
||||||
int32_t speed_switch_halt_countdown;
|
int32_t speed_switch_halt_countdown;
|
||||||
|
@ -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;
|
gb->hdma_steps_left = (gb->io_registers[GB_IO_HDMA5] & 0x7F) + 1;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Todo: what happens when starting a transfer during a transfer?
|
/* TODO: What happens when starting a transfer during external clock?
|
||||||
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:
|
case GB_IO_SC:
|
||||||
|
gb->serial_count = 0;
|
||||||
if (!gb->cgb_mode) {
|
if (!gb->cgb_mode) {
|
||||||
value |= 2;
|
value |= 2;
|
||||||
}
|
}
|
||||||
|
if (gb->serial_master_clock) {
|
||||||
|
GB_serial_master_edge(gb);
|
||||||
|
}
|
||||||
gb->io_registers[GB_IO_SC] = value | (~0x83);
|
gb->io_registers[GB_IO_SC] = value | (~0x83);
|
||||||
|
gb->serial_mask = gb->cgb_mode && (value & 2)? 4 : 0x80;
|
||||||
if ((value & 0x80) && (value & 0x1) ) {
|
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) {
|
if (gb->serial_transfer_bit_start_callback) {
|
||||||
gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80);
|
gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
gb->serial_length = 0;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case GB_IO_RP: {
|
case GB_IO_RP: {
|
||||||
|
@ -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)
|
void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value)
|
||||||
{
|
{
|
||||||
/* TIMA increases when a specific high-bit becomes a low-bit. */
|
/* 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);
|
increase_tima(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (triggers & gb->serial_mask) {
|
||||||
|
GB_serial_master_edge(gb);
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: Can switching to double speed mode trigger an event? */
|
/* TODO: Can switching to double speed mode trigger an event? */
|
||||||
uint16_t apu_bit = gb->cgb_double_speed? 0x2000 : 0x1000;
|
uint16_t apu_bit = gb->cgb_double_speed? 0x2000 : 0x1000;
|
||||||
if (triggers & apu_bit) {
|
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)
|
void GB_set_rtc_mode(GB_gameboy_t *gb, GB_rtc_mode_t mode)
|
||||||
{
|
{
|
||||||
if (gb->rtc_mode != 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;
|
gb->dma_cycles = cycles;
|
||||||
|
|
||||||
timers_run(gb, 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)) {
|
if (unlikely(gb->speed_switch_halt_countdown)) {
|
||||||
gb->speed_switch_halt_countdown -= cycles;
|
gb->speed_switch_halt_countdown -= cycles;
|
||||||
|
@ -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 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_timing_sync(GB_gameboy_t *gb);
|
||||||
internal void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value);
|
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 {
|
enum {
|
||||||
GB_TIMA_RUNNING = 0,
|
GB_TIMA_RUNNING = 0,
|
||||||
GB_TIMA_RELOADING = 1,
|
GB_TIMA_RELOADING = 1,
|
||||||
|
Loading…
Reference in New Issue
Block a user