diff --git a/Core/gb.c b/Core/gb.c index e512f21..79848fb 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -474,6 +474,17 @@ void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback) void GB_set_infrared_input(GB_gameboy_t *gb, bool state) { gb->infrared_input = state; + gb->cycles_since_input_ir_change = 0; + gb->ir_queue_length = 0; +} + +void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change) +{ + if (gb->ir_queue_length == GB_MAX_IR_QUEUE) { + GB_log(gb, "IR Queue is full\n"); + return; + } + gb->ir_queue[gb->ir_queue_length++] = (GB_ir_queue_item_t){state, cycles_after_previous_change}; } void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate) diff --git a/Core/gb.h b/Core/gb.h index 563fef1..9de63cd 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -28,6 +28,8 @@ enum { GB_ZERO_FLAG = 128, }; +#define GB_MAX_IR_QUEUE 256 + enum { /* Joypad and Serial */ GB_IO_JOYP = 0x00, // Joypad (R/W) @@ -155,7 +157,7 @@ typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb); typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes); typedef char *(*GB_input_callback_t)(GB_gameboy_t *gb); typedef uint32_t (*GB_rgb_encode_callback_t)(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b); -typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on); +typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on, long cycles_since_last_update); typedef struct { enum { @@ -172,6 +174,11 @@ typedef struct { bool has_rumble; } GB_cartridge_t; +typedef struct { + bool state; + long delay; +} GB_ir_queue_item_t; + struct GB_breakpoint_s; struct GB_watchpoint_s; @@ -349,6 +356,12 @@ typedef struct GB_gameboy_s { GB_vblank_callback_t vblank_callback; GB_infrared_callback_t infrared_callback; + /* IR */ + long cycles_since_ir_change; + long cycles_since_input_ir_change; + GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE]; + size_t ir_queue_length; + /*** Debugger ***/ bool debug_stopped; bool debug_fin_command, debug_next_command; @@ -406,4 +419,5 @@ void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate); void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback); void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback); void GB_set_infrared_input(GB_gameboy_t *gb, bool state); +void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change); #endif /* GB_h */ diff --git a/Core/memory.c b/Core/memory.c index 490cd87..89f67b5 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -166,7 +166,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) bool read_value = gb->infrared_input || (gb->io_registers[GB_IO_RP] & 1); uint8_t ret = (gb->io_registers[GB_IO_RP] & 0xC1) | 0x3C; if ((gb->io_registers[GB_IO_RP] & 0xC0) == 0xC0 && read_value) { - ret |= read_value; + ret |= 2; } return ret; } @@ -455,7 +455,8 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) } if ((value & 1) != (gb->io_registers[GB_IO_RP] & 1)) { if (gb->infrared_callback) { - gb->infrared_callback(gb, value & 1); + gb->infrared_callback(gb, value & 1, gb->cycles_since_ir_change); + gb->cycles_since_ir_change = 0; } } gb->io_registers[GB_IO_RP] = value; diff --git a/Core/timing.c b/Core/timing.c index 9390c1c..beffa71 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -3,6 +3,17 @@ #include "memory.h" #include "display.h" +static void GB_ir_run(GB_gameboy_t *gb) +{ + if (gb->ir_queue_length == 0) return; + if (gb->cycles_since_input_ir_change >= gb->ir_queue[0].delay) { + gb->cycles_since_input_ir_change -= gb->ir_queue[0].delay; + gb->infrared_input = gb->ir_queue[0].state; + gb->ir_queue_length--; + memmove(&gb->ir_queue[0], &gb->ir_queue[1], sizeof(gb->ir_queue[0]) * (gb->ir_queue_length)); + } +} + void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) { // Affected by speed boost @@ -24,10 +35,13 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) gb->hdma_cycles += cycles; gb->display_cycles += cycles; gb->apu_cycles += cycles; + gb->cycles_since_ir_change += cycles; + gb->cycles_since_input_ir_change += cycles; GB_hdma_run(gb); GB_timers_run(gb); GB_apu_run(gb); GB_display_run(gb); + GB_ir_run(gb); } void GB_timers_run(GB_gameboy_t *gb)