Emulate serial bit shifting, update the serial API to use bits instead of bytes, update printer emulation and libretro to use the new API

This commit is contained in:
Lior Halphon 2019-03-15 14:36:10 +02:00
parent 0b03b61564
commit c342663200
8 changed files with 127 additions and 74 deletions

View File

@ -1376,12 +1376,12 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
currentPrinterImageData = [[NSMutableData alloc] init]; currentPrinterImageData = [[NSMutableData alloc] init];
} }
[currentPrinterImageData appendBytes:paddedImage length:sizeof(paddedImage)]; [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. */ /* UI related code must run on main thread. */
dispatch_async(dispatch_get_main_queue(), ^{ 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; NSRect frame = self.printerFeedWindow.frame;
frame.size = self.feedImageView.image.size; frame.size = self.feedImageView.image.size;
frame.size.height += self.printerFeedWindow.frame.size.height - self.printerFeedWindow.contentView.frame.size.height; frame.size.height += self.printerFeedWindow.frame.size.height - self.printerFeedWindow.contentView.frame.size.height;

View File

@ -477,40 +477,45 @@ void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback)
gb->rumble_callback = 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) { if (gb->io_registers[GB_IO_SC] & 1) {
/* Internal Clock */ /* Internal Clock */
GB_log(gb, "Serial read request while using internal clock. \n"); GB_log(gb, "Serial read request while using internal clock. \n");
return 0xFF; 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) { if (gb->io_registers[GB_IO_SC] & 1) {
/* Internal Clock */ /* Internal Clock */
GB_log(gb, "Serial write request while using internal clock. \n"); GB_log(gb, "Serial write request while using internal clock. \n");
return; return;
} }
gb->io_registers[GB_IO_SB] = data; gb->io_registers[GB_IO_SB] <<= 1;
gb->io_registers[GB_IO_IF] |= 8; 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) void GB_disconnect_serial(GB_gameboy_t *gb)
{ {
gb->serial_transfer_start_callback = NULL; gb->serial_transfer_bit_start_callback = NULL;
gb->serial_transfer_end_callback = NULL; gb->serial_transfer_bit_end_callback = NULL;
/* Reset any internally-emulated device. Currently, only the printer. */ /* Reset any internally-emulated device. Currently, only the printer. */
memset(&gb->printer, 0, sizeof(gb->printer)); memset(&gb->printer, 0, sizeof(gb->printer));

View File

@ -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 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_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_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 void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send);
typedef uint8_t (*GB_serial_transfer_end_callback_t)(GB_gameboy_t *gb); typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb);
typedef struct { typedef struct {
bool state; bool state;
@ -404,6 +404,7 @@ struct GB_gameboy_internal_s {
uint16_t serial_cycles; uint16_t serial_cycles;
uint16_t serial_length; uint16_t serial_length;
uint8_t double_speed_alignment; uint8_t double_speed_alignment;
uint8_t serial_count;
); );
/* APU */ /* APU */
@ -514,8 +515,8 @@ struct GB_gameboy_internal_s {
GB_camera_get_pixel_callback_t camera_get_pixel_callback; GB_camera_get_pixel_callback_t camera_get_pixel_callback;
GB_camera_update_request_callback_t camera_update_request_callback; GB_camera_update_request_callback_t camera_update_request_callback;
GB_rumble_callback_t rumble_callback; GB_rumble_callback_t rumble_callback;
GB_serial_transfer_start_callback_t serial_transfer_start_callback; GB_serial_transfer_bit_start_callback_t serial_transfer_bit_start_callback;
GB_serial_transfer_end_callback_t serial_transfer_end_callback; GB_serial_transfer_bit_end_callback_t serial_transfer_bit_end_callback;
/* IR */ /* IR */
long cycles_since_ir_change; // In 8MHz units 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); void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
/* These APIs are used when using internal clock */ /* 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_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_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_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback);
/* These APIs are used when using external clock */ /* These APIs are used when using external clock */
uint8_t GB_serial_get_data(GB_gameboy_t *gb); bool GB_serial_get_data_bit(GB_gameboy_t *gb);
void GB_serial_set_data(GB_gameboy_t *gb, uint8_t data); void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data);
void GB_disconnect_serial(GB_gameboy_t *gb); void GB_disconnect_serial(GB_gameboy_t *gb);

View File

@ -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); gb->io_registers[GB_IO_SC] = value | (~0x83);
if ((value & 0x80) && (value & 0x1) ) { 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. */ /* Todo: This is probably incorrect for CGB's faster clock mode. */
gb->serial_cycles &= 0xFF; gb->serial_cycles &= 0xFF;
if (gb->serial_transfer_start_callback) { if (gb->serial_transfer_bit_start_callback) {
gb->serial_transfer_start_callback(gb, gb->io_registers[GB_IO_SB]); gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80);
} }
} }
else { else {

View File

@ -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; gb->printer.byte_to_send = 0;
switch (gb->printer.command_state) { 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; gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
return; return;
} }
gb->printer.byte_to_send = 0x81;
break; break;
case GB_PRINTER_COMMAND_ACTIVE: 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) { if ((gb->printer.command_id & 0xF) == GB_PRINTER_INIT_COMMAND) {
/* Games expect INIT commands to return 0? */ /* Games expect INIT commands to return 0? */
gb->printer.byte_to_send = 0; gb->printer.byte_to_send = 0;
@ -160,6 +159,8 @@ static void serial_start(GB_gameboy_t *gb, uint8_t byte_received)
else { else {
gb->printer.byte_to_send = gb->printer.status; 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 */ /* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */
if (gb->printer.status == 6) { 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++; 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) void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback)
{ {
memset(&gb->printer, 0, sizeof(gb->printer)); memset(&gb->printer, 0, sizeof(gb->printer));
GB_set_serial_transfer_start_callback(gb, serial_start); GB_set_serial_transfer_bit_start_callback(gb, serial_start);
GB_set_serial_transfer_end_callback(gb, serial_end); GB_set_serial_transfer_bit_end_callback(gb, serial_end);
gb->printer.callback = callback; gb->printer.callback = callback;
} }

View File

@ -52,6 +52,10 @@ typedef struct
uint8_t compression_run_lenth; uint8_t compression_run_lenth;
bool compression_run_is_compressed; bool compression_run_is_compressed;
uint8_t bits_recieved;
uint8_t byte_being_recieved;
bool bit_to_send;
} GB_printer_t; } GB_printer_t;

View File

@ -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) void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
{ {
// Affected by speed boost // Affected by speed boost
gb->dma_cycles += cycles; gb->dma_cycles += cycles;
GB_timers_run(gb, cycles); GB_timers_run(gb, cycles);
advance_serial(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;
}
}
gb->debugger_ticks += cycles; gb->debugger_ticks += cycles;

View File

@ -180,29 +180,29 @@ static void vblank2(GB_gameboy_t *gb)
audio_callback(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]); bool ret = GB_serial_get_data_bit(&gameboy[1]);
GB_serial_set_data(&gameboy[1], byte_to_send1); GB_serial_set_data_bit(&gameboy[1], bit_to_send1);
return ret; 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]); bool ret = GB_serial_get_data_bit(&gameboy[0]);
GB_serial_set_data(&gameboy[0], byte_to_send2); GB_serial_set_data_bit(&gameboy[0], bit_to_send2);
return ret; return ret;
} }
@ -337,17 +337,17 @@ static void set_link_cable_state(bool state)
{ {
if (state && emulated_devices == 2) if (state && emulated_devices == 2)
{ {
GB_set_serial_transfer_start_callback(&gameboy[0], serial_start1); GB_set_serial_transfer_bit_start_callback(&gameboy[0], serial_start1);
GB_set_serial_transfer_end_callback(&gameboy[0], serial_end1); GB_set_serial_transfer_bit_end_callback(&gameboy[0], serial_end1);
GB_set_serial_transfer_start_callback(&gameboy[1], serial_start2); GB_set_serial_transfer_bit_start_callback(&gameboy[1], serial_start2);
GB_set_serial_transfer_end_callback(&gameboy[1], serial_end2); GB_set_serial_transfer_bit_end_callback(&gameboy[1], serial_end2);
} }
else if (!state) else if (!state)
{ {
GB_set_serial_transfer_start_callback(&gameboy[0], NULL); GB_set_serial_transfer_bit_start_callback(&gameboy[0], NULL);
GB_set_serial_transfer_end_callback(&gameboy[0], NULL); GB_set_serial_transfer_bit_end_callback(&gameboy[0], NULL);
GB_set_serial_transfer_start_callback(&gameboy[1], NULL); GB_set_serial_transfer_bit_start_callback(&gameboy[1], NULL);
GB_set_serial_transfer_end_callback(&gameboy[1], NULL); GB_set_serial_transfer_bit_end_callback(&gameboy[1], NULL);
} }
} }