From c3426632003d7d0664ec0d861c7aa876db62fe8a Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 15 Mar 2019 14:36:10 +0200 Subject: [PATCH] Emulate serial bit shifting, update the serial API to use bits instead of bytes, update printer emulation and libretro to use the new API --- Cocoa/Document.m | 8 +++--- Core/gb.c | 27 +++++++++++-------- Core/gb.h | 17 ++++++------ Core/memory.c | 7 ++--- Core/printer.c | 37 ++++++++++++++++++-------- Core/printer.h | 4 +++ Core/timing.c | 63 ++++++++++++++++++++++++++++++++------------- libretro/libretro.c | 38 +++++++++++++-------------- 8 files changed, 127 insertions(+), 74 deletions(-) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index c94f77b..3d3ad7b 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -1376,12 +1376,12 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, currentPrinterImageData = [[NSMutableData alloc] init]; } [currentPrinterImageData appendBytes:paddedImage length:sizeof(paddedImage)]; - self.feedImageView.image = [Document imageFromData:currentPrinterImageData - width:160 - height:currentPrinterImageData.length / 160 / sizeof(imageBytes[0]) - scale:2.0]; /* UI related code must run on main thread. */ dispatch_async(dispatch_get_main_queue(), ^{ + self.feedImageView.image = [Document imageFromData:currentPrinterImageData + width:160 + height:currentPrinterImageData.length / 160 / sizeof(imageBytes[0]) + scale:2.0]; NSRect frame = self.printerFeedWindow.frame; frame.size = self.feedImageView.image.size; frame.size.height += self.printerFeedWindow.frame.size.height - self.printerFeedWindow.contentView.frame.size.height; diff --git a/Core/gb.c b/Core/gb.c index 0d957ee..e509445 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -477,40 +477,45 @@ void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback) gb->rumble_callback = callback; } -void GB_set_serial_transfer_start_callback(GB_gameboy_t *gb, GB_serial_transfer_start_callback_t callback) +void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback) { - gb->serial_transfer_start_callback = callback; + gb->serial_transfer_bit_start_callback = callback; } -void GB_set_serial_transfer_end_callback(GB_gameboy_t *gb, GB_serial_transfer_end_callback_t callback) +void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback) { - gb->serial_transfer_end_callback = callback; + gb->serial_transfer_bit_end_callback = callback; } -uint8_t GB_serial_get_data(GB_gameboy_t *gb) +bool GB_serial_get_data_bit(GB_gameboy_t *gb) { if (gb->io_registers[GB_IO_SC] & 1) { /* Internal Clock */ GB_log(gb, "Serial read request while using internal clock. \n"); return 0xFF; } - return gb->io_registers[GB_IO_SB]; + return gb->io_registers[GB_IO_SB] & 0x80; } -void GB_serial_set_data(GB_gameboy_t *gb, uint8_t data) +void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data) { if (gb->io_registers[GB_IO_SC] & 1) { /* Internal Clock */ GB_log(gb, "Serial write request while using internal clock. \n"); return; } - gb->io_registers[GB_IO_SB] = data; - gb->io_registers[GB_IO_IF] |= 8; + gb->io_registers[GB_IO_SB] <<= 1; + gb->io_registers[GB_IO_SB] |= data; + gb->serial_count++; + if (gb->serial_count == 8) { + gb->io_registers[GB_IO_IF] |= 8; + gb->serial_count = 0; + } } void GB_disconnect_serial(GB_gameboy_t *gb) { - gb->serial_transfer_start_callback = NULL; - gb->serial_transfer_end_callback = NULL; + gb->serial_transfer_bit_start_callback = NULL; + gb->serial_transfer_bit_end_callback = NULL; /* Reset any internally-emulated device. Currently, only the printer. */ memset(&gb->printer, 0, sizeof(gb->printer)); diff --git a/Core/gb.h b/Core/gb.h index 66df9c9..f2c4199 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -227,8 +227,8 @@ 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 void (*GB_rumble_callback_t)(GB_gameboy_t *gb, bool rumble_on); -typedef void (*GB_serial_transfer_start_callback_t)(GB_gameboy_t *gb, uint8_t byte_to_send); -typedef uint8_t (*GB_serial_transfer_end_callback_t)(GB_gameboy_t *gb); +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); typedef struct { bool state; @@ -404,6 +404,7 @@ struct GB_gameboy_internal_s { uint16_t serial_cycles; uint16_t serial_length; uint8_t double_speed_alignment; + uint8_t serial_count; ); /* APU */ @@ -514,8 +515,8 @@ struct GB_gameboy_internal_s { GB_camera_get_pixel_callback_t camera_get_pixel_callback; GB_camera_update_request_callback_t camera_update_request_callback; GB_rumble_callback_t rumble_callback; - GB_serial_transfer_start_callback_t serial_transfer_start_callback; - GB_serial_transfer_end_callback_t serial_transfer_end_callback; + GB_serial_transfer_bit_start_callback_t serial_transfer_bit_start_callback; + GB_serial_transfer_bit_end_callback_t serial_transfer_bit_end_callback; /* IR */ long cycles_since_ir_change; // In 8MHz units @@ -662,12 +663,12 @@ void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback) void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback); /* These APIs are used when using internal clock */ -void GB_set_serial_transfer_start_callback(GB_gameboy_t *gb, GB_serial_transfer_start_callback_t callback); -void GB_set_serial_transfer_end_callback(GB_gameboy_t *gb, GB_serial_transfer_end_callback_t callback); +void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback); +void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback); /* These APIs are used when using external clock */ -uint8_t GB_serial_get_data(GB_gameboy_t *gb); -void GB_serial_set_data(GB_gameboy_t *gb, uint8_t data); +bool GB_serial_get_data_bit(GB_gameboy_t *gb); +void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data); void GB_disconnect_serial(GB_gameboy_t *gb); diff --git a/Core/memory.c b/Core/memory.c index 980e92c..c15ae42 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -872,11 +872,12 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) } gb->io_registers[GB_IO_SC] = value | (~0x83); if ((value & 0x80) && (value & 0x1) ) { - gb->serial_length = gb->cgb_mode && (value & 2)? 128 : 4096; + 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_start_callback) { - gb->serial_transfer_start_callback(gb, gb->io_registers[GB_IO_SB]); + if (gb->serial_transfer_bit_start_callback) { + gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80); } } else { diff --git a/Core/printer.c b/Core/printer.c index bc1d45a..add1f86 100644 --- a/Core/printer.c +++ b/Core/printer.c @@ -70,7 +70,8 @@ static void handle_command(GB_gameboy_t *gb) } } -static void serial_start(GB_gameboy_t *gb, uint8_t byte_received) + +static void byte_reieve_completed(GB_gameboy_t *gb, uint8_t byte_received) { gb->printer.byte_to_send = 0; switch (gb->printer.command_state) { @@ -147,12 +148,10 @@ static void serial_start(GB_gameboy_t *gb, uint8_t byte_received) gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1; return; } + gb->printer.byte_to_send = 0x81; + break; case GB_PRINTER_COMMAND_ACTIVE: - gb->printer.byte_to_send = 0x81; - break; - case GB_PRINTER_COMMAND_STATUS: - if ((gb->printer.command_id & 0xF) == GB_PRINTER_INIT_COMMAND) { /* Games expect INIT commands to return 0? */ gb->printer.byte_to_send = 0; @@ -160,6 +159,8 @@ static void serial_start(GB_gameboy_t *gb, uint8_t byte_received) else { gb->printer.byte_to_send = gb->printer.status; } + break; + case GB_PRINTER_COMMAND_STATUS: /* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */ if (gb->printer.status == 6) { @@ -184,18 +185,32 @@ static void serial_start(GB_gameboy_t *gb, uint8_t byte_received) gb->printer.command_state++; } } - } -static uint8_t serial_end(GB_gameboy_t *gb) +static void serial_start(GB_gameboy_t *gb, bool bit_received) { - return gb->printer.byte_to_send; + gb->printer.byte_being_recieved <<= 1; + gb->printer.byte_being_recieved |= bit_received; + gb->printer.bits_recieved++; + if (gb->printer.bits_recieved == 8) { + byte_reieve_completed(gb, gb->printer.byte_being_recieved); + gb->printer.bits_recieved = 0; + gb->printer.byte_being_recieved = 0; + } +} + +static bool serial_end(GB_gameboy_t *gb) +{ + bool ret = gb->printer.bit_to_send; + gb->printer.bit_to_send = gb->printer.byte_to_send & 0x80; + gb->printer.byte_to_send <<= 1; + return ret; } void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback) { memset(&gb->printer, 0, sizeof(gb->printer)); - GB_set_serial_transfer_start_callback(gb, serial_start); - GB_set_serial_transfer_end_callback(gb, serial_end); + GB_set_serial_transfer_bit_start_callback(gb, serial_start); + GB_set_serial_transfer_bit_end_callback(gb, serial_end); gb->printer.callback = callback; -} \ No newline at end of file +} diff --git a/Core/printer.h b/Core/printer.h index e5d9036..7cf179e 100644 --- a/Core/printer.h +++ b/Core/printer.h @@ -52,6 +52,10 @@ typedef struct uint8_t compression_run_lenth; bool compression_run_is_compressed; + + uint8_t bits_recieved; + uint8_t byte_being_recieved; + bool bit_to_send; } GB_printer_t; diff --git a/Core/timing.c b/Core/timing.c index 06c603a..8affd86 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -161,30 +161,57 @@ main: } } +static void advance_serial(GB_gameboy_t *gb, uint8_t cycles) +{ + if (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_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) { // Affected by speed boost gb->dma_cycles += cycles; GB_timers_run(gb, cycles); - - uint16_t previous_serial_cycles = gb->serial_cycles; - gb->serial_cycles += cycles; - if (gb->serial_length) { - if ((gb->serial_cycles & gb->serial_length) != (previous_serial_cycles & gb->serial_length)) { - gb->serial_length = 0; - gb->io_registers[GB_IO_SC] &= ~0x80; - /* TODO: Does SB "update" bit by bit? */ - if (gb->serial_transfer_end_callback) { - gb->io_registers[GB_IO_SB] = gb->serial_transfer_end_callback(gb); - } - else { - gb->io_registers[GB_IO_SB] = 0xFF; - } - - gb->io_registers[GB_IO_IF] |= 8; - } - } + advance_serial(gb, cycles); gb->debugger_ticks += cycles; diff --git a/libretro/libretro.c b/libretro/libretro.c index 3dc33a5..9bf894a 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -180,29 +180,29 @@ static void vblank2(GB_gameboy_t *gb) audio_callback(gb); } -static uint8_t byte_to_send1 = 0xFF, byte_to_send2 = 0xFF; +static bool bit_to_send1 = true, bit_to_send2 = true; -static void serial_start1(GB_gameboy_t *gb, uint8_t byte_received) +static void serial_start1(GB_gameboy_t *gb, bool bit_received) { - byte_to_send1 = byte_received; + bit_to_send1 = bit_received; } -static uint8_t serial_end1(GB_gameboy_t *gb) +static bool serial_end1(GB_gameboy_t *gb) { - uint8_t ret = GB_serial_get_data(&gameboy[1]); - GB_serial_set_data(&gameboy[1], byte_to_send1); + bool ret = GB_serial_get_data_bit(&gameboy[1]); + GB_serial_set_data_bit(&gameboy[1], bit_to_send1); return ret; } -static void serial_start2(GB_gameboy_t *gb, uint8_t byte_received) +static void serial_start2(GB_gameboy_t *gb, bool bit_received) { - byte_to_send2 = byte_received; + bit_to_send2 = bit_received; } -static uint8_t serial_end2(GB_gameboy_t *gb) +static bool serial_end2(GB_gameboy_t *gb) { - uint8_t ret = GB_serial_get_data(&gameboy[0]); - GB_serial_set_data(&gameboy[0], byte_to_send2); + bool ret = GB_serial_get_data_bit(&gameboy[0]); + GB_serial_set_data_bit(&gameboy[0], bit_to_send2); return ret; } @@ -337,17 +337,17 @@ static void set_link_cable_state(bool state) { if (state && emulated_devices == 2) { - GB_set_serial_transfer_start_callback(&gameboy[0], serial_start1); - GB_set_serial_transfer_end_callback(&gameboy[0], serial_end1); - GB_set_serial_transfer_start_callback(&gameboy[1], serial_start2); - GB_set_serial_transfer_end_callback(&gameboy[1], serial_end2); + GB_set_serial_transfer_bit_start_callback(&gameboy[0], serial_start1); + GB_set_serial_transfer_bit_end_callback(&gameboy[0], serial_end1); + GB_set_serial_transfer_bit_start_callback(&gameboy[1], serial_start2); + GB_set_serial_transfer_bit_end_callback(&gameboy[1], serial_end2); } else if (!state) { - GB_set_serial_transfer_start_callback(&gameboy[0], NULL); - GB_set_serial_transfer_end_callback(&gameboy[0], NULL); - GB_set_serial_transfer_start_callback(&gameboy[1], NULL); - GB_set_serial_transfer_end_callback(&gameboy[1], NULL); + GB_set_serial_transfer_bit_start_callback(&gameboy[0], NULL); + GB_set_serial_transfer_bit_end_callback(&gameboy[0], NULL); + GB_set_serial_transfer_bit_start_callback(&gameboy[1], NULL); + GB_set_serial_transfer_bit_end_callback(&gameboy[1], NULL); } }