More accurate IR emulation, simplify API
This commit is contained in:
parent
b7f3454763
commit
1d9ac5ccc3
@ -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];
|
||||||
|
11
Core/gb.c
11
Core/gb.c
@ -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)
|
||||||
|
19
Core/gb.h
19
Core/gb.h
@ -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 */
|
||||||
@ -613,12 +609,6 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_print_image_callback_t printer_callback;
|
GB_print_image_callback_t printer_callback;
|
||||||
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;
|
||||||
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user