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];
|
||||
}
|
||||
|
||||
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);
|
||||
[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)
|
||||
{
|
||||
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)
|
||||
|
19
Core/gb.h
19
Core/gb.h
@ -125,8 +125,6 @@ typedef enum {
|
||||
GB_BORDER_ALWAYS,
|
||||
} GB_border_mode_t;
|
||||
|
||||
#define GB_MAX_IR_QUEUE 256
|
||||
|
||||
enum {
|
||||
/* Joypad and Serial */
|
||||
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 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, 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_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);
|
||||
@ -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_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_watchpoint_s;
|
||||
|
||||
@ -374,6 +367,9 @@ struct GB_gameboy_internal_s {
|
||||
uint8_t extra_oam[0xff00 - 0xfea0];
|
||||
uint32_t ram_size; // Different between CGB and DMG
|
||||
GB_workboy_t workboy;
|
||||
|
||||
int32_t ir_sensor;
|
||||
bool effective_ir_input;
|
||||
);
|
||||
|
||||
/* DMA and HDMA */
|
||||
@ -614,12 +610,6 @@ struct GB_gameboy_internal_s {
|
||||
GB_workboy_set_time_callback workboy_set_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 ***/
|
||||
volatile bool debug_stopped, debug_disable;
|
||||
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_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_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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
return 1;
|
||||
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:
|
||||
GB_log(gb, "Unsupported HuC-3 mode %x read: %04x\n", gb->huc3_mode, addr);
|
||||
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) {
|
||||
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 &&
|
||||
@ -432,7 +427,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
if (gb->model != GB_MODEL_CGB_E) {
|
||||
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;
|
||||
}
|
||||
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
|
||||
return true;
|
||||
case 0xE: { // IR mode
|
||||
bool old_input = effective_ir_input(gb);
|
||||
if (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) {
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
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;
|
||||
bool new_input = effective_ir_input(gb);
|
||||
if (new_input != old_input) {
|
||||
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;
|
||||
}
|
||||
@ -1111,15 +1100,13 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
if (!GB_is_cgb(gb)) {
|
||||
return;
|
||||
}
|
||||
bool old_input = effective_ir_input(gb);
|
||||
gb->io_registers[GB_IO_RP] = value;
|
||||
bool new_input = effective_ir_input(gb);
|
||||
if (new_input != old_input) {
|
||||
if ((gb->io_registers[GB_IO_RP] ^ value) & 1) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -89,15 +89,32 @@ void GB_timing_sync(GB_gameboy_t *gb)
|
||||
}
|
||||
|
||||
#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->cycles_since_input_ir_change >= gb->ir_queue[0].delay) {
|
||||
gb->cycles_since_input_ir_change -= gb->ir_queue[0].delay;
|
||||
gb->infrared_input = gb->ir_queue[0].state;
|
||||
gb->ir_queue_length--;
|
||||
memmove(&gb->ir_queue[0], &gb->ir_queue[1], sizeof(gb->ir_queue[0]) * (gb->ir_queue_length));
|
||||
if (gb->model == GB_MODEL_AGB) return;
|
||||
if (gb->infrared_input || gb->cart_ir || (gb->io_registers[GB_IO_RP] & 1)) {
|
||||
gb->ir_sensor += cycles;
|
||||
if (gb->ir_sensor > IR_MAX) {
|
||||
gb->ir_sensor = IR_MAX;
|
||||
}
|
||||
|
||||
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)
|
||||
@ -234,8 +251,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
||||
gb->double_speed_alignment += cycles;
|
||||
gb->hdma_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_run += cycles;
|
||||
|
||||
@ -252,7 +267,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
||||
}
|
||||
GB_apu_run(gb);
|
||||
GB_display_run(gb, cycles);
|
||||
GB_ir_run(gb);
|
||||
GB_ir_run(gb, cycles);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user