From bb8f973fb4002864979931c019af258fa3178e8c Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Fri, 22 Jul 2022 20:15:00 -0700 Subject: [PATCH 1/2] Implement GB Camera timing (when no webcam is connected), misc camera accuracy improvements, use GB_random instead of rand for noise_seed --- Core/camera.c | 35 +++++++++++++++++++++++++---------- Core/gb.h | 2 ++ Core/memory.c | 18 +++++++++++++++--- Core/sm83_cpu.c | 3 +++ Core/timing.c | 24 ++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/Core/camera.c b/Core/camera.c index 7751f18..22ecf5d 100644 --- a/Core/camera.c +++ b/Core/camera.c @@ -3,7 +3,7 @@ static uint32_t noise_seed = 0; /* This is not a complete emulation of the camera chip. Only the features used by the Game Boy Camera ROMs are supported. - We also do not emulate the timing of the real cart, as it might be actually faster than the webcam. */ + We also do not emulate the timing of the real cart when a webcam is used, as it might be actually faster than the webcam. */ static uint8_t generate_noise(uint8_t x, uint8_t y) { @@ -55,10 +55,6 @@ static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y) uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr) { - if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) { - /* Forbid reading the image while the camera is busy. */ - return 0xFF; - } uint8_t tile_x = addr / 0x10 % 0x10; uint8_t tile_y = addr / 0x10 / 0x10; @@ -112,6 +108,12 @@ void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_call void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback) { + if (gb->camera_countdown > 0 && callback) { + GB_log(gb, "Camera update request callback set while camera was proccessing, clearing camera countdown.\n"); + gb->camera_countdown = 0; + GB_camera_updated(gb); + } + gb->camera_update_request_callback = callback; } @@ -125,12 +127,25 @@ void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value) addr &= 0x7F; if (addr == GB_CAMERA_SHOOT_AND_1D_FLAGS) { value &= 0x7; - noise_seed = rand(); - if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && gb->camera_update_request_callback) { - /* If no callback is set, ignore the write as if the camera is instantly done */ - gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] |= 1; - gb->camera_update_request_callback(gb); + noise_seed = GB_random(); + if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1)) { + if (gb->camera_update_request_callback) { + gb->camera_update_request_callback(gb); + } + else { + /* If no callback is set, wait the amount of time the real camera would take before clearing the busy bit */ + uint16_t exposure = (gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) | gb->camera_registers[GB_CAMERA_EXPOSURE_LOW]; + gb->camera_countdown = 129792 + ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x80)? 0 : 2048) + (exposure * 64) + (gb->camera_alignment & 4); + } } + + if (!(value & 1) && (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1)) { + /* We don't support cancelling a camera shoot */ + GB_log(gb, "ROM attempted to cancel camera shoot, which is currently not supported. The camera shoot will not be cancelled.\n"); + value |= 1; + } + + gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] = value; } else { if (addr >= 0x36) { diff --git a/Core/gb.h b/Core/gb.h index 1621149..6c84eef 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -529,6 +529,8 @@ struct GB_gameboy_internal_s { }; bool camera_registers_mapped; uint8_t camera_registers[0x36]; + uint8_t camera_alignment; + int32_t camera_countdown; uint8_t rumble_strength; bool cart_ir; ) diff --git a/Core/memory.c b/Core/memory.c index 18f8cdf..cc47287 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -414,8 +414,15 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) return 0xFF; } - if (gb->cartridge_type->mbc_type == GB_CAMERA && gb->mbc_ram_bank == 0 && addr >= 0xA100 && addr < 0xAF00) { - return GB_camera_read_image(gb, addr - 0xA100); + if (gb->cartridge_type->mbc_type == GB_CAMERA) { + /* Forbid reading RAM while the camera is busy. */ + if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) { + return 0; + } + + if (gb->mbc_ram_bank == 0 && addr >= 0xA100 && addr < 0xAF00) { + return GB_camera_read_image(gb, addr - 0xA100); + } } uint8_t effective_bank = gb->mbc_ram_bank; @@ -1220,7 +1227,12 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) if (!gb->mbc_ram || !gb->mbc_ram_size) { return; } - + + if (gb->cartridge_type->mbc_type == GB_CAMERA && (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1)) { + /* Forbid writing to RAM while the camera is busy. */ + return; + } + uint8_t effective_bank = gb->mbc_ram_bank; if (gb->cartridge_type->mbc_type == GB_MBC3 && !gb->is_mbc30) { if (gb->cartridge_type->has_rtc) { diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index 7fad1ab..a56fbde 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -416,6 +416,9 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode) if (gb->apu.global_enable && gb->cgb_double_speed) { GB_log(gb, "ROM triggered an APU odd mode, which is currently not tested.\n"); } + if (gb->cartridge_type->mbc_type == GB_CAMERA && (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && !gb->cgb_double_speed) { + GB_log(gb, "ROM entered double speed mode with a camera cartridge, this could damage a real cartridge's camera.\n"); + } if (gb->cgb_double_speed) { gb->cgb_double_speed = false; diff --git a/Core/timing.c b/Core/timing.c index 41c6752..91191cf 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -365,6 +365,29 @@ static void rtc_run(GB_gameboy_t *gb, uint8_t cycles) } } +static void camera_run(GB_gameboy_t *gb, uint8_t cycles) +{ + /* Do we have a camera? */ + if (likely(gb->cartridge_type->mbc_type != GB_CAMERA)) return; + + /* The camera mapper uses the PHI pin to clock itself */ + + /* PHI does not run in halt nor stop mode */ + if (unlikely(gb->halted || gb->stopped)) return; + + /* Only every other PHI is used (as the camera wants a 512KiHz clock) */ + gb->camera_alignment += cycles; + + /* Is the camera processing an image? */ + if (likely(gb->camera_countdown == 0)) return; + + gb->camera_countdown -= cycles; + if (gb->camera_countdown <= 0) { + gb->camera_countdown = 0; + GB_camera_updated(gb); + } +} + void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) { @@ -389,6 +412,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) gb->dma_cycles = cycles; timers_run(gb, cycles); + camera_run(gb, cycles); if (unlikely(gb->speed_switch_halt_countdown)) { gb->speed_switch_halt_countdown -= cycles; From 93c611e9bc383a5b56d7d9d8ef0b7f55fa434407 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 23 Jul 2022 13:12:48 +0300 Subject: [PATCH 2/2] Values must be added to the end of a GB_SECTION for save state compatibility --- Core/gb.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/gb.h b/Core/gb.h index 6c84eef..49fe671 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -529,10 +529,10 @@ struct GB_gameboy_internal_s { }; bool camera_registers_mapped; uint8_t camera_registers[0x36]; - uint8_t camera_alignment; - int32_t camera_countdown; uint8_t rumble_strength; bool cart_ir; + uint8_t camera_alignment; + int32_t camera_countdown; ) /* HRAM and HW Registers */