mirror of
https://github.com/libretro/sameduck.git
synced 2024-11-23 17:19:47 +00:00
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:
parent
0b03b61564
commit
c342663200
@ -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;
|
||||
|
27
Core/gb.c
27
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));
|
||||
|
17
Core/gb.h
17
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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user