More accurate IR emulation, simplify API

This commit is contained in:
Lior Halphon 2020-11-21 00:52:54 +02:00
parent b7f3454763
commit 1d9ac5ccc3
5 changed files with 43 additions and 63 deletions

View File

@ -184,7 +184,7 @@ static bool linkCableBitEnd(GB_gameboy_t *gb)
return [self linkCableBitEnd]; return [self linkCableBitEnd];
} }
static void infraredStateChanged(GB_gameboy_t *gb, bool on, uint64_t cycles_since_last_update) static void infraredStateChanged(GB_gameboy_t *gb, bool on)
{ {
Document *self = (__bridge Document *)GB_get_user_data(gb); Document *self = (__bridge Document *)GB_get_user_data(gb);
[self infraredStateChanged:on]; [self infraredStateChanged:on];

View File

@ -1073,17 +1073,6 @@ void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback)
void GB_set_infrared_input(GB_gameboy_t *gb, bool state) void GB_set_infrared_input(GB_gameboy_t *gb, bool state)
{ {
gb->infrared_input = state; gb->infrared_input = state;
gb->cycles_since_input_ir_change = 0;
gb->ir_queue_length = 0;
}
void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, uint64_t cycles_after_previous_change)
{
if (gb->ir_queue_length == GB_MAX_IR_QUEUE) {
GB_log(gb, "IR Queue is full\n");
return;
}
gb->ir_queue[gb->ir_queue_length++] = (GB_ir_queue_item_t){state, cycles_after_previous_change};
} }
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)

View File

@ -125,8 +125,6 @@ typedef enum {
GB_BORDER_ALWAYS, GB_BORDER_ALWAYS,
} GB_border_mode_t; } GB_border_mode_t;
#define GB_MAX_IR_QUEUE 256
enum { enum {
/* Joypad and Serial */ /* Joypad and Serial */
GB_IO_JOYP = 0x00, // Joypad (R/W) GB_IO_JOYP = 0x00, // Joypad (R/W)
@ -272,7 +270,7 @@ typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes); typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes);
typedef char *(*GB_input_callback_t)(GB_gameboy_t *gb); 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, uint64_t cycles_since_last_update); typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on);
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, double rumble_amplitude); typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, double rumble_amplitude);
typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send); 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 bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb);
@ -283,11 +281,6 @@ typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb); typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type); typedef void (*GB_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type);
typedef struct {
bool state;
uint64_t delay;
} GB_ir_queue_item_t;
struct GB_breakpoint_s; struct GB_breakpoint_s;
struct GB_watchpoint_s; struct GB_watchpoint_s;
@ -374,6 +367,9 @@ struct GB_gameboy_internal_s {
uint8_t extra_oam[0xff00 - 0xfea0]; uint8_t extra_oam[0xff00 - 0xfea0];
uint32_t ram_size; // Different between CGB and DMG uint32_t ram_size; // Different between CGB and DMG
GB_workboy_t workboy; GB_workboy_t workboy;
int32_t ir_sensor;
bool effective_ir_input;
); );
/* DMA and HDMA */ /* DMA and HDMA */
@ -614,12 +610,6 @@ struct GB_gameboy_internal_s {
GB_workboy_set_time_callback workboy_set_time_callback; GB_workboy_set_time_callback workboy_set_time_callback;
GB_workboy_get_time_callback workboy_get_time_callback; GB_workboy_get_time_callback workboy_get_time_callback;
/* IR */
uint64_t cycles_since_ir_change; // In 8MHz units
uint64_t cycles_since_input_ir_change; // In 8MHz units
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
size_t ir_queue_length;
/*** Debugger ***/ /*** Debugger ***/
volatile bool debug_stopped, debug_disable; volatile bool debug_stopped, debug_disable;
bool debug_fin_command, debug_next_command; bool debug_fin_command, debug_next_command;
@ -771,7 +761,6 @@ void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output);
void GB_set_border_mode(GB_gameboy_t *gb, GB_border_mode_t border_mode); void GB_set_border_mode(GB_gameboy_t *gb, GB_border_mode_t border_mode);
void GB_set_infrared_input(GB_gameboy_t *gb, bool state); void GB_set_infrared_input(GB_gameboy_t *gb, bool state);
void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, uint64_t cycles_after_previous_change); /* In 8MHz units*/
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback); void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback);
void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback); void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback);

View File

@ -113,11 +113,6 @@ static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr)
return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src); return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src);
} }
static bool effective_ir_input(GB_gameboy_t *gb)
{
return gb->infrared_input || gb->cart_ir;
}
static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr) static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr)
{ {
if (addr < 0x100 && !gb->boot_rom_finished) { if (addr < 0x100 && !gb->boot_rom_finished) {
@ -173,7 +168,7 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
case 0xD: // RTC status case 0xD: // RTC status
return 1; return 1;
case 0xE: // IR mode case 0xE: // IR mode
return effective_ir_input(gb); // TODO: What are the other bits? return gb->effective_ir_input; // TODO: What are the other bits?
default: default:
GB_log(gb, "Unsupported HuC-3 mode %x read: %04x\n", gb->huc3_mode, addr); GB_log(gb, "Unsupported HuC-3 mode %x read: %04x\n", gb->huc3_mode, addr);
return 1; // TODO: What happens in this case? return 1; // TODO: What happens in this case?
@ -191,7 +186,7 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
} }
if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) { if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) {
return 0xc0 | effective_ir_input(gb); return 0xc0 | gb->effective_ir_input;
} }
if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3 && if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3 &&
@ -432,7 +427,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
if (gb->model != GB_MODEL_CGB_E) { if (gb->model != GB_MODEL_CGB_E) {
ret |= 0x10; ret |= 0x10;
} }
if (((gb->io_registers[GB_IO_RP] & 0xC1) == 0xC0 && effective_ir_input(gb)) && gb->model != GB_MODEL_AGB) { if (((gb->io_registers[GB_IO_RP] & 0xC0) == 0xC0 && gb->effective_ir_input) && gb->model != GB_MODEL_AGB) {
ret &= ~2; ret &= ~2;
} }
return ret; return ret;
@ -655,14 +650,11 @@ static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
// Not sure what writes here mean, they're always 0xFE // Not sure what writes here mean, they're always 0xFE
return true; return true;
case 0xE: { // IR mode case 0xE: { // IR mode
bool old_input = effective_ir_input(gb); if (gb->cart_ir != (value & 1)) {
gb->cart_ir = value & 1; gb->cart_ir = value & 1;
bool new_input = effective_ir_input(gb);
if (new_input != old_input) {
if (gb->infrared_callback) { if (gb->infrared_callback) {
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change); gb->infrared_callback(gb, value & 1);
} }
gb->cycles_since_ir_change = 0;
} }
return true; return true;
} }
@ -691,14 +683,11 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
&& gb->cartridge_type->mbc_type != GB_HUC1) return; && gb->cartridge_type->mbc_type != GB_HUC1) return;
if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) { if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) {
bool old_input = effective_ir_input(gb); if (gb->cart_ir != (value & 1)) {
gb->cart_ir = value & 1; gb->cart_ir = value & 1;
bool new_input = effective_ir_input(gb);
if (new_input != old_input) {
if (gb->infrared_callback) { if (gb->infrared_callback) {
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change); gb->infrared_callback(gb, value & 1);
} }
gb->cycles_since_ir_change = 0;
} }
return; return;
} }
@ -1111,15 +1100,13 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
if (!GB_is_cgb(gb)) { if (!GB_is_cgb(gb)) {
return; return;
} }
bool old_input = effective_ir_input(gb); if ((gb->io_registers[GB_IO_RP] ^ value) & 1) {
gb->io_registers[GB_IO_RP] = value;
bool new_input = effective_ir_input(gb);
if (new_input != old_input) {
if (gb->infrared_callback) { if (gb->infrared_callback) {
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change); gb->infrared_callback(gb, value & 1);
} }
gb->cycles_since_ir_change = 0;
} }
gb->io_registers[GB_IO_RP] = value;
return; return;
} }

View File

@ -89,15 +89,32 @@ void GB_timing_sync(GB_gameboy_t *gb)
} }
#endif #endif
static void GB_ir_run(GB_gameboy_t *gb)
#define IR_DECAY 31500
#define IR_THRESHOLD 19900
#define IR_MAX IR_THRESHOLD * 2 + IR_DECAY
static void GB_ir_run(GB_gameboy_t *gb, uint32_t cycles)
{ {
if (gb->ir_queue_length == 0) return; if (gb->model == GB_MODEL_AGB) return;
if (gb->cycles_since_input_ir_change >= gb->ir_queue[0].delay) { if (gb->infrared_input || gb->cart_ir || (gb->io_registers[GB_IO_RP] & 1)) {
gb->cycles_since_input_ir_change -= gb->ir_queue[0].delay; gb->ir_sensor += cycles;
gb->infrared_input = gb->ir_queue[0].state; if (gb->ir_sensor > IR_MAX) {
gb->ir_queue_length--; gb->ir_sensor = IR_MAX;
memmove(&gb->ir_queue[0], &gb->ir_queue[1], sizeof(gb->ir_queue[0]) * (gb->ir_queue_length)); }
gb->effective_ir_input = gb->ir_sensor >= IR_THRESHOLD && gb->ir_sensor <= IR_THRESHOLD + IR_DECAY;
} }
else {
if (gb->ir_sensor <= cycles) {
gb->ir_sensor = 0;
}
else {
gb->ir_sensor -= cycles;
}
gb->effective_ir_input = false;
}
} }
static void advance_tima_state_machine(GB_gameboy_t *gb) static void advance_tima_state_machine(GB_gameboy_t *gb)
@ -234,8 +251,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
gb->double_speed_alignment += cycles; gb->double_speed_alignment += cycles;
gb->hdma_cycles += cycles; gb->hdma_cycles += cycles;
gb->apu_output.sample_cycles += cycles; gb->apu_output.sample_cycles += cycles;
gb->cycles_since_ir_change += cycles;
gb->cycles_since_input_ir_change += cycles;
gb->cycles_since_last_sync += cycles; gb->cycles_since_last_sync += cycles;
gb->cycles_since_run += cycles; gb->cycles_since_run += cycles;
@ -252,7 +267,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
} }
GB_apu_run(gb); GB_apu_run(gb);
GB_display_run(gb, cycles); GB_display_run(gb, cycles);
GB_ir_run(gb); GB_ir_run(gb, cycles);
} }
/* /*