From ab5f66795ab48b0fddb6ec71c3a0d7b66bbb3ee1 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 2 Oct 2016 17:14:58 +0300 Subject: [PATCH] Gameboy Camera API --- Core/camera.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ Core/camera.h | 15 +++++++++ Core/gb.h | 5 +++ Core/memory.c | 16 ++++++--- 4 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 Core/camera.c create mode 100644 Core/camera.h diff --git a/Core/camera.c b/Core/camera.c new file mode 100644 index 0000000..cfbf905 --- /dev/null +++ b/Core/camera.c @@ -0,0 +1,89 @@ +#include "camera.h" + +static int8_t dither_random(uint8_t x, uint8_t y) +{ + static bool once = false; + static int8_t random[128*112]; + if (!once) { + unsigned int r = 0x1337c0de; + for (int i = 0; i < sizeof(random); i++) { + random[i] = r % 85 - 42; + r += 11; + r *= 25214903917; + } + once = true; + } + + return random[x + y * 128]; +} + +uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr) +{ + uint8_t tile_x = addr / 0x10 % 0x10; + uint8_t tile_y = addr / 0x10 / 0x10; + + uint8_t y = ((addr >> 1) & 0x7) + tile_y * 8; + uint8_t bit = addr & 1; + + uint8_t ret = 0; + + for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) { + + int color = gb->camera_get_pixel_callback? ~gb->camera_get_pixel_callback(gb, x,y) : (rand() & 0xFF); + + /* Dither using a deterministic random */ + color += dither_random(x, y); + if (color > 255) { + color = 255; + } + else if (color < 0) { + color = 0; + } + + ret <<= 1; + ret |= (color >> (6 + bit)) & 1; + } + + return ret; +} + +void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback) +{ + gb->camera_get_pixel_callback = callback; +} + +void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback) +{ + gb->camera_update_request_callback = callback; +} + +void GB_camera_updated(GB_gameboy_t *gb) +{ + gb->camera_registers[0] = 0; +} + +void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value) +{ + addr &= 0x7F; + if (addr == 0) { + if ((value & 1) && gb->camera_update_request_callback) { + /* If no callback is set, ignore the write as if the camera is instantly done */ + gb->camera_update_request_callback(gb); + gb->camera_registers[0] = 1; + } + } + else { + if (addr >= 0x36) { + GB_log(gb, "Wrote invalid camera register %02x: %2x\n", addr, value); + } + /* Todo: find out what these registers do */ + gb->camera_registers[addr] = value; + } +} +uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr) +{ + if ((addr & 0x7F) == 0) { + return gb->camera_registers[0]; + } + return 0; +} \ No newline at end of file diff --git a/Core/camera.h b/Core/camera.h new file mode 100644 index 0000000..d0d7a9d --- /dev/null +++ b/Core/camera.h @@ -0,0 +1,15 @@ +#ifndef camera_h +#define camera_h +#include "gb.h" + +uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr); + +void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback); +void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback); + +void GB_camera_updated(GB_gameboy_t *gb); + +void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value); +uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr); + +#endif diff --git a/Core/gb.h b/Core/gb.h index 75066c0..0836dbb 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -159,6 +159,8 @@ typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_a 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, long cycles_since_last_update); +typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y); +typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb); typedef struct { enum { @@ -284,6 +286,7 @@ typedef struct GB_gameboy_s { }; uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */ bool camera_registers_mapped; + uint8_t camera_registers[0x36]; ); @@ -380,6 +383,8 @@ typedef struct GB_gameboy_s { GB_rgb_encode_callback_t rgb_encode_callback; GB_vblank_callback_t vblank_callback; GB_infrared_callback_t infrared_callback; + GB_camera_get_pixel_callback_t camera_get_pixel_callback; + GB_camera_update_request_callback_t camera_update_request_callback; /* IR */ long cycles_since_ir_change; diff --git a/Core/memory.c b/Core/memory.c index f003565..28d2a7d 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -7,6 +7,7 @@ #include "debugger.h" #include "mbc.h" #include "timing.h" +#include "camera.h" typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr); typedef void GB_write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value); @@ -83,13 +84,17 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) } if (gb->camera_registers_mapped) { - return 0; + return GB_camera_read_register(gb, addr); } if (!gb->mbc_ram) { return 0xFF; } + if (gb->cartridge_type->mbc_subtype == GB_CAMERA && gb->mbc_ram_bank == 0 && addr >= 0xa100 && addr < 0xaf00) { + return GB_camera_read_image(gb, addr - 0xa100); + } + uint8_t ret = gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)]; if (gb->cartridge_type->mbc_type == GB_MBC2) { ret |= 0xF0; @@ -329,6 +334,11 @@ static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) { + if (gb->camera_registers_mapped) { + GB_camera_write_register(gb, addr, value); + return; + } + if (!gb->mbc_ram_enable || !gb->mbc_ram_size) return; if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) { @@ -340,10 +350,6 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) return; } - if (gb->camera_registers_mapped) { - return; - } - gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)] = value; }