From 1d9ac5ccc3f54a5d527e6f7dab554015107ad5d5 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 21 Nov 2020 00:52:54 +0200 Subject: [PATCH] More accurate IR emulation, simplify API --- Cocoa/Document.m | 2 +- Core/gb.c | 11 ----------- Core/gb.h | 19 ++++--------------- Core/memory.c | 39 +++++++++++++-------------------------- Core/timing.c | 35 +++++++++++++++++++++++++---------- 5 files changed, 43 insertions(+), 63 deletions(-) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index 84e181f..ea7ef49 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -184,7 +184,7 @@ static bool linkCableBitEnd(GB_gameboy_t *gb) return [self linkCableBitEnd]; } -static void infraredStateChanged(GB_gameboy_t *gb, bool on, uint64_t cycles_since_last_update) +static void infraredStateChanged(GB_gameboy_t *gb, bool on) { Document *self = (__bridge Document *)GB_get_user_data(gb); [self infraredStateChanged:on]; diff --git a/Core/gb.c b/Core/gb.c index 7325d79..57c3788 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1073,17 +1073,6 @@ 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, uint64_t 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_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback) diff --git a/Core/gb.h b/Core/gb.h index 0d0aaa9..7610fca 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -125,8 +125,6 @@ typedef enum { GB_BORDER_ALWAYS, } GB_border_mode_t; -#define GB_MAX_IR_QUEUE 256 - enum { /* Joypad and Serial */ GB_IO_JOYP = 0x00, // Joypad (R/W) @@ -272,7 +270,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, uint64_t cycles_since_last_update); +typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on); typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, double rumble_amplitude); typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send); typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb); @@ -283,11 +281,6 @@ typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb); typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb); typedef void (*GB_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type); -typedef struct { - bool state; - uint64_t delay; -} GB_ir_queue_item_t; - struct GB_breakpoint_s; struct GB_watchpoint_s; @@ -374,6 +367,9 @@ struct GB_gameboy_internal_s { uint8_t extra_oam[0xff00 - 0xfea0]; uint32_t ram_size; // Different between CGB and DMG GB_workboy_t workboy; + + int32_t ir_sensor; + bool effective_ir_input; ); /* DMA and HDMA */ @@ -613,12 +609,6 @@ struct GB_gameboy_internal_s { GB_print_image_callback_t printer_callback; GB_workboy_set_time_callback workboy_set_time_callback; GB_workboy_get_time_callback workboy_get_time_callback; - - /* IR */ - uint64_t cycles_since_ir_change; // In 8MHz units - uint64_t cycles_since_input_ir_change; // In 8MHz units - GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE]; - size_t ir_queue_length; /*** Debugger ***/ volatile bool debug_stopped, debug_disable; @@ -771,7 +761,6 @@ void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output); void GB_set_border_mode(GB_gameboy_t *gb, GB_border_mode_t border_mode); void GB_set_infrared_input(GB_gameboy_t *gb, bool state); -void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, uint64_t cycles_after_previous_change); /* In 8MHz units*/ void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback); void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback); diff --git a/Core/memory.c b/Core/memory.c index 0d33e6c..40af0e5 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -113,11 +113,6 @@ static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr) return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src); } -static bool effective_ir_input(GB_gameboy_t *gb) -{ - return gb->infrared_input || gb->cart_ir; -} - static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr) { if (addr < 0x100 && !gb->boot_rom_finished) { @@ -173,7 +168,7 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) case 0xD: // RTC status return 1; case 0xE: // IR mode - return effective_ir_input(gb); // TODO: What are the other bits? + return gb->effective_ir_input; // TODO: What are the other bits? default: GB_log(gb, "Unsupported HuC-3 mode %x read: %04x\n", gb->huc3_mode, addr); return 1; // TODO: What happens in this case? @@ -191,7 +186,7 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) } if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) { - return 0xc0 | effective_ir_input(gb); + return 0xc0 | gb->effective_ir_input; } if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3 && @@ -432,7 +427,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) if (gb->model != GB_MODEL_CGB_E) { ret |= 0x10; } - if (((gb->io_registers[GB_IO_RP] & 0xC1) == 0xC0 && effective_ir_input(gb)) && gb->model != GB_MODEL_AGB) { + if (((gb->io_registers[GB_IO_RP] & 0xC0) == 0xC0 && gb->effective_ir_input) && gb->model != GB_MODEL_AGB) { ret &= ~2; } return ret; @@ -655,14 +650,11 @@ static bool huc3_write(GB_gameboy_t *gb, uint8_t value) // Not sure what writes here mean, they're always 0xFE return true; case 0xE: { // IR mode - bool old_input = effective_ir_input(gb); - gb->cart_ir = value & 1; - bool new_input = effective_ir_input(gb); - if (new_input != old_input) { + if (gb->cart_ir != (value & 1)) { + gb->cart_ir = value & 1; if (gb->infrared_callback) { - gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change); + gb->infrared_callback(gb, value & 1); } - gb->cycles_since_ir_change = 0; } return true; } @@ -691,14 +683,11 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) && gb->cartridge_type->mbc_type != GB_HUC1) return; if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) { - bool old_input = effective_ir_input(gb); - gb->cart_ir = value & 1; - bool new_input = effective_ir_input(gb); - if (new_input != old_input) { + if (gb->cart_ir != (value & 1)) { + gb->cart_ir = value & 1; if (gb->infrared_callback) { - gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change); + gb->infrared_callback(gb, value & 1); } - gb->cycles_since_ir_change = 0; } return; } @@ -1111,15 +1100,13 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) if (!GB_is_cgb(gb)) { return; } - bool old_input = effective_ir_input(gb); - gb->io_registers[GB_IO_RP] = value; - bool new_input = effective_ir_input(gb); - if (new_input != old_input) { + if ((gb->io_registers[GB_IO_RP] ^ value) & 1) { if (gb->infrared_callback) { - gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change); + gb->infrared_callback(gb, value & 1); } - gb->cycles_since_ir_change = 0; } + gb->io_registers[GB_IO_RP] = value; + return; } diff --git a/Core/timing.c b/Core/timing.c index 965ba27..44ff8f7 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -89,15 +89,32 @@ void GB_timing_sync(GB_gameboy_t *gb) } #endif -static void GB_ir_run(GB_gameboy_t *gb) + +#define IR_DECAY 31500 +#define IR_THRESHOLD 19900 +#define IR_MAX IR_THRESHOLD * 2 + IR_DECAY + +static void GB_ir_run(GB_gameboy_t *gb, uint32_t cycles) { - 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)); + if (gb->model == GB_MODEL_AGB) return; + if (gb->infrared_input || gb->cart_ir || (gb->io_registers[GB_IO_RP] & 1)) { + gb->ir_sensor += cycles; + if (gb->ir_sensor > IR_MAX) { + gb->ir_sensor = IR_MAX; + } + + gb->effective_ir_input = gb->ir_sensor >= IR_THRESHOLD && gb->ir_sensor <= IR_THRESHOLD + IR_DECAY; } + else { + if (gb->ir_sensor <= cycles) { + gb->ir_sensor = 0; + } + else { + gb->ir_sensor -= cycles; + } + gb->effective_ir_input = false; + } + } static void advance_tima_state_machine(GB_gameboy_t *gb) @@ -234,8 +251,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) gb->double_speed_alignment += cycles; gb->hdma_cycles += cycles; gb->apu_output.sample_cycles += cycles; - gb->cycles_since_ir_change += cycles; - gb->cycles_since_input_ir_change += cycles; gb->cycles_since_last_sync += cycles; gb->cycles_since_run += cycles; @@ -252,7 +267,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) } GB_apu_run(gb); GB_display_run(gb, cycles); - GB_ir_run(gb); + GB_ir_run(gb, cycles); } /*