diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index b63e3765..b30150bb 100644 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -29,7 +29,7 @@ using namespace nall; namespace Emulator { static const string Name = "bsnes"; - static const string Version = "112.11"; + static const string Version = "112.12"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org"; diff --git a/bsnes/gb/Core-new/apu.c b/bsnes/gb/Core-new/apu.c deleted file mode 100644 index 9afa5c93..00000000 --- a/bsnes/gb/Core-new/apu.c +++ /dev/null @@ -1,1040 +0,0 @@ -#include -#include -#include -#include -#include "gb.h" - -#define likely(x) __builtin_expect((x), 1) -#define unlikely(x) __builtin_expect((x), 0) - -static const uint8_t duties[] = { - 0, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 0, -}; - -static void refresh_channel(GB_gameboy_t *gb, unsigned index, unsigned cycles_offset) -{ - unsigned multiplier = gb->apu_output.cycles_since_render + cycles_offset - gb->apu_output.last_update[index]; - gb->apu_output.summed_samples[index].left += gb->apu_output.current_sample[index].left * multiplier; - gb->apu_output.summed_samples[index].right += gb->apu_output.current_sample[index].right * multiplier; - gb->apu_output.last_update[index] = gb->apu_output.cycles_since_render + cycles_offset; -} - -bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index) -{ - if (gb->model >= GB_MODEL_AGB) { - /* On the AGB, mixing is done digitally, so there are no per-channel - DACs. Instead, all channels are summed digital regardless of - whatever the DAC state would be on a CGB or earlier model. */ - return true; - } - - switch (index) { - case GB_SQUARE_1: - return gb->io_registers[GB_IO_NR12] & 0xF8; - - case GB_SQUARE_2: - return gb->io_registers[GB_IO_NR22] & 0xF8; - - case GB_WAVE: - return gb->apu.wave_channel.enable; - - case GB_NOISE: - return gb->io_registers[GB_IO_NR42] & 0xF8; - } - - return false; -} - -static uint8_t agb_bias_for_channel(GB_gameboy_t *gb, unsigned index) -{ - if (!gb->apu.is_active[index]) return 0; - - switch (index) { - case GB_SQUARE_1: - return gb->apu.square_channels[GB_SQUARE_1].current_volume; - case GB_SQUARE_2: - return gb->apu.square_channels[GB_SQUARE_2].current_volume; - case GB_WAVE: - return 0; - case GB_NOISE: - return gb->apu.noise_channel.current_volume; - } - return 0; -} - -static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset) -{ - if (gb->model >= GB_MODEL_AGB) { - /* On the AGB, because no analog mixing is done, the behavior of NR51 is a bit different. - A channel that is not connected to a terminal is idenitcal to a connected channel - playing PCM sample 0. */ - gb->apu.samples[index] = value; - - if (gb->apu_output.sample_rate) { - unsigned right_volume = (gb->io_registers[GB_IO_NR50] & 7) + 1; - unsigned left_volume = ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1; - - if (index == GB_WAVE) { - /* For some reason, channel 3 is inverted on the AGB */ - value ^= 0xF; - } - - GB_sample_t output; - uint8_t bias = agb_bias_for_channel(gb, index); - - if (gb->io_registers[GB_IO_NR51] & (1 << index)) { - output.right = (0xf - value * 2 + bias) * right_volume; - } - else { - output.right = 0xf * right_volume; - } - - if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) { - output.left = (0xf - value * 2 + bias) * left_volume; - } - else { - output.left = 0xf * left_volume; - } - - if (*(uint32_t *)&(gb->apu_output.current_sample[index]) != *(uint32_t *)&output) { - refresh_channel(gb, index, cycles_offset); - gb->apu_output.current_sample[index] = output; - } - } - - return; - } - - if (!GB_apu_is_DAC_enabled(gb, index)) { - value = gb->apu.samples[index]; - } - else { - gb->apu.samples[index] = value; - } - - if (gb->apu_output.sample_rate) { - unsigned right_volume = 0; - if (gb->io_registers[GB_IO_NR51] & (1 << index)) { - right_volume = (gb->io_registers[GB_IO_NR50] & 7) + 1; - } - unsigned left_volume = 0; - if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) { - left_volume = ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1; - } - GB_sample_t output = {(0xf - value * 2) * left_volume, (0xf - value * 2) * right_volume}; - if (*(uint32_t *)&(gb->apu_output.current_sample[index]) != *(uint32_t *)&output) { - refresh_channel(gb, index, cycles_offset); - gb->apu_output.current_sample[index] = output; - } - } -} - -static double smooth(double x) -{ - return 3*x*x - 2*x*x*x; -} - -static void render(GB_gameboy_t *gb) -{ - GB_sample_t output = {0,0}; - - UNROLL - for (unsigned i = 0; i < GB_N_CHANNELS; i++) { - double multiplier = CH_STEP; - - if (gb->model < GB_MODEL_AGB) { - if (!GB_apu_is_DAC_enabled(gb, i)) { - gb->apu_output.dac_discharge[i] -= ((double) DAC_DECAY_SPEED) / gb->apu_output.sample_rate; - if (gb->apu_output.dac_discharge[i] < 0) { - multiplier = 0; - gb->apu_output.dac_discharge[i] = 0; - } - else { - multiplier *= smooth(gb->apu_output.dac_discharge[i]); - } - } - else { - gb->apu_output.dac_discharge[i] += ((double) DAC_ATTACK_SPEED) / gb->apu_output.sample_rate; - if (gb->apu_output.dac_discharge[i] > 1) { - gb->apu_output.dac_discharge[i] = 1; - } - else { - multiplier *= smooth(gb->apu_output.dac_discharge[i]); - } - } - } - - if (likely(gb->apu_output.last_update[i] == 0)) { - output.left += gb->apu_output.current_sample[i].left * multiplier; - output.right += gb->apu_output.current_sample[i].right * multiplier; - } - else { - refresh_channel(gb, i, 0); - output.left += (signed long) gb->apu_output.summed_samples[i].left * multiplier - / gb->apu_output.cycles_since_render; - output.right += (signed long) gb->apu_output.summed_samples[i].right * multiplier - / gb->apu_output.cycles_since_render; - gb->apu_output.summed_samples[i] = (GB_sample_t){0, 0}; - } - gb->apu_output.last_update[i] = 0; - } - gb->apu_output.cycles_since_render = 0; - - GB_sample_t filtered_output = gb->apu_output.highpass_mode? - (GB_sample_t) {output.left - gb->apu_output.highpass_diff.left, - output.right - gb->apu_output.highpass_diff.right} : - output; - - switch (gb->apu_output.highpass_mode) { - case GB_HIGHPASS_OFF: - gb->apu_output.highpass_diff = (GB_double_sample_t) {0, 0}; - break; - case GB_HIGHPASS_ACCURATE: - gb->apu_output.highpass_diff = (GB_double_sample_t) - {output.left - filtered_output.left * gb->apu_output.highpass_rate, - output.right - filtered_output.right * gb->apu_output.highpass_rate}; - break; - case GB_HIGHPASS_REMOVE_DC_OFFSET: { - unsigned mask = gb->io_registers[GB_IO_NR51]; - unsigned left_volume = 0; - unsigned right_volume = 0; - UNROLL - for (unsigned i = GB_N_CHANNELS; i--;) { - if (gb->apu.is_active[i]) { - if (mask & 1) { - left_volume += (gb->io_registers[GB_IO_NR50] & 7) * CH_STEP * 0xF; - } - if (mask & 0x10) { - right_volume += ((gb->io_registers[GB_IO_NR50] >> 4) & 7) * CH_STEP * 0xF; - } - } - else { - left_volume += gb->apu_output.current_sample[i].left * CH_STEP; - right_volume += gb->apu_output.current_sample[i].right * CH_STEP; - } - mask >>= 1; - } - gb->apu_output.highpass_diff = (GB_double_sample_t) - {left_volume * (1 - gb->apu_output.highpass_rate) + gb->apu_output.highpass_diff.left * gb->apu_output.highpass_rate, - right_volume * (1 - gb->apu_output.highpass_rate) + gb->apu_output.highpass_diff.right * gb->apu_output.highpass_rate}; - - case GB_HIGHPASS_MAX:; - } - - } - - assert(gb->apu_output.sample_callback); - gb->apu_output.sample_callback(gb, &filtered_output); -} - -static uint16_t new_sweep_sample_legnth(GB_gameboy_t *gb) -{ - uint16_t delta = gb->apu.shadow_sweep_sample_legnth >> (gb->io_registers[GB_IO_NR10] & 7); - if (gb->io_registers[GB_IO_NR10] & 8) { - return gb->apu.shadow_sweep_sample_legnth - delta; - } - return gb->apu.shadow_sweep_sample_legnth + delta; -} - -static void update_square_sample(GB_gameboy_t *gb, unsigned index) -{ - if (gb->apu.square_channels[index].current_sample_index & 0x80) return; - - uint8_t duty = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6; - update_sample(gb, index, - duties[gb->apu.square_channels[index].current_sample_index + duty * 8]? - gb->apu.square_channels[index].current_volume : 0, - 0); -} - - -/* the effects of NRX2 writes on current volume are not well documented and differ - between models and variants. The exact behavior can only be verified on CGB as it - requires the PCM12 register. The behavior implemented here was verified on *my* - CGB, which might behave differently from other CGB revisions, as well as from the - DMG, MGB or SGB/2 */ -static void nrx2_glitch(uint8_t *volume, uint8_t value, uint8_t old_value) -{ - if (value & 8) { - (*volume)++; - } - - if (((value ^ old_value) & 8)) { - *volume = 0x10 - *volume; - } - - if ((value & 7) && !(old_value & 7) && *volume && !(value & 8)) { - (*volume)--; - } - - if ((old_value & 7) && (value & 8)) { - (*volume)--; - } - - (*volume) &= 0xF; -} - -static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index) -{ - uint8_t nrx2 = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22]; - - if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 7)) { - if (!gb->apu.square_channels[index].volume_countdown || !--gb->apu.square_channels[index].volume_countdown) { - if ((nrx2 & 8) && gb->apu.square_channels[index].current_volume < 0xF) { - gb->apu.square_channels[index].current_volume++; - } - - else if (!(nrx2 & 8) && gb->apu.square_channels[index].current_volume > 0) { - gb->apu.square_channels[index].current_volume--; - } - - gb->apu.square_channels[index].volume_countdown = nrx2 & 7; - - if (gb->apu.is_active[index]) { - update_square_sample(gb, index); - } - } - } -} - -static void tick_noise_envelope(GB_gameboy_t *gb) -{ - uint8_t nr42 = gb->io_registers[GB_IO_NR42]; - - if (gb->apu.noise_channel.volume_countdown || (nr42 & 7)) { - if (!--gb->apu.noise_channel.volume_countdown) { - if ((nr42 & 8) && gb->apu.noise_channel.current_volume < 0xF) { - gb->apu.noise_channel.current_volume++; - } - - else if (!(nr42 & 8) && gb->apu.noise_channel.current_volume > 0) { - gb->apu.noise_channel.current_volume--; - } - - gb->apu.noise_channel.volume_countdown = nr42 & 7; - - if (gb->apu.is_active[GB_NOISE]) { - update_sample(gb, GB_NOISE, - (gb->apu.noise_channel.lfsr & 1) ? - gb->apu.noise_channel.current_volume : 0, - 0); - } - } - } -} - -void GB_apu_div_event(GB_gameboy_t *gb) -{ - if (!gb->apu.global_enable) return; - if (gb->apu.skip_div_event) { - gb->apu.skip_div_event = false; - return; - } - gb->apu.div_divider++; - - if ((gb->apu.div_divider & 1) == 0) { - for (unsigned i = GB_SQUARE_2 + 1; i--;) { - uint8_t nrx2 = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22]; - if (gb->apu.is_active[i] && gb->apu.square_channels[i].volume_countdown == 0 && (nrx2 & 7)) { - tick_square_envelope(gb, i); - } - } - - if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0 && (gb->io_registers[GB_IO_NR42] & 7)) { - tick_noise_envelope(gb); - } - } - - if ((gb->apu.div_divider & 7) == 0) { - for (unsigned i = GB_SQUARE_2 + 1; i--;) { - tick_square_envelope(gb, i); - } - - tick_noise_envelope(gb); - } - - if ((gb->apu.div_divider & 1) == 1) { - for (unsigned i = GB_SQUARE_2 + 1; i--;) { - if (gb->apu.square_channels[i].length_enabled) { - if (gb->apu.square_channels[i].pulse_length) { - if (!--gb->apu.square_channels[i].pulse_length) { - gb->apu.is_active[i] = false; - update_sample(gb, i, 0, 0); - } - } - } - } - - if (gb->apu.wave_channel.length_enabled) { - if (gb->apu.wave_channel.pulse_length) { - if (!--gb->apu.wave_channel.pulse_length) { - gb->apu.is_active[GB_WAVE] = false; - update_sample(gb, GB_WAVE, 0, 0); - } - } - } - - if (gb->apu.noise_channel.length_enabled) { - if (gb->apu.noise_channel.pulse_length) { - if (!--gb->apu.noise_channel.pulse_length) { - gb->apu.is_active[GB_NOISE] = false; - update_sample(gb, GB_NOISE, 0, 0); - } - } - } - } - - if ((gb->apu.div_divider & 3) == 3) { - if (!gb->apu.sweep_enabled) { - return; - } - if (gb->apu.square_sweep_countdown) { - if (!--gb->apu.square_sweep_countdown) { - if ((gb->io_registers[GB_IO_NR10] & 0x70) && (gb->io_registers[GB_IO_NR10] & 0x07)) { - gb->apu.square_channels[GB_SQUARE_1].sample_length = - gb->apu.shadow_sweep_sample_legnth = - gb->apu.new_sweep_sample_legnth; - } - - if (gb->io_registers[GB_IO_NR10] & 0x70) { - /* Recalculation and overflow check only occurs after a delay */ - gb->apu.square_sweep_calculate_countdown = 0x13 - gb->apu.lf_div; - } - - gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7); - if (!gb->apu.square_sweep_countdown) gb->apu.square_sweep_countdown = 8; - } - } - } -} - - -void GB_apu_run(GB_gameboy_t *gb) -{ - /* Convert 4MHZ to 2MHz. apu_cycles is always divisable by 4. */ - uint8_t cycles = gb->apu.apu_cycles >> 2; - gb->apu.apu_cycles = 0; - if (!cycles) return; - - if (likely(!gb->stopped || GB_is_cgb(gb))) { - /* To align the square signal to 1MHz */ - gb->apu.lf_div ^= cycles & 1; - gb->apu.noise_channel.alignment += cycles; - - if (gb->apu.square_sweep_calculate_countdown) { - if (gb->apu.square_sweep_calculate_countdown > cycles) { - gb->apu.square_sweep_calculate_countdown -= cycles; - } - else { - /* APU bug: sweep frequency is checked after adding the sweep delta twice */ - gb->apu.new_sweep_sample_legnth = new_sweep_sample_legnth(gb); - if (gb->apu.new_sweep_sample_legnth > 0x7ff) { - gb->apu.is_active[GB_SQUARE_1] = false; - update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles); - gb->apu.sweep_enabled = false; - } - gb->apu.sweep_decreasing |= gb->io_registers[GB_IO_NR10] & 8; - gb->apu.square_sweep_calculate_countdown = 0; - } - } - - UNROLL - for (unsigned i = GB_SQUARE_1; i <= GB_SQUARE_2; i++) { - if (gb->apu.is_active[i]) { - uint8_t cycles_left = cycles; - while (unlikely(cycles_left > gb->apu.square_channels[i].sample_countdown)) { - cycles_left -= gb->apu.square_channels[i].sample_countdown + 1; - gb->apu.square_channels[i].sample_countdown = (gb->apu.square_channels[i].sample_length ^ 0x7FF) * 2 + 1; - gb->apu.square_channels[i].current_sample_index++; - gb->apu.square_channels[i].current_sample_index &= 0x7; - - update_square_sample(gb, i); - } - if (cycles_left) { - gb->apu.square_channels[i].sample_countdown -= cycles_left; - } - } - } - - gb->apu.wave_channel.wave_form_just_read = false; - if (gb->apu.is_active[GB_WAVE]) { - uint8_t cycles_left = cycles; - while (unlikely(cycles_left > gb->apu.wave_channel.sample_countdown)) { - cycles_left -= gb->apu.wave_channel.sample_countdown + 1; - gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length ^ 0x7FF; - gb->apu.wave_channel.current_sample_index++; - gb->apu.wave_channel.current_sample_index &= 0x1F; - gb->apu.wave_channel.current_sample = - gb->apu.wave_channel.wave_form[gb->apu.wave_channel.current_sample_index]; - update_sample(gb, GB_WAVE, - gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift, - cycles - cycles_left); - gb->apu.wave_channel.wave_form_just_read = true; - } - if (cycles_left) { - gb->apu.wave_channel.sample_countdown -= cycles_left; - gb->apu.wave_channel.wave_form_just_read = false; - } - } - - if (gb->apu.is_active[GB_NOISE]) { - uint8_t cycles_left = cycles; - while (unlikely(cycles_left > gb->apu.noise_channel.sample_countdown)) { - cycles_left -= gb->apu.noise_channel.sample_countdown + 1; - gb->apu.noise_channel.sample_countdown = gb->apu.noise_channel.sample_length * 4 + 3; - - /* Step LFSR */ - unsigned high_bit_mask = gb->apu.noise_channel.narrow ? 0x4040 : 0x4000; - /* Todo: is this formula is different on a GBA? */ - bool new_high_bit = (gb->apu.noise_channel.lfsr ^ (gb->apu.noise_channel.lfsr >> 1) ^ 1) & 1; - gb->apu.noise_channel.lfsr >>= 1; - - if (new_high_bit) { - gb->apu.noise_channel.lfsr |= high_bit_mask; - } - else { - /* This code is not redundent, it's relevant when switching LFSR widths */ - gb->apu.noise_channel.lfsr &= ~high_bit_mask; - } - - gb->apu.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1; - - update_sample(gb, GB_NOISE, - gb->apu.current_lfsr_sample ? - gb->apu.noise_channel.current_volume : 0, - 0); - } - if (cycles_left) { - gb->apu.noise_channel.sample_countdown -= cycles_left; - } - } - } - - if (gb->apu_output.sample_rate) { - gb->apu_output.cycles_since_render += cycles; - - if (gb->apu_output.sample_cycles >= gb->apu_output.cycles_per_sample) { - gb->apu_output.sample_cycles -= gb->apu_output.cycles_per_sample; - render(gb); - } - } -} -void GB_apu_init(GB_gameboy_t *gb) -{ - memset(&gb->apu, 0, sizeof(gb->apu)); - /* Restore the wave form */ - for (unsigned reg = GB_IO_WAV_START; reg <= GB_IO_WAV_END; reg++) { - gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = gb->io_registers[reg] >> 4; - gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = gb->io_registers[reg] & 0xF; - } - gb->apu.lf_div = 1; - /* APU glitch: When turning the APU on while DIV's bit 4 (or 5 in double speed mode) is on, - the first DIV/APU event is skipped. */ - if (gb->div_counter & (gb->cgb_double_speed? 0x2000 : 0x1000)) { - gb->apu.skip_div_event = true; - } -} - -uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg) -{ - if (reg == GB_IO_NR52) { - uint8_t value = 0; - for (int i = 0; i < GB_N_CHANNELS; i++) { - value >>= 1; - if (gb->apu.is_active[i]) { - value |= 0x8; - } - } - if (gb->apu.global_enable) { - value |= 0x80; - } - value |= 0x70; - return value; - } - - static const char read_mask[GB_IO_WAV_END - GB_IO_NR10 + 1] = { - /* NRX0 NRX1 NRX2 NRX3 NRX4 */ - 0x80, 0x3F, 0x00, 0xFF, 0xBF, // NR1X - 0xFF, 0x3F, 0x00, 0xFF, 0xBF, // NR2X - 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, // NR3X - 0xFF, 0xFF, 0x00, 0x00, 0xBF, // NR4X - 0x00, 0x00, 0x70, 0xFF, 0xFF, // NR5X - - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Unused - // Wave RAM - 0, /* ... */ - }; - - if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) { - if (!GB_is_cgb(gb) && !gb->apu.wave_channel.wave_form_just_read) { - return 0xFF; - } - reg = GB_IO_WAV_START + gb->apu.wave_channel.current_sample_index / 2; - } - - return gb->io_registers[reg] | read_mask[reg - GB_IO_NR10]; -} - -void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) -{ - if (!gb->apu.global_enable && reg != GB_IO_NR52 && reg < GB_IO_WAV_START && (GB_is_cgb(gb) || - ( - reg != GB_IO_NR11 && - reg != GB_IO_NR21 && - reg != GB_IO_NR31 && - reg != GB_IO_NR41 - ) - )) { - return; - } - - if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) { - if (!GB_is_cgb(gb) && !gb->apu.wave_channel.wave_form_just_read) { - return; - } - reg = GB_IO_WAV_START + gb->apu.wave_channel.current_sample_index / 2; - } - - /* Todo: this can and should be rewritten with a function table. */ - switch (reg) { - /* Globals */ - case GB_IO_NR50: - case GB_IO_NR51: - gb->io_registers[reg] = value; - /* These registers affect the output of all 4 channels (but not the output of the PCM registers).*/ - /* We call update_samples with the current value so the APU output is updated with the new outputs */ - for (unsigned i = GB_N_CHANNELS; i--;) { - update_sample(gb, i, gb->apu.samples[i], 0); - } - break; - case GB_IO_NR52: { - - uint8_t old_nrx1[] = { - gb->io_registers[GB_IO_NR11], - gb->io_registers[GB_IO_NR21], - gb->io_registers[GB_IO_NR31], - gb->io_registers[GB_IO_NR41] - }; - if ((value & 0x80) && !gb->apu.global_enable) { - GB_apu_init(gb); - gb->apu.global_enable = true; - } - else if (!(value & 0x80) && gb->apu.global_enable) { - for (unsigned i = GB_N_CHANNELS; i--;) { - update_sample(gb, i, 0, 0); - } - memset(&gb->apu, 0, sizeof(gb->apu)); - memset(gb->io_registers + GB_IO_NR10, 0, GB_IO_WAV_START - GB_IO_NR10); - old_nrx1[0] &= 0x3F; - old_nrx1[1] &= 0x3F; - - gb->apu.global_enable = false; - } - - if (!GB_is_cgb(gb) && (value & 0x80)) { - GB_apu_write(gb, GB_IO_NR11, old_nrx1[0]); - GB_apu_write(gb, GB_IO_NR21, old_nrx1[1]); - GB_apu_write(gb, GB_IO_NR31, old_nrx1[2]); - GB_apu_write(gb, GB_IO_NR41, old_nrx1[3]); - } - } - break; - - /* Square channels */ - case GB_IO_NR10: - if (gb->apu.sweep_decreasing && !(value & 8)) { - gb->apu.is_active[GB_SQUARE_1] = false; - update_sample(gb, GB_SQUARE_1, 0, 0); - gb->apu.sweep_enabled = false; - gb->apu.square_sweep_calculate_countdown = 0; - } - if ((value & 0x70) == 0) { - /* Todo: what happens if we set period to 0 while a calculate event is scheduled, and then - re-set it to non-zero? */ - gb->apu.square_sweep_calculate_countdown = 0; - } - break; - - case GB_IO_NR11: - case GB_IO_NR21: { - unsigned index = reg == GB_IO_NR21? GB_SQUARE_2: GB_SQUARE_1; - gb->apu.square_channels[index].pulse_length = (0x40 - (value & 0x3f)); - if (!gb->apu.global_enable) { - value &= 0x3f; - } - break; - } - - case GB_IO_NR12: - case GB_IO_NR22: { - unsigned index = reg == GB_IO_NR22? GB_SQUARE_2: GB_SQUARE_1; - if (((value & 0x7) == 0) && ((gb->io_registers[reg] & 0x7) != 0)) { - /* Envelope disabled */ - gb->apu.square_channels[index].volume_countdown = 0; - } - if ((value & 0xF8) == 0) { - /* This disables the DAC */ - gb->io_registers[reg] = value; - gb->apu.is_active[index] = false; - update_sample(gb, index, 0, 0); - } - else if (gb->apu.is_active[index]) { - nrx2_glitch(&gb->apu.square_channels[index].current_volume, value, gb->io_registers[reg]); - update_square_sample(gb, index); - } - - break; - } - - case GB_IO_NR13: - case GB_IO_NR23: { - unsigned index = reg == GB_IO_NR23? GB_SQUARE_2: GB_SQUARE_1; - gb->apu.square_channels[index].sample_length &= ~0xFF; - gb->apu.square_channels[index].sample_length |= value & 0xFF; - break; - } - - case GB_IO_NR14: - case GB_IO_NR24: { - unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1; - - /* TODO: When the sample length changes right before being updated, the countdown should change to the - old length, but the current sample should not change. Because our write timing isn't accurate to - the T-cycle, we hack around it by stepping the sample index backwards. */ - if ((value & 0x80) == 0 && gb->apu.is_active[index]) { - /* On an AGB, as well as on CGB C and earlier (TODO: Tested: 0, B and C), it behaves slightly different on - double speed. */ - if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */ || gb->apu.square_channels[index].sample_countdown & 1) { - if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) { - gb->apu.square_channels[index].current_sample_index--; - gb->apu.square_channels[index].current_sample_index &= 7; - } - } - } - - gb->apu.square_channels[index].sample_length &= 0xFF; - gb->apu.square_channels[index].sample_length |= (value & 7) << 8; - if (index == GB_SQUARE_1) { - gb->apu.shadow_sweep_sample_legnth = - gb->apu.new_sweep_sample_legnth = - gb->apu.square_channels[0].sample_length; - } - if (value & 0x80) { - /* Current sample index remains unchanged when restarting channels 1 or 2. It is only reset by - turning the APU off. */ - if (!gb->apu.is_active[index]) { - gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 6 - gb->apu.lf_div; - } - else { - /* Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier.*/ - gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 4 - gb->apu.lf_div; - } - gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4; - - /* The volume changes caused by NRX4 sound start take effect instantly (i.e. the effect the previously - started sound). The playback itself is not instant which is why we don't update the sample for other - cases. */ - if (gb->apu.is_active[index]) { - update_square_sample(gb, index); - } - - gb->apu.square_channels[index].volume_countdown = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 7; - - if ((gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0xF8) != 0 && !gb->apu.is_active[index]) { - gb->apu.is_active[index] = true; - update_sample(gb, index, 0, 0); - /* We use the highest bit in current_sample_index to mark this sample is not actually playing yet, */ - gb->apu.square_channels[index].current_sample_index |= 0x80; - } - if (gb->apu.square_channels[index].pulse_length == 0) { - gb->apu.square_channels[index].pulse_length = 0x40; - gb->apu.square_channels[index].length_enabled = false; - } - - if (index == GB_SQUARE_1) { - gb->apu.sweep_decreasing = false; - if (gb->io_registers[GB_IO_NR10] & 7) { - /* APU bug: if shift is nonzero, overflow check also occurs on trigger */ - gb->apu.square_sweep_calculate_countdown = 0x13 - gb->apu.lf_div; - } - else { - gb->apu.square_sweep_calculate_countdown = 0; - } - gb->apu.sweep_enabled = gb->io_registers[GB_IO_NR10] & 0x77; - gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7); - if (!gb->apu.square_sweep_countdown) gb->apu.square_sweep_countdown = 8; - } - - } - - /* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */ - if ((value & 0x40) && - !gb->apu.square_channels[index].length_enabled && - (gb->apu.div_divider & 1) && - gb->apu.square_channels[index].pulse_length) { - gb->apu.square_channels[index].pulse_length--; - if (gb->apu.square_channels[index].pulse_length == 0) { - if (value & 0x80) { - gb->apu.square_channels[index].pulse_length = 0x3F; - } - else { - gb->apu.is_active[index] = false; - update_sample(gb, index, 0, 0); - } - } - } - gb->apu.square_channels[index].length_enabled = value & 0x40; - break; - } - - /* Wave channel */ - case GB_IO_NR30: - gb->apu.wave_channel.enable = value & 0x80; - if (!gb->apu.wave_channel.enable) { - gb->apu.is_active[GB_WAVE] = false; - update_sample(gb, GB_WAVE, 0, 0); - } - break; - case GB_IO_NR31: - gb->apu.wave_channel.pulse_length = (0x100 - value); - break; - case GB_IO_NR32: - gb->apu.wave_channel.shift = (uint8_t[]){4, 0, 1, 2}[(value >> 5) & 3]; - if (gb->apu.is_active[GB_WAVE]) { - update_sample(gb, GB_WAVE, gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift, 0); - } - break; - case GB_IO_NR33: - gb->apu.wave_channel.sample_length &= ~0xFF; - gb->apu.wave_channel.sample_length |= value & 0xFF; - break; - case GB_IO_NR34: - gb->apu.wave_channel.sample_length &= 0xFF; - gb->apu.wave_channel.sample_length |= (value & 7) << 8; - if ((value & 0x80)) { - /* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU - reads from it. */ - if (!GB_is_cgb(gb) && - gb->apu.is_active[GB_WAVE] && - gb->apu.wave_channel.sample_countdown == 0 && - gb->apu.wave_channel.enable) { - unsigned offset = ((gb->apu.wave_channel.current_sample_index + 1) >> 1) & 0xF; - - /* This glitch varies between models and even specific instances: - DMG-B: Most of them behave as emulated. A few behave differently. - SGB: As far as I know, all tested instances behave as emulated. - MGB, SGB2: Most instances behave non-deterministically, a few behave as emulated. - - Additionally, I believe DMGs, including those we behave differently than emulated, - are all deterministic. */ - if (offset < 4) { - gb->io_registers[GB_IO_WAV_START] = gb->io_registers[GB_IO_WAV_START + offset]; - gb->apu.wave_channel.wave_form[0] = gb->apu.wave_channel.wave_form[offset / 2]; - gb->apu.wave_channel.wave_form[1] = gb->apu.wave_channel.wave_form[offset / 2 + 1]; - } - else { - memcpy(gb->io_registers + GB_IO_WAV_START, - gb->io_registers + GB_IO_WAV_START + (offset & ~3), - 4); - memcpy(gb->apu.wave_channel.wave_form, - gb->apu.wave_channel.wave_form + (offset & ~3) * 2, - 8); - } - } - if (!gb->apu.is_active[GB_WAVE]) { - gb->apu.is_active[GB_WAVE] = true; - update_sample(gb, GB_WAVE, - gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift, - 0); - } - gb->apu.wave_channel.sample_countdown = (gb->apu.wave_channel.sample_length ^ 0x7FF) + 3; - gb->apu.wave_channel.current_sample_index = 0; - if (gb->apu.wave_channel.pulse_length == 0) { - gb->apu.wave_channel.pulse_length = 0x100; - gb->apu.wave_channel.length_enabled = false; - } - /* Note that we don't change the sample just yet! This was verified on hardware. */ - } - - /* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */ - if ((value & 0x40) && - !gb->apu.wave_channel.length_enabled && - (gb->apu.div_divider & 1) && - gb->apu.wave_channel.pulse_length) { - gb->apu.wave_channel.pulse_length--; - if (gb->apu.wave_channel.pulse_length == 0) { - if (value & 0x80) { - gb->apu.wave_channel.pulse_length = 0xFF; - } - else { - gb->apu.is_active[GB_WAVE] = false; - update_sample(gb, GB_WAVE, 0, 0); - } - } - } - gb->apu.wave_channel.length_enabled = value & 0x40; - if (gb->apu.is_active[GB_WAVE] && !gb->apu.wave_channel.enable) { - gb->apu.is_active[GB_WAVE] = false; - update_sample(gb, GB_WAVE, 0, 0); - } - - break; - - /* Noise Channel */ - - case GB_IO_NR41: { - gb->apu.noise_channel.pulse_length = (0x40 - (value & 0x3f)); - break; - } - - case GB_IO_NR42: { - if (((value & 0x7) == 0) && ((gb->io_registers[reg] & 0x7) != 0)) { - /* Envelope disabled */ - gb->apu.noise_channel.volume_countdown = 0; - } - if ((value & 0xF8) == 0) { - /* This disables the DAC */ - gb->io_registers[reg] = value; - gb->apu.is_active[GB_NOISE] = false; - update_sample(gb, GB_NOISE, 0, 0); - } - else if (gb->apu.is_active[GB_NOISE]){ - nrx2_glitch(&gb->apu.noise_channel.current_volume, value, gb->io_registers[reg]); - update_sample(gb, GB_NOISE, - gb->apu.current_lfsr_sample ? - gb->apu.noise_channel.current_volume : 0, - 0); - } - break; - } - - case GB_IO_NR43: { - gb->apu.noise_channel.narrow = value & 8; - unsigned divisor = (value & 0x07) << 1; - if (!divisor) divisor = 1; - gb->apu.noise_channel.sample_length = (divisor << (value >> 4)) - 1; - - /* Todo: changing the frequency sometimes delays the next sample. This is probably - due to how the frequency is actually calculated in the noise channel, which is probably - not by calculating the effective sample length and counting simiarly to the other channels. - This is not emulated correctly. */ - break; - } - - case GB_IO_NR44: { - if (value & 0x80) { - gb->apu.noise_channel.sample_countdown = (gb->apu.noise_channel.sample_length) * 2 + 6 - gb->apu.lf_div; - - /* I'm COMPLETELY unsure about this logic, but it passes all relevant tests. - See comment in NR43. */ - if ((gb->io_registers[GB_IO_NR43] & 7) && (gb->apu.noise_channel.alignment & 2) == 0) { - if ((gb->io_registers[GB_IO_NR43] & 7) == 1) { - gb->apu.noise_channel.sample_countdown += 2; - } - else { - gb->apu.noise_channel.sample_countdown -= 2; - } - } - if (gb->apu.is_active[GB_NOISE]) { - gb->apu.noise_channel.sample_countdown += 2; - } - - gb->apu.noise_channel.current_volume = gb->io_registers[GB_IO_NR42] >> 4; - - /* The volume changes caused by NRX4 sound start take effect instantly (i.e. the effect the previously - started sound). The playback itself is not instant which is why we don't update the sample for other - cases. */ - if (gb->apu.is_active[GB_NOISE]) { - update_sample(gb, GB_NOISE, - gb->apu.current_lfsr_sample ? - gb->apu.noise_channel.current_volume : 0, - 0); - } - gb->apu.noise_channel.lfsr = 0; - gb->apu.current_lfsr_sample = false; - gb->apu.noise_channel.volume_countdown = gb->io_registers[GB_IO_NR42] & 7; - - if (!gb->apu.is_active[GB_NOISE] && (gb->io_registers[GB_IO_NR42] & 0xF8) != 0) { - gb->apu.is_active[GB_NOISE] = true; - update_sample(gb, GB_NOISE, 0, 0); - } - - if (gb->apu.noise_channel.pulse_length == 0) { - gb->apu.noise_channel.pulse_length = 0x40; - gb->apu.noise_channel.length_enabled = false; - } - } - - /* APU glitch - if length is enabled while the DIV-divider's LSB is 1, tick the length once. */ - if ((value & 0x40) && - !gb->apu.noise_channel.length_enabled && - (gb->apu.div_divider & 1) && - gb->apu.noise_channel.pulse_length) { - gb->apu.noise_channel.pulse_length--; - if (gb->apu.noise_channel.pulse_length == 0) { - if (value & 0x80) { - gb->apu.noise_channel.pulse_length = 0x3F; - } - else { - gb->apu.is_active[GB_NOISE] = false; - update_sample(gb, GB_NOISE, 0, 0); - } - } - } - gb->apu.noise_channel.length_enabled = value & 0x40; - break; - } - - default: - if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END) { - gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = value >> 4; - gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = value & 0xF; - } - } - gb->io_registers[reg] = value; -} - -void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate) -{ - - gb->apu_output.sample_rate = sample_rate; - if (sample_rate) { - gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate); - } - gb->apu_output.rate_set_in_clocks = false; - GB_apu_update_cycles_per_sample(gb); -} - -void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample) -{ - - if (cycles_per_sample == 0) { - GB_set_sample_rate(gb, 0); - return; - } - gb->apu_output.cycles_per_sample = cycles_per_sample; - gb->apu_output.sample_rate = GB_get_clock_rate(gb) / cycles_per_sample * 2; - gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample); - gb->apu_output.rate_set_in_clocks = true; -} - -void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback) -{ - gb->apu_output.sample_callback = callback; -} - -void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode) -{ - gb->apu_output.highpass_mode = mode; -} - -void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb) -{ - if (gb->apu_output.rate_set_in_clocks) return; - if (gb->apu_output.sample_rate) { - gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */ - } -} diff --git a/bsnes/gb/Core-new/apu.h b/bsnes/gb/Core-new/apu.h deleted file mode 100644 index 885e0ce3..00000000 --- a/bsnes/gb/Core-new/apu.h +++ /dev/null @@ -1,164 +0,0 @@ -#ifndef apu_h -#define apu_h -#include -#include -#include -#include "gb_struct_def.h" - - -#ifdef GB_INTERNAL -/* Speed = 1 / Length (in seconds) */ -#define DAC_DECAY_SPEED 20000 -#define DAC_ATTACK_SPEED 20000 - - -/* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */ -#ifdef WIIU -/* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/ -#define MAX_CH_AMP (0xFF0 / 2) -#else -#define MAX_CH_AMP 0xFF0 -#endif -#define CH_STEP (MAX_CH_AMP/0xF/8) -#endif - - - -/* APU ticks are 2MHz, triggered by an internal APU clock. */ - -typedef struct -{ - int16_t left; - int16_t right; -} GB_sample_t; - -typedef struct -{ - double left; - double right; -} GB_double_sample_t; - -enum GB_CHANNELS { - GB_SQUARE_1, - GB_SQUARE_2, - GB_WAVE, - GB_NOISE, - GB_N_CHANNELS -}; - -typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample); - -typedef struct -{ - bool global_enable; - uint8_t apu_cycles; - - uint8_t samples[GB_N_CHANNELS]; - bool is_active[GB_N_CHANNELS]; - - uint8_t div_divider; // The DIV register ticks the APU at 512Hz, but is then divided - // once more to generate 128Hz and 64Hz clocks - - uint8_t lf_div; // The APU runs in 2MHz, but channels 1, 2 and 4 run in 1MHZ so we divide - // need to divide the signal. - - uint8_t square_sweep_countdown; // In 128Hz - uint8_t square_sweep_calculate_countdown; // In 2 MHz - uint16_t new_sweep_sample_legnth; - uint16_t shadow_sweep_sample_legnth; - bool sweep_enabled; - bool sweep_decreasing; - - struct { - uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks - uint8_t current_volume; // Reloaded from NRX2 - uint8_t volume_countdown; // Reloaded from NRX2 - uint8_t current_sample_index; /* For save state compatibility, - highest bit is reused (See NR14/NR24's - write code)*/ - - uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF) - uint16_t sample_length; // From NRX3, NRX4, in APU ticks - bool length_enabled; // NRX4 - - } square_channels[2]; - - struct { - bool enable; // NR30 - uint16_t pulse_length; // Reloaded from NR31 (xorred), in 256Hz DIV ticks - uint8_t shift; // NR32 - uint16_t sample_length; // NR33, NR34, in APU ticks - bool length_enabled; // NR34 - - uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF) - uint8_t current_sample_index; - uint8_t current_sample; // Current sample before shifting. - - int8_t wave_form[32]; - bool wave_form_just_read; - } wave_channel; - - struct { - uint16_t pulse_length; // Reloaded from NR41 (xorred), in 256Hz DIV ticks - uint8_t current_volume; // Reloaded from NR42 - uint8_t volume_countdown; // Reloaded from NR42 - uint16_t lfsr; - bool narrow; - - uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length) - uint16_t sample_length; // From NR43, in APU ticks - bool length_enabled; // NR44 - - uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of - // 1MHz. This variable keeps track of the alignment. - - } noise_channel; - - bool skip_div_event; - bool current_lfsr_sample; -} GB_apu_t; - -typedef enum { - GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset - GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware - GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform - GB_HIGHPASS_MAX -} GB_highpass_mode_t; - -typedef struct { - unsigned sample_rate; - - double sample_cycles; // In 8 MHz units - double cycles_per_sample; - - // Samples are NOT normalized to MAX_CH_AMP * 4 at this stage! - unsigned cycles_since_render; - unsigned last_update[GB_N_CHANNELS]; - GB_sample_t current_sample[GB_N_CHANNELS]; - GB_sample_t summed_samples[GB_N_CHANNELS]; - double dac_discharge[GB_N_CHANNELS]; - - GB_highpass_mode_t highpass_mode; - double highpass_rate; - GB_double_sample_t highpass_diff; - - GB_sample_callback_t sample_callback; - - bool rate_set_in_clocks; -} GB_apu_output_t; - -void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate); -void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */ -void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode); -void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback); -#ifdef GB_INTERNAL -bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index); -void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value); -uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg); -void GB_apu_div_event(GB_gameboy_t *gb); -void GB_apu_init(GB_gameboy_t *gb); -void GB_apu_run(GB_gameboy_t *gb); -void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb); -#endif - -#endif /* apu_h */ diff --git a/bsnes/gb/Core-new/camera.c b/bsnes/gb/Core-new/camera.c deleted file mode 100644 index 9b34998a..00000000 --- a/bsnes/gb/Core-new/camera.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "gb.h" - -static int noise_seed = 0; - -/* This is not a complete emulation of the camera chip. Only the features used by the GameBoy Camera ROMs are supported. - We also do not emulate the timing of the real cart, as it might be actually faster than the webcam. */ - -static uint8_t generate_noise(uint8_t x, uint8_t y) -{ - int value = (x + y * 128 + noise_seed); - uint8_t *data = (uint8_t *) &value; - unsigned hash = 0; - - while ((int *) data != &value + 1) { - hash ^= (*data << 8); - if (hash & 0x8000) { - hash ^= 0x8a00; - hash ^= *data; - } - data++; - hash <<= 1; - } - return (hash >> 8); -} - -static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y) -{ - if (x >= 128) { - x = 0; - } - if (y >= 112) { - y = 0; - } - - long color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x, y) : (generate_noise(x, y)); - - static const double gain_values[] = - {0.8809390, 0.9149149, 0.9457498, 0.9739758, - 1.0000000, 1.0241412, 1.0466537, 1.0677433, - 1.0875793, 1.1240310, 1.1568911, 1.1868043, - 1.2142561, 1.2396208, 1.2743837, 1.3157323, - 1.3525190, 1.3856512, 1.4157897, 1.4434309, - 1.4689574, 1.4926697, 1.5148087, 1.5355703, - 1.5551159, 1.5735801, 1.5910762, 1.6077008, - 1.6235366, 1.6386550, 1.6531183, 1.6669808}; - /* Multiply color by gain value */ - color *= gain_values[gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x1F]; - - - /* Color is multiplied by the exposure register to simulate exposure. */ - color = color * ((gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) + gb->camera_registers[GB_CAMERA_EXPOSURE_LOW]) / 0x1000; - - return color; -} - -uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr) -{ - if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) { - /* Forbid reading the image while the camera is busy. */ - return 0xFF; - } - uint8_t tile_x = addr / 0x10 % 0x10; - uint8_t tile_y = addr / 0x10 / 0x10; - - uint8_t y = ((addr >> 1) & 0x7) + tile_y * 8; - uint8_t bit = addr & 1; - - uint8_t ret = 0; - - for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) { - - long color = get_processed_color(gb, x, y); - - static const double edge_enhancement_ratios[] = {0.5, 0.75, 1, 1.25, 2, 3, 4, 5}; - double edge_enhancement_ratio = edge_enhancement_ratios[(gb->camera_registers[GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE] >> 4) & 0x7]; - if ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0xE0) == 0xE0) { - color += (color * 4) * edge_enhancement_ratio; - color -= get_processed_color(gb, x - 1, y) * edge_enhancement_ratio; - color -= get_processed_color(gb, x + 1, y) * edge_enhancement_ratio; - color -= get_processed_color(gb, x, y - 1) * edge_enhancement_ratio; - color -= get_processed_color(gb, x, y + 1) * edge_enhancement_ratio; - } - - - /* The camera's registers are used as a threshold pattern, which defines the dithering */ - uint8_t pattern_base = ((x & 3) + (y & 3) * 4) * 3 + GB_CAMERA_DITHERING_PATTERN_START; - - if (color < gb->camera_registers[pattern_base]) { - color = 3; - } - else if (color < gb->camera_registers[pattern_base + 1]) { - color = 2; - } - else if (color < gb->camera_registers[pattern_base + 2]) { - color = 1; - } - else { - color = 0; - } - - ret <<= 1; - ret |= (color >> bit) & 1; - } - - return ret; -} - -void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback) -{ - gb->camera_get_pixel_callback = callback; -} - -void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback) -{ - gb->camera_update_request_callback = callback; -} - -void GB_camera_updated(GB_gameboy_t *gb) -{ - gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] &= ~1; -} - -void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - addr &= 0x7F; - if (addr == GB_CAMERA_SHOOT_AND_1D_FLAGS) { - value &= 0x7; - noise_seed = rand(); - if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && gb->camera_update_request_callback) { - /* If no callback is set, ignore the write as if the camera is instantly done */ - gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] |= 1; - gb->camera_update_request_callback(gb); - } - } - else { - if (addr >= 0x36) { - GB_log(gb, "Wrote invalid camera register %02x: %2x\n", addr, value); - return; - } - gb->camera_registers[addr] = value; - } -} -uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr) -{ - if ((addr & 0x7F) == 0) { - return gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS]; - } - return 0; -} diff --git a/bsnes/gb/Core-new/camera.h b/bsnes/gb/Core-new/camera.h deleted file mode 100644 index 21c69b68..00000000 --- a/bsnes/gb/Core-new/camera.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef camera_h -#define camera_h -#include -#include "gb_struct_def.h" - -typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y); -typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb); - -enum { - GB_CAMERA_SHOOT_AND_1D_FLAGS = 0, - GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS = 1, - GB_CAMERA_EXPOSURE_HIGH = 2, - GB_CAMERA_EXPOSURE_LOW = 3, - GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE = 4, - GB_CAMERA_DITHERING_PATTERN_START = 6, - GB_CAMERA_DITHERING_PATTERN_END = 0x35, -}; - -uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr); - -void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback); -void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback); - -void GB_camera_updated(GB_gameboy_t *gb); - -void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value); -uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr); - -#endif diff --git a/bsnes/gb/Core-new/debugger.c b/bsnes/gb/Core-new/debugger.c deleted file mode 100644 index df480f34..00000000 --- a/bsnes/gb/Core-new/debugger.c +++ /dev/null @@ -1,2459 +0,0 @@ -#include -#include -#include -#include "gb.h" - -typedef struct { - bool has_bank; - uint16_t bank:9; - uint16_t value; -} value_t; - -typedef struct { - enum { - LVALUE_MEMORY, - LVALUE_MEMORY16, - LVALUE_REG16, - LVALUE_REG_H, - LVALUE_REG_L, - } kind; - union { - uint16_t *register_address; - value_t memory_address; - }; -} lvalue_t; - -#define VALUE_16(x) ((value_t){false, 0, (x)}) - -struct GB_breakpoint_s { - union { - struct { - uint16_t addr; - uint16_t bank; /* -1 = any bank*/ - }; - uint32_t key; /* For sorting and comparing */ - }; - char *condition; - bool is_jump_to; -}; - -#define BP_KEY(x) (((struct GB_breakpoint_s){.addr = ((x).value), .bank = (x).has_bank? (x).bank : -1 }).key) - -#define GB_WATCHPOINT_R (1) -#define GB_WATCHPOINT_W (2) - -struct GB_watchpoint_s { - union { - struct { - uint16_t addr; - uint16_t bank; /* -1 = any bank*/ - }; - uint32_t key; /* For sorting and comparing */ - }; - char *condition; - uint8_t flags; -}; - -#define WP_KEY(x) (((struct GB_watchpoint_s){.addr = ((x).value), .bank = (x).has_bank? (x).bank : -1 }).key) - -static uint16_t bank_for_addr(GB_gameboy_t *gb, uint16_t addr) -{ - if (addr < 0x4000) { - return gb->mbc_rom0_bank; - } - - if (addr < 0x8000) { - return gb->mbc_rom_bank; - } - - if (addr < 0xD000) { - return 0; - } - - if (addr < 0xE000) { - return gb->cgb_ram_bank; - } - - return 0; -} - -typedef struct { - uint16_t rom0_bank; - uint16_t rom_bank; - uint8_t mbc_ram_bank; - bool mbc_ram_enable; - uint8_t ram_bank; - uint8_t vram_bank; -} banking_state_t; - -static inline void save_banking_state(GB_gameboy_t *gb, banking_state_t *state) -{ - state->rom0_bank = gb->mbc_rom0_bank; - state->rom_bank = gb->mbc_rom_bank; - state->mbc_ram_bank = gb->mbc_ram_bank; - state->mbc_ram_enable = gb->mbc_ram_enable; - state->ram_bank = gb->cgb_ram_bank; - state->vram_bank = gb->cgb_vram_bank; -} - -static inline void restore_banking_state(GB_gameboy_t *gb, banking_state_t *state) -{ - - gb->mbc_rom0_bank = state->rom0_bank; - gb->mbc_rom_bank = state->rom_bank; - gb->mbc_ram_bank = state->mbc_ram_bank; - gb->mbc_ram_enable = state->mbc_ram_enable; - gb->cgb_ram_bank = state->ram_bank; - gb->cgb_vram_bank = state->vram_bank; -} - -static inline void switch_banking_state(GB_gameboy_t *gb, uint16_t bank) -{ - gb->mbc_rom0_bank = bank; - gb->mbc_rom_bank = bank; - gb->mbc_ram_bank = bank; - gb->mbc_ram_enable = true; - if (GB_is_cgb(gb)) { - gb->cgb_ram_bank = bank & 7; - gb->cgb_vram_bank = bank & 1; - if (gb->cgb_ram_bank == 0) { - gb->cgb_ram_bank = 1; - } - } -} - -static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer_name) -{ - static __thread char output[256]; - const GB_bank_symbol_t *symbol = GB_debugger_find_symbol(gb, value); - - if (symbol && (value - symbol->addr > 0x1000 || symbol->addr == 0) ) { - symbol = NULL; - } - - /* Avoid overflow */ - if (symbol && strlen(symbol->name) > 240) { - symbol = NULL; - } - - if (!symbol) { - sprintf(output, "$%04x", value); - } - - else if (symbol->addr == value) { - if (prefer_name) { - sprintf(output, "%s ($%04x)", symbol->name, value); - } - else { - sprintf(output, "$%04x (%s)", value, symbol->name); - } - } - - else { - if (prefer_name) { - sprintf(output, "%s+$%03x ($%04x)", symbol->name, value - symbol->addr, value); - } - else { - sprintf(output, "$%04x (%s+$%03x)", value, symbol->name, value - symbol->addr); - } - } - return output; -} - -static const char *debugger_value_to_string(GB_gameboy_t *gb, value_t value, bool prefer_name) -{ - if (!value.has_bank) return value_to_string(gb, value.value, prefer_name); - - static __thread char output[256]; - const GB_bank_symbol_t *symbol = GB_map_find_symbol(gb->bank_symbols[value.bank], value.value); - - if (symbol && (value.value - symbol->addr > 0x1000 || symbol->addr == 0) ) { - symbol = NULL; - } - - /* Avoid overflow */ - if (symbol && strlen(symbol->name) > 240) { - symbol = NULL; - } - - if (!symbol) { - sprintf(output, "$%02x:$%04x", value.bank, value.value); - } - - else if (symbol->addr == value.value) { - if (prefer_name) { - sprintf(output, "%s ($%02x:$%04x)", symbol->name, value.bank, value.value); - } - else { - sprintf(output, "$%02x:$%04x (%s)", value.bank, value.value, symbol->name); - } - } - - else { - if (prefer_name) { - sprintf(output, "%s+$%03x ($%02x:$%04x)", symbol->name, value.value - symbol->addr, value.bank, value.value); - } - else { - sprintf(output, "$%02x:$%04x (%s+$%03x)", value.bank, value.value, symbol->name, value.value - symbol->addr); - } - } - return output; -} - -static value_t read_lvalue(GB_gameboy_t *gb, lvalue_t lvalue) -{ - /* Not used until we add support for operators like += */ - switch (lvalue.kind) { - case LVALUE_MEMORY: - if (lvalue.memory_address.has_bank) { - banking_state_t state; - save_banking_state(gb, &state); - switch_banking_state(gb, lvalue.memory_address.bank); - value_t r = VALUE_16(GB_read_memory(gb, lvalue.memory_address.value)); - restore_banking_state(gb, &state); - return r; - } - return VALUE_16(GB_read_memory(gb, lvalue.memory_address.value)); - - case LVALUE_MEMORY16: - if (lvalue.memory_address.has_bank) { - banking_state_t state; - save_banking_state(gb, &state); - switch_banking_state(gb, lvalue.memory_address.bank); - value_t r = VALUE_16(GB_read_memory(gb, lvalue.memory_address.value)); - restore_banking_state(gb, &state); - return r; - } - return VALUE_16(GB_read_memory(gb, lvalue.memory_address.value) | - (GB_read_memory(gb, lvalue.memory_address.value + 1) * 0x100)); - - case LVALUE_REG16: - return VALUE_16(*lvalue.register_address); - - case LVALUE_REG_L: - return VALUE_16(*lvalue.register_address & 0x00FF); - - case LVALUE_REG_H: - return VALUE_16(*lvalue.register_address >> 8); - } - - return VALUE_16(0); -} - -static void write_lvalue(GB_gameboy_t *gb, lvalue_t lvalue, uint16_t value) -{ - switch (lvalue.kind) { - case LVALUE_MEMORY: - if (lvalue.memory_address.has_bank) { - banking_state_t state; - save_banking_state(gb, &state); - switch_banking_state(gb, lvalue.memory_address.bank); - GB_write_memory(gb, lvalue.memory_address.value, value); - restore_banking_state(gb, &state); - return; - } - GB_write_memory(gb, lvalue.memory_address.value, value); - return; - - case LVALUE_MEMORY16: - if (lvalue.memory_address.has_bank) { - banking_state_t state; - save_banking_state(gb, &state); - switch_banking_state(gb, lvalue.memory_address.bank); - GB_write_memory(gb, lvalue.memory_address.value, value); - restore_banking_state(gb, &state); - return; - } - GB_write_memory(gb, lvalue.memory_address.value, value); - GB_write_memory(gb, lvalue.memory_address.value + 1, value >> 8); - return; - - case LVALUE_REG16: - *lvalue.register_address = value; - return; - - case LVALUE_REG_L: - *lvalue.register_address &= 0xFF00; - *lvalue.register_address |= value & 0xFF; - return; - - case LVALUE_REG_H: - *lvalue.register_address &= 0x00FF; - *lvalue.register_address |= value << 8; - return; - } -} - -/* 16 bit value 16 bit value = 16 bit value - 25 bit address 16 bit value = 25 bit address - 16 bit value 25 bit address = 25 bit address - 25 bit address 25 bit address = 16 bit value (since adding pointers, for examples, makes no sense) - - Boolean operators always return a 16-bit value - */ -#define FIX_BANK(x) ((value_t){a.has_bank ^ b.has_bank, a.has_bank? a.bank : b.bank, (x)}) - -static value_t add(value_t a, value_t b) {return FIX_BANK(a.value + b.value);} -static value_t sub(value_t a, value_t b) {return FIX_BANK(a.value - b.value);} -static value_t mul(value_t a, value_t b) {return FIX_BANK(a.value * b.value);} -static value_t _div(value_t a, value_t b) { - if (b.value == 0) { - return FIX_BANK(0); - } - return FIX_BANK(a.value / b.value); -}; -static value_t mod(value_t a, value_t b) { - if (b.value == 0) { - return FIX_BANK(0); - } - return FIX_BANK(a.value % b.value); -}; -static value_t and(value_t a, value_t b) {return FIX_BANK(a.value & b.value);} -static value_t or(value_t a, value_t b) {return FIX_BANK(a.value | b.value);} -static value_t xor(value_t a, value_t b) {return FIX_BANK(a.value ^ b.value);} -static value_t shleft(value_t a, value_t b) {return FIX_BANK(a.value << b.value);} -static value_t shright(value_t a, value_t b) {return FIX_BANK(a.value >> b.value);} -static value_t assign(GB_gameboy_t *gb, lvalue_t a, uint16_t b) -{ - write_lvalue(gb, a, b); - return read_lvalue(gb, a); -} - -static value_t bool_and(value_t a, value_t b) {return VALUE_16(a.value && b.value);} -static value_t bool_or(value_t a, value_t b) {return VALUE_16(a.value || b.value);} -static value_t equals(value_t a, value_t b) {return VALUE_16(a.value == b.value);} -static value_t different(value_t a, value_t b) {return VALUE_16(a.value != b.value);} -static value_t lower(value_t a, value_t b) {return VALUE_16(a.value < b.value);} -static value_t greater(value_t a, value_t b) {return VALUE_16(a.value > b.value);} -static value_t lower_equals(value_t a, value_t b) {return VALUE_16(a.value <= b.value);} -static value_t greater_equals(value_t a, value_t b) {return VALUE_16(a.value >= b.value);} -static value_t bank(value_t a, value_t b) {return (value_t) {true, a.value, b.value};} - - -static struct { - const char *string; - char priority; - value_t (*operator)(value_t, value_t); - value_t (*lvalue_operator)(GB_gameboy_t *, lvalue_t, uint16_t); -} operators[] = -{ - // Yes. This is not C-like. But it makes much more sense. - // Deal with it. - {"+", 0, add}, - {"-", 0, sub}, - {"||", 0, bool_or}, - {"|", 0, or}, - {"*", 1, mul}, - {"/", 1, _div}, - {"%", 1, mod}, - {"&&", 1, bool_and}, - {"&", 1, and}, - {"^", 1, xor}, - {"<<", 2, shleft}, - {"<=", -1, lower_equals}, - {"<", -1, lower}, - {">>", 2, shright}, - {">=", -1, greater_equals}, - {">", -1, greater}, - {"==", -1, equals}, - {"=", -2, NULL, assign}, - {"!=", -1, different}, - {":", 3, bank}, -}; - -value_t debugger_evaluate(GB_gameboy_t *gb, const char *string, - size_t length, bool *error, - uint16_t *watchpoint_address, uint8_t *watchpoint_new_value); - -static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string, - size_t length, bool *error, - uint16_t *watchpoint_address, uint8_t *watchpoint_new_value) -{ - *error = false; - // Strip whitespace - while (length && (string[0] == ' ' || string[0] == '\n' || string[0] == '\r' || string[0] == '\t')) { - string++; - length--; - } - while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) { - length--; - } - if (length == 0) - { - GB_log(gb, "Expected expression.\n"); - *error = true; - return (lvalue_t){0,}; - } - if (string[0] == '(' && string[length - 1] == ')') { - // Attempt to strip parentheses - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '(') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == ')') depth--; - } - if (depth == 0) return debugger_evaluate_lvalue(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value); - } - else if (string[0] == '[' && string[length - 1] == ']') { - // Attempt to strip square parentheses (memory dereference) - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '[') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == ']') depth--; - } - if (depth == 0) { - return (lvalue_t){LVALUE_MEMORY, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value)}; - } - } - else if (string[0] == '{' && string[length - 1] == '}') { - // Attempt to strip curly parentheses (memory dereference) - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '{') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == '}') depth--; - } - if (depth == 0) { - return (lvalue_t){LVALUE_MEMORY16, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value)}; - } - } - - // Registers - if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) { - if (length == 1) { - switch (string[0]) { - case 'a': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_AF]}; - case 'f': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_AF]}; - case 'b': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_BC]}; - case 'c': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_BC]}; - case 'd': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_DE]}; - case 'e': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_DE]}; - case 'h': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_HL]}; - case 'l': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_HL]}; - } - } - else if (length == 2) { - switch (string[0]) { - case 'a': if (string[1] == 'f') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_AF]}; - case 'b': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_BC]}; - case 'd': if (string[1] == 'e') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_DE]}; - case 'h': if (string[1] == 'l') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_HL]}; - case 's': if (string[1] == 'p') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_SP]}; - case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc}; - } - } - GB_log(gb, "Unknown register: %.*s\n", (unsigned) length, string); - *error = true; - return (lvalue_t){0,}; - } - - GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned) length, string); - *error = true; - return (lvalue_t){0,}; -} - -#define ERROR ((value_t){0,}) -value_t debugger_evaluate(GB_gameboy_t *gb, const char *string, - size_t length, bool *error, - uint16_t *watchpoint_address, uint8_t *watchpoint_new_value) -{ - /* Disable watchpoints while evaulating expressions */ - uint16_t n_watchpoints = gb->n_watchpoints; - gb->n_watchpoints = 0; - - value_t ret = ERROR; - - *error = false; - // Strip whitespace - while (length && (string[0] == ' ' || string[0] == '\n' || string[0] == '\r' || string[0] == '\t')) { - string++; - length--; - } - while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) { - length--; - } - if (length == 0) - { - GB_log(gb, "Expected expression.\n"); - *error = true; - goto exit; - } - if (string[0] == '(' && string[length - 1] == ')') { - // Attempt to strip parentheses - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '(') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == ')') depth--; - } - if (depth == 0) { - ret = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value); - goto exit; - } - } - else if (string[0] == '[' && string[length - 1] == ']') { - // Attempt to strip square parentheses (memory dereference) - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '[') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == ']') depth--; - } - - if (depth == 0) { - value_t addr = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value); - banking_state_t state; - if (addr.bank) { - save_banking_state(gb, &state); - switch_banking_state(gb, addr.bank); - } - ret = VALUE_16(GB_read_memory(gb, addr.value)); - if (addr.bank) { - restore_banking_state(gb, &state); - } - goto exit; - } - } - else if (string[0] == '{' && string[length - 1] == '}') { - // Attempt to strip curly parentheses (memory dereference) - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '{') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == '}') depth--; - } - - if (depth == 0) { - value_t addr = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value); - banking_state_t state; - if (addr.bank) { - save_banking_state(gb, &state); - switch_banking_state(gb, addr.bank); - } - ret = VALUE_16(GB_read_memory(gb, addr.value) | (GB_read_memory(gb, addr.value + 1) * 0x100)); - if (addr.bank) { - restore_banking_state(gb, &state); - } - goto exit; - } - } - // Search for lowest priority operator - signed int depth = 0; - unsigned operator_index = -1; - unsigned operator_pos = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '(') depth++; - else if (string[i] == ')') depth--; - else if (string[i] == '[') depth++; - else if (string[i] == ']') depth--; - else if (depth == 0) { - for (int j = 0; j < sizeof(operators) / sizeof(operators[0]); j++) { - if (strlen(operators[j].string) > length - i) continue; // Operator too big. - // Priority higher than what we already have. - unsigned long operator_length = strlen(operators[j].string); - if (memcmp(string + i, operators[j].string, operator_length) == 0) { - if (operator_index != -1 && operators[operator_index].priority < operators[j].priority) { - /* for supporting = vs ==, etc*/ - i += operator_length - 1; - continue; - } - // Found an operator! - operator_pos = i; - operator_index = j; - /* for supporting = vs ==, etc*/ - i += operator_length - 1; - break; - } - } - } - } - if (operator_index != -1) { - unsigned right_start = (unsigned)(operator_pos + strlen(operators[operator_index].string)); - value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, watchpoint_address, watchpoint_new_value); - if (*error) goto exit; - if (operators[operator_index].lvalue_operator) { - lvalue_t left = debugger_evaluate_lvalue(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value); - if (*error) goto exit; - ret = operators[operator_index].lvalue_operator(gb, left, right.value); - goto exit; - } - value_t left = debugger_evaluate(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value); - if (*error) goto exit; - ret = operators[operator_index].operator(left, right); - goto exit; - } - - // Not an expression - must be a register or a literal - - // Registers - if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) { - if (length == 1) { - switch (string[0]) { - case 'a': ret = VALUE_16(gb->registers[GB_REGISTER_AF] >> 8); goto exit; - case 'f': ret = VALUE_16(gb->registers[GB_REGISTER_AF] & 0xFF); goto exit; - case 'b': ret = VALUE_16(gb->registers[GB_REGISTER_BC] >> 8); goto exit; - case 'c': ret = VALUE_16(gb->registers[GB_REGISTER_BC] & 0xFF); goto exit; - case 'd': ret = VALUE_16(gb->registers[GB_REGISTER_DE] >> 8); goto exit; - case 'e': ret = VALUE_16(gb->registers[GB_REGISTER_DE] & 0xFF); goto exit; - case 'h': ret = VALUE_16(gb->registers[GB_REGISTER_HL] >> 8); goto exit; - case 'l': ret = VALUE_16(gb->registers[GB_REGISTER_HL] & 0xFF); goto exit; - } - } - else if (length == 2) { - switch (string[0]) { - case 'a': if (string[1] == 'f') {ret = VALUE_16(gb->registers[GB_REGISTER_AF]); goto exit;} - case 'b': if (string[1] == 'c') {ret = VALUE_16(gb->registers[GB_REGISTER_BC]); goto exit;} - case 'd': if (string[1] == 'e') {ret = VALUE_16(gb->registers[GB_REGISTER_DE]); goto exit;} - case 'h': if (string[1] == 'l') {ret = VALUE_16(gb->registers[GB_REGISTER_HL]); goto exit;} - case 's': if (string[1] == 'p') {ret = VALUE_16(gb->registers[GB_REGISTER_SP]); goto exit;} - case 'p': if (string[1] == 'c') {ret = (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}; goto exit;} - } - } - else if (length == 3) { - if (watchpoint_address && memcmp(string, "old", 3) == 0) { - ret = VALUE_16(GB_read_memory(gb, *watchpoint_address)); - goto exit; - } - - if (watchpoint_new_value && memcmp(string, "new", 3) == 0) { - ret = VALUE_16(*watchpoint_new_value); - goto exit; - } - - /* $new is identical to $old in read conditions */ - if (watchpoint_address && memcmp(string, "new", 3) == 0) { - ret = VALUE_16(GB_read_memory(gb, *watchpoint_address)); - goto exit; - } - } - - char symbol_name[length + 1]; - memcpy(symbol_name, string, length); - symbol_name[length] = 0; - const GB_symbol_t *symbol = GB_reversed_map_find_symbol(&gb->reversed_symbol_map, symbol_name); - if (symbol) { - ret = (value_t){true, symbol->bank, symbol->addr}; - goto exit; - } - - GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned) length, string); - *error = true; - goto exit; - } - - char *end; - int base = 10; - if (string[0] == '$') { - string++; - base = 16; - length--; - } - uint16_t literal = (uint16_t) (strtol(string, &end, base)); - if (end != string + length) { - GB_log(gb, "Failed to parse: %.*s\n", (unsigned) length, string); - *error = true; - goto exit; - } - ret = VALUE_16(literal); -exit: - gb->n_watchpoints = n_watchpoints; - return ret; -} - -struct debugger_command_s; -typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments, char *modifiers, const struct debugger_command_s *command); - -typedef struct debugger_command_s { - const char *command; - uint8_t min_length; - debugger_command_imp_t *implementation; - const char *help_string; // Null if should not appear in help - const char *arguments_format; // For usage message - const char *modifiers_format; // For usage message -} debugger_command_t; - -static const char *lstrip(const char *str) -{ - while (*str == ' ' || *str == '\t') { - str++; - } - return str; -} - -#define STOPPED_ONLY \ -if (!gb->debug_stopped) { \ -GB_log(gb, "Program is running. \n"); \ -return false; \ -} - -#define NO_MODIFIERS \ -if (modifiers) { \ -print_usage(gb, command); \ -return true; \ -} - -static void print_usage(GB_gameboy_t *gb, const debugger_command_t *command) -{ - GB_log(gb, "Usage: %s", command->command); - - if (command->modifiers_format) { - GB_log(gb, "[/%s]", command->modifiers_format); - } - - if (command->arguments_format) { - GB_log(gb, " %s", command->arguments_format); - } - - GB_log(gb, "\n"); -} - -static bool cont(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - gb->debug_stopped = false; - return false; -} - -static bool next(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - gb->debug_stopped = false; - gb->debug_next_command = true; - gb->debug_call_depth = 0; - return false; -} - -static bool step(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - return false; -} - -static bool finish(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - gb->debug_stopped = false; - gb->debug_fin_command = true; - gb->debug_call_depth = 0; - return false; -} - -static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - gb->debug_stopped = false; - gb->stack_leak_detection = true; - gb->debug_call_depth = 0; - return false; -} - -static bool registers(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - - GB_log(gb, "AF = $%04x (%c%c%c%c)\n", gb->registers[GB_REGISTER_AF], /* AF can't really be an address */ - (gb->f & GB_CARRY_FLAG)? 'C' : '-', - (gb->f & GB_HALF_CARRY_FLAG)? 'H' : '-', - (gb->f & GB_SUBSTRACT_FLAG)? 'N' : '-', - (gb->f & GB_ZERO_FLAG)? 'Z' : '-'); - GB_log(gb, "BC = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_BC], false)); - GB_log(gb, "DE = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_DE], false)); - GB_log(gb, "HL = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_HL], false)); - GB_log(gb, "SP = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_SP], false)); - GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false)); - return true; -} - -/* Find the index of the closest breakpoint equal or greater to addr */ -static uint16_t find_breakpoint(GB_gameboy_t *gb, value_t addr) -{ - if (!gb->breakpoints) { - return 0; - } - - uint32_t key = BP_KEY(addr); - - int min = 0; - int max = gb->n_breakpoints; - while (min < max) { - uint16_t pivot = (min + max) / 2; - if (gb->breakpoints[pivot].key == key) return pivot; - if (gb->breakpoints[pivot].key > key) { - max = pivot; - } - else { - min = pivot + 1; - } - } - return (uint16_t) min; -} - -static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - bool is_jump_to = true; - if (!modifiers) { - is_jump_to = false; - } - else if (strcmp(modifiers, "j") != 0) { - print_usage(gb, command); - return true; - } - - if (strlen(lstrip(arguments)) == 0) { - print_usage(gb, command); - return true; - } - - if (gb->n_breakpoints == (typeof(gb->n_breakpoints)) -1) { - GB_log(gb, "Too many breakpoints set\n"); - return true; - } - - char *condition = NULL; - if ((condition = strstr(arguments, " if "))) { - *condition = 0; - condition += strlen(" if "); - /* Verify condition is sane (Todo: This might have side effects!) */ - bool error; - debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, NULL, NULL); - if (error) return true; - - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); - uint32_t key = BP_KEY(result); - - if (error) return true; - - uint16_t index = find_breakpoint(gb, result); - if (index < gb->n_breakpoints && gb->breakpoints[index].key == key) { - GB_log(gb, "Breakpoint already set at %s\n", debugger_value_to_string(gb, result, true)); - if (!gb->breakpoints[index].condition && condition) { - GB_log(gb, "Added condition to breakpoint\n"); - gb->breakpoints[index].condition = strdup(condition); - } - else if (gb->breakpoints[index].condition && condition) { - GB_log(gb, "Replaced breakpoint condition\n"); - free(gb->breakpoints[index].condition); - gb->breakpoints[index].condition = strdup(condition); - } - else if (gb->breakpoints[index].condition && !condition) { - GB_log(gb, "Removed breakpoint condition\n"); - free(gb->breakpoints[index].condition); - gb->breakpoints[index].condition = NULL; - } - return true; - } - - gb->breakpoints = realloc(gb->breakpoints, (gb->n_breakpoints + 1) * sizeof(gb->breakpoints[0])); - memmove(&gb->breakpoints[index + 1], &gb->breakpoints[index], (gb->n_breakpoints - index) * sizeof(gb->breakpoints[0])); - gb->breakpoints[index].key = key; - - if (condition) { - gb->breakpoints[index].condition = strdup(condition); - } - else { - gb->breakpoints[index].condition = NULL; - } - gb->n_breakpoints++; - - gb->breakpoints[index].is_jump_to = is_jump_to; - - if (is_jump_to) { - gb->has_jump_to_breakpoints = true; - } - - GB_log(gb, "Breakpoint set at %s\n", debugger_value_to_string(gb, result, true)); - return true; -} - -static bool delete(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments)) == 0) { - for (unsigned i = gb->n_breakpoints; i--;) { - if (gb->breakpoints[i].condition) { - free(gb->breakpoints[i].condition); - } - } - free(gb->breakpoints); - gb->breakpoints = NULL; - gb->n_breakpoints = 0; - return true; - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); - uint32_t key = BP_KEY(result); - - if (error) return true; - - uint16_t index = 0; - for (unsigned i = 0; i < gb->n_breakpoints; i++) { - if (gb->breakpoints[i].key == key) { - /* Full match */ - index = i; - break; - } - if (gb->breakpoints[i].addr == result.value && result.has_bank != (gb->breakpoints[i].bank != (uint16_t) -1)) { - /* Partial match */ - index = i; - } - } - - if (index >= gb->n_breakpoints) { - GB_log(gb, "No breakpoint set at %s\n", debugger_value_to_string(gb, result, true)); - return true; - } - - result.bank = gb->breakpoints[index].bank; - result.has_bank = gb->breakpoints[index].bank != (uint16_t) -1; - - if (gb->breakpoints[index].condition) { - free(gb->breakpoints[index].condition); - } - - if (gb->breakpoints[index].is_jump_to) { - gb->has_jump_to_breakpoints = false; - for (unsigned i = 0; i < gb->n_breakpoints; i++) { - if (i == index) continue; - if (gb->breakpoints[i].is_jump_to) { - gb->has_jump_to_breakpoints = true; - break; - } - } - } - - memmove(&gb->breakpoints[index], &gb->breakpoints[index + 1], (gb->n_breakpoints - index - 1) * sizeof(gb->breakpoints[0])); - gb->n_breakpoints--; - gb->breakpoints = realloc(gb->breakpoints, gb->n_breakpoints * sizeof(gb->breakpoints[0])); - - GB_log(gb, "Breakpoint removed from %s\n", debugger_value_to_string(gb, result, true)); - return true; -} - -/* Find the index of the closest watchpoint equal or greater to addr */ -static uint16_t find_watchpoint(GB_gameboy_t *gb, value_t addr) -{ - if (!gb->watchpoints) { - return 0; - } - uint32_t key = WP_KEY(addr); - int min = 0; - int max = gb->n_watchpoints; - while (min < max) { - uint16_t pivot = (min + max) / 2; - if (gb->watchpoints[pivot].key == key) return pivot; - if (gb->watchpoints[pivot].key > key) { - max = pivot; - } - else { - min = pivot + 1; - } - } - return (uint16_t) min; -} - -static bool watch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - if (strlen(lstrip(arguments)) == 0) { -print_usage: - print_usage(gb, command); - return true; - } - - if (gb->n_watchpoints == (typeof(gb->n_watchpoints)) -1) { - GB_log(gb, "Too many watchpoints set\n"); - return true; - } - - if (!modifiers) { - modifiers = "w"; - } - - uint8_t flags = 0; - while (*modifiers) { - switch (*modifiers) { - case 'r': - flags |= GB_WATCHPOINT_R; - break; - case 'w': - flags |= GB_WATCHPOINT_W; - break; - default: - goto print_usage; - } - modifiers++; - } - - if (!flags) { - goto print_usage; - } - - char *condition = NULL; - if ((condition = strstr(arguments, " if "))) { - *condition = 0; - condition += strlen(" if "); - /* Verify condition is sane (Todo: This might have side effects!) */ - bool error; - /* To make $new and $old legal */ - uint16_t dummy = 0; - uint8_t dummy2 = 0; - debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, &dummy, &dummy2); - if (error) return true; - - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); - uint32_t key = WP_KEY(result); - - if (error) return true; - - uint16_t index = find_watchpoint(gb, result); - if (index < gb->n_watchpoints && gb->watchpoints[index].key == key) { - GB_log(gb, "Watchpoint already set at %s\n", debugger_value_to_string(gb, result, true)); - if (gb->watchpoints[index].flags != flags) { - GB_log(gb, "Modified watchpoint type\n"); - gb->watchpoints[index].flags = flags; - } - if (!gb->watchpoints[index].condition && condition) { - GB_log(gb, "Added condition to watchpoint\n"); - gb->watchpoints[index].condition = strdup(condition); - } - else if (gb->watchpoints[index].condition && condition) { - GB_log(gb, "Replaced watchpoint condition\n"); - free(gb->watchpoints[index].condition); - gb->watchpoints[index].condition = strdup(condition); - } - else if (gb->watchpoints[index].condition && !condition) { - GB_log(gb, "Removed watchpoint condition\n"); - free(gb->watchpoints[index].condition); - gb->watchpoints[index].condition = NULL; - } - return true; - } - - gb->watchpoints = realloc(gb->watchpoints, (gb->n_watchpoints + 1) * sizeof(gb->watchpoints[0])); - memmove(&gb->watchpoints[index + 1], &gb->watchpoints[index], (gb->n_watchpoints - index) * sizeof(gb->watchpoints[0])); - gb->watchpoints[index].key = key; - gb->watchpoints[index].flags = flags; - if (condition) { - gb->watchpoints[index].condition = strdup(condition); - } - else { - gb->watchpoints[index].condition = NULL; - } - gb->n_watchpoints++; - - GB_log(gb, "Watchpoint set at %s\n", debugger_value_to_string(gb, result, true)); - return true; -} - -static bool unwatch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments)) == 0) { - for (unsigned i = gb->n_watchpoints; i--;) { - if (gb->watchpoints[i].condition) { - free(gb->watchpoints[i].condition); - } - } - free(gb->watchpoints); - gb->watchpoints = NULL; - gb->n_watchpoints = 0; - return true; - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); - uint32_t key = WP_KEY(result); - - if (error) return true; - - uint16_t index = 0; - for (unsigned i = 0; i < gb->n_watchpoints; i++) { - if (gb->watchpoints[i].key == key) { - /* Full match */ - index = i; - break; - } - if (gb->watchpoints[i].addr == result.value && result.has_bank != (gb->watchpoints[i].bank != (uint16_t) -1)) { - /* Partial match */ - index = i; - } - } - - if (index >= gb->n_watchpoints) { - GB_log(gb, "No watchpoint set at %s\n", debugger_value_to_string(gb, result, true)); - return true; - } - - result.bank = gb->watchpoints[index].bank; - result.has_bank = gb->watchpoints[index].bank != (uint16_t) -1; - - if (gb->watchpoints[index].condition) { - free(gb->watchpoints[index].condition); - } - - memmove(&gb->watchpoints[index], &gb->watchpoints[index + 1], (gb->n_watchpoints - index - 1) * sizeof(gb->watchpoints[0])); - gb->n_watchpoints--; - gb->watchpoints = realloc(gb->watchpoints, gb->n_watchpoints* sizeof(gb->watchpoints[0])); - - GB_log(gb, "Watchpoint removed from %s\n", debugger_value_to_string(gb, result, true)); - return true; -} - -static bool list(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - if (gb->n_breakpoints == 0) { - GB_log(gb, "No breakpoints set.\n"); - } - else { - GB_log(gb, "%d breakpoint(s) set:\n", gb->n_breakpoints); - for (uint16_t i = 0; i < gb->n_breakpoints; i++) { - value_t addr = (value_t){gb->breakpoints[i].bank != (uint16_t)-1, gb->breakpoints[i].bank, gb->breakpoints[i].addr}; - if (gb->breakpoints[i].condition) { - GB_log(gb, " %d. %s (%sCondition: %s)\n", i + 1, - debugger_value_to_string(gb, addr, addr.has_bank), - gb->breakpoints[i].is_jump_to? "Jump to, ": "", - gb->breakpoints[i].condition); - } - else { - GB_log(gb, " %d. %s%s\n", i + 1, - debugger_value_to_string(gb, addr, addr.has_bank), - gb->breakpoints[i].is_jump_to? " (Jump to)" : ""); - } - } - } - - if (gb->n_watchpoints == 0) { - GB_log(gb, "No watchpoints set.\n"); - } - else { - GB_log(gb, "%d watchpoint(s) set:\n", gb->n_watchpoints); - for (uint16_t i = 0; i < gb->n_watchpoints; i++) { - value_t addr = (value_t){gb->watchpoints[i].bank != (uint16_t)-1, gb->watchpoints[i].bank, gb->watchpoints[i].addr}; - if (gb->watchpoints[i].condition) { - GB_log(gb, " %d. %s (%c%c, Condition: %s)\n", i + 1, debugger_value_to_string(gb, addr, addr.has_bank), - (gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-', - (gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-', - gb->watchpoints[i].condition); - } - else { - GB_log(gb, " %d. %s (%c%c)\n", i + 1, debugger_value_to_string(gb,addr, addr.has_bank), - (gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-', - (gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-'); - } - } - } - - return true; -} - -static bool _should_break(GB_gameboy_t *gb, value_t addr, bool jump_to) -{ - uint16_t index = find_breakpoint(gb, addr); - uint32_t key = BP_KEY(addr); - - if (index < gb->n_breakpoints && gb->breakpoints[index].key == key && gb->breakpoints[index].is_jump_to == jump_to) { - if (!gb->breakpoints[index].condition) { - return true; - } - bool error; - bool condition = debugger_evaluate(gb, gb->breakpoints[index].condition, - (unsigned)strlen(gb->breakpoints[index].condition), &error, NULL, NULL).value; - if (error) { - /* Should never happen */ - GB_log(gb, "An internal error has occured\n"); - return true; - } - return condition; - } - return false; -} - -static bool should_break(GB_gameboy_t *gb, uint16_t addr, bool jump_to) -{ - /* Try any-bank breakpoint */ - value_t full_addr = (VALUE_16(addr)); - if (_should_break(gb, full_addr, jump_to)) return true; - - /* Try bank-specific breakpoint */ - full_addr.has_bank = true; - full_addr.bank = bank_for_addr(gb, addr); - return _should_break(gb, full_addr, jump_to); -} - -static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - if (strlen(lstrip(arguments)) == 0) { - print_usage(gb, command); - return true; - } - - if (!modifiers || !modifiers[0]) { - modifiers = "a"; - } - else if (modifiers[1]) { - print_usage(gb, command); - return true; - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); - if (!error) { - switch (modifiers[0]) { - case 'a': - GB_log(gb, "=%s\n", debugger_value_to_string(gb, result, false)); - break; - case 'd': - GB_log(gb, "=%d\n", result.value); - break; - case 'x': - GB_log(gb, "=$%x\n", result.value); - break; - case 'o': - GB_log(gb, "=0%o\n", result.value); - break; - case 'b': - { - if (!result.value) { - GB_log(gb, "=%%0\n"); - break; - } - char binary[17]; - binary[16] = 0; - char *ptr = &binary[16]; - while (result.value) { - *(--ptr) = (result.value & 1)? '1' : '0'; - result.value >>= 1; - } - GB_log(gb, "=%%%s\n", ptr); - break; - } - default: - break; - } - } - return true; -} - -static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - if (strlen(lstrip(arguments)) == 0) { - print_usage(gb, command); - return true; - } - - bool error; - value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); - uint16_t count = 32; - - if (modifiers) { - char *end; - count = (uint16_t) (strtol(modifiers, &end, 10)); - if (*end) { - print_usage(gb, command); - return true; - } - } - - if (!error) { - if (addr.has_bank) { - banking_state_t old_state; - save_banking_state(gb, &old_state); - switch_banking_state(gb, addr.bank); - - while (count) { - GB_log(gb, "%02x:%04x: ", addr.bank, addr.value); - for (int i = 0; i < 16 && count; i++) { - GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i)); - count--; - } - addr.value += 16; - GB_log(gb, "\n"); - } - - restore_banking_state(gb, &old_state); - } - else { - while (count) { - GB_log(gb, "%04x: ", addr.value); - for (int i = 0; i < 16 && count; i++) { - GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i)); - count--; - } - addr.value += 16; - GB_log(gb, "\n"); - } - } - } - return true; -} - -static bool disassemble(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - if (strlen(lstrip(arguments)) == 0) { - arguments = "pc"; - } - - bool error; - value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL); - uint16_t count = 5; - - if (modifiers) { - char *end; - count = (uint16_t) (strtol(modifiers, &end, 10)); - if (*end) { - print_usage(gb, command); - return true; - } - } - - if (!error) { - if (addr.has_bank) { - banking_state_t old_state; - save_banking_state(gb, &old_state); - switch_banking_state(gb, addr.bank); - - GB_cpu_disassemble(gb, addr.value, count); - - restore_banking_state(gb, &old_state); - } - else { - GB_cpu_disassemble(gb, addr.value, count); - } - } - return true; -} - -static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - const GB_cartridge_t *cartridge = gb->cartridge_type; - - if (cartridge->has_ram) { - GB_log(gb, "Cartrdige includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size); - } - else { - GB_log(gb, "No cartridge RAM\n"); - } - - if (cartridge->mbc_type) { - static const char * const mapper_names[] = { - [GB_MBC1] = "MBC1", - [GB_MBC2] = "MBC2", - [GB_MBC3] = "MBC3", - [GB_MBC5] = "MBC5", - [GB_HUC1] = "HUC1", - [GB_HUC3] = "HUC3", - }; - GB_log(gb, "%s\n", mapper_names[cartridge->mbc_type]); - GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank); - if (cartridge->has_ram) { - GB_log(gb, "Current mapped RAM bank: %x\n", gb->mbc_ram_bank); - GB_log(gb, "RAM is curently %s\n", gb->mbc_ram_enable? "enabled" : "disabled"); - } - if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_STANDARD_MBC1_WIRING) { - GB_log(gb, "MBC1 banking mode is %s\n", gb->mbc1.mode == 1 ? "RAM" : "ROM"); - } - if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_MBC1M_WIRING) { - GB_log(gb, "MBC1 uses MBC1M wiring. \n"); - GB_log(gb, "Current mapped ROM0 bank: %x\n", gb->mbc_rom0_bank); - GB_log(gb, "MBC1 multicart banking mode is %s\n", gb->mbc1.mode == 1 ? "enabled" : "disabled"); - } - - } - else { - GB_log(gb, "No MBC\n"); - } - - if (cartridge->has_rumble) { - GB_log(gb, "Cart contains a rumble pak\n"); - } - - if (cartridge->has_rtc) { - GB_log(gb, "Cart contains a real time clock\n"); - } - - return true; -} - -static bool backtrace(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - GB_log(gb, " 1. %s\n", debugger_value_to_string(gb, (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}, true)); - for (unsigned i = gb->backtrace_size; i--;) { - GB_log(gb, "%3d. %s\n", gb->backtrace_size - i + 1, debugger_value_to_string(gb, (value_t){true, gb->backtrace_returns[i].bank, gb->backtrace_returns[i].addr}, true)); - } - - return true; -} - -static bool ticks(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - GB_log(gb, "Ticks: %lu. (Resetting)\n", gb->debugger_ticks); - gb->debugger_ticks = 0; - - return true; -} - - -static bool palettes(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - if (!GB_is_cgb(gb)) { - GB_log(gb, "Not available on a DMG.\n"); - return true; - } - - GB_log(gb, "Background palettes: \n"); - for (unsigned i = 0; i < 32; i++) { - GB_log(gb, "%04x ", ((uint16_t *)&gb->background_palettes_data)[i]); - if (i % 4 == 3) { - GB_log(gb, "\n"); - } - } - - GB_log(gb, "Sprites palettes: \n"); - for (unsigned i = 0; i < 32; i++) { - GB_log(gb, "%04x ", ((uint16_t *)&gb->sprite_palettes_data)[i]); - if (i % 4 == 3) { - GB_log(gb, "\n"); - } - } - - return true; -} - -static bool lcd(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - GB_log(gb, "LCDC:\n"); - GB_log(gb, " LCD enabled: %s\n",(gb->io_registers[GB_IO_LCDC] & 128)? "Enabled" : "Disabled"); - GB_log(gb, " %s: %s\n", (gb->cgb_mode? "Sprite priority flags" : "Background and Window"), - (gb->io_registers[GB_IO_LCDC] & 1)? "Enabled" : "Disabled"); - GB_log(gb, " Objects: %s\n", (gb->io_registers[GB_IO_LCDC] & 2)? "Enabled" : "Disabled"); - GB_log(gb, " Object size: %s\n", (gb->io_registers[GB_IO_LCDC] & 4)? "8x16" : "8x8"); - GB_log(gb, " Background tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 8)? "$9C00" : "$9800"); - GB_log(gb, " Background and Window Tileset: %s\n", (gb->io_registers[GB_IO_LCDC] & 16)? "$8000" : "$8800"); - GB_log(gb, " Window: %s\n", (gb->io_registers[GB_IO_LCDC] & 32)? "Enabled" : "Disabled"); - GB_log(gb, " Window tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 64)? "$9C00" : "$9800"); - - GB_log(gb, "\nSTAT:\n"); - static const char *modes[] = {"Mode 0, H-Blank", "Mode 1, V-Blank", "Mode 2, OAM", "Mode 3, Rendering"}; - GB_log(gb, " Current mode: %s\n", modes[gb->io_registers[GB_IO_STAT] & 3]); - GB_log(gb, " LYC flag: %s\n", (gb->io_registers[GB_IO_STAT] & 4)? "On" : "Off"); - GB_log(gb, " H-Blank interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 8)? "Enabled" : "Disabled"); - GB_log(gb, " V-Blank interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 16)? "Enabled" : "Disabled"); - GB_log(gb, " OAM interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 32)? "Enabled" : "Disabled"); - GB_log(gb, " LYC interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 64)? "Enabled" : "Disabled"); - - - - GB_log(gb, "\nCurrent line: %d\n", gb->current_line); - GB_log(gb, "Current state: "); - if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) { - GB_log(gb, "Off\n"); - } - else if (gb->display_state == 7 || gb->display_state == 8) { - GB_log(gb, "Reading OAM data (%d/40)\n", gb->display_state == 8? gb->oam_search_index : 0); - } - else if (gb->display_state <= 3 || gb->display_state == 24 || gb->display_state == 31) { - GB_log(gb, "Glitched line 0 OAM mode (%d cycles to next event)\n", -gb->display_cycles / 2); - } - else if (gb->mode_for_interrupt == 3) { - signed pixel = gb->position_in_line > 160? (int8_t) gb->position_in_line : gb->position_in_line; - GB_log(gb, "Rendering pixel (%d/160)\n", pixel); - } - else { - GB_log(gb, "Sleeping (%d cycles to next event)\n", -gb->display_cycles / 2); - } - GB_log(gb, "LY: %d\n", gb->io_registers[GB_IO_LY]); - GB_log(gb, "LYC: %d\n", gb->io_registers[GB_IO_LYC]); - GB_log(gb, "Window position: %d, %d\n", (signed) gb->io_registers[GB_IO_WX] - 7 , gb->io_registers[GB_IO_WY]); - GB_log(gb, "Interrupt line: %s\n", gb->stat_interrupt_line? "On" : "Off"); - - return true; -} - -static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - - GB_log(gb, "Current state: "); - if (!gb->apu.global_enable) { - GB_log(gb, "Disabled\n"); - } - else { - GB_log(gb, "Enabled\n"); - for (uint8_t channel = 0; channel < GB_N_CHANNELS; channel++) { - GB_log(gb, "CH%u is %s, DAC %s; current sample = 0x%x\n", channel + 1, - gb->apu.is_active[channel] ? "active " : "inactive", - GB_apu_is_DAC_enabled(gb, channel) ? "active " : "inactive", - gb->apu.samples[channel]); - } - } - - GB_log(gb, "SO1 (left output): volume %u,", gb->io_registers[GB_IO_NR50] & 0x07); - if (gb->io_registers[GB_IO_NR51] & 0x0f) { - for (uint8_t channel = 0, mask = 0x01; channel < GB_N_CHANNELS; channel++, mask <<= 1) { - if (gb->io_registers[GB_IO_NR51] & mask) { - GB_log(gb, " CH%u", channel + 1); - } - } - } - else { - GB_log(gb, " no channels"); - } - GB_log(gb, "%s\n", gb->io_registers[GB_IO_NR50] & 0x80 ? " VIN": ""); - - GB_log(gb, "SO2 (right output): volume %u,", gb->io_registers[GB_IO_NR50] & 0x70 >> 4); - if (gb->io_registers[GB_IO_NR51] & 0xf0) { - for (uint8_t channel = 0, mask = 0x10; channel < GB_N_CHANNELS; channel++, mask <<= 1) { - if (gb->io_registers[GB_IO_NR51] & mask) { - GB_log(gb, " CH%u", channel + 1); - } - } - } - else { - GB_log(gb, " no channels"); - } - GB_log(gb, "%s\n", gb->io_registers[GB_IO_NR50] & 0x80 ? " VIN": ""); - - - for (uint8_t channel = GB_SQUARE_1; channel <= GB_SQUARE_2; channel++) { - GB_log(gb, "\nCH%u:\n", channel + 1); - GB_log(gb, " Current volume: %u, current sample length: %u APU ticks (next in %u ticks)\n", - gb->apu.square_channels[channel].current_volume, - (gb->apu.square_channels[channel].sample_length ^ 0x7FF) * 2 + 1, - gb->apu.square_channels[channel].sample_countdown); - - uint8_t nrx2 = gb->io_registers[channel == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22]; - GB_log(gb, " %u 256 Hz ticks till next volume %screase (out of %u)\n", - gb->apu.square_channels[channel].volume_countdown, - nrx2 & 8 ? "in" : "de", - nrx2 & 7); - - uint8_t duty = gb->io_registers[channel == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6; - GB_log(gb, " Duty cycle %s%% (%s), current index %u/8%s\n", - duty > 3? "" : (const char *[]){"12.5", " 25", " 50", " 75"}[duty], - duty > 3? "" : (const char *[]){"_______-", "-______-", "-____---", "_------_"}[duty], - gb->apu.square_channels[channel].current_sample_index & 0x7f, - gb->apu.square_channels[channel].current_sample_index >> 7 ? " (suppressed)" : ""); - - if (channel == GB_SQUARE_1) { - GB_log(gb, " Frequency sweep %s and %s (next in %u APU ticks)\n", - gb->apu.sweep_enabled? "active" : "inactive", - gb->apu.sweep_decreasing? "decreasing" : "increasing", - gb->apu.square_sweep_calculate_countdown); - } - - if (gb->apu.square_channels[channel].length_enabled) { - GB_log(gb, " Channel will end in %u 256 Hz ticks\n", - gb->apu.square_channels[channel].pulse_length); - } - } - - - GB_log(gb, "\nCH3:\n"); - GB_log(gb, " Wave:"); - for (uint8_t i = 0; i < 32; i++) { - GB_log(gb, "%s%X", i%4?"":" ", gb->apu.wave_channel.wave_form[i]); - } - GB_log(gb, "\n"); - GB_log(gb, " Current position: %u\n", gb->apu.wave_channel.current_sample_index); - - GB_log(gb, " Volume %s (right-shifted %u times)\n", - gb->apu.wave_channel.shift > 4? "" : (const char *[]){"100%", "50%", "25%", "", "muted"}[gb->apu.wave_channel.shift], - gb->apu.wave_channel.shift); - - GB_log(gb, " Current sample length: %u APU ticks (next in %u ticks)\n", - gb->apu.wave_channel.sample_length ^ 0x7ff, - gb->apu.wave_channel.sample_countdown); - - if (gb->apu.wave_channel.length_enabled) { - GB_log(gb, " Channel will end in %u 256 Hz ticks\n", - gb->apu.wave_channel.pulse_length); - } - - - GB_log(gb, "\nCH4:\n"); - GB_log(gb, " Current volume: %u, current sample length: %u APU ticks (next in %u ticks)\n", - gb->apu.noise_channel.current_volume, - gb->apu.noise_channel.sample_length * 4 + 3, - gb->apu.noise_channel.sample_countdown); - - GB_log(gb, " %u 256 Hz ticks till next volume %screase (out of %u)\n", - gb->apu.noise_channel.volume_countdown, - gb->io_registers[GB_IO_NR42] & 8 ? "in" : "de", - gb->io_registers[GB_IO_NR42] & 7); - - GB_log(gb, " LFSR in %u-step mode, current value ", - gb->apu.noise_channel.narrow? 7 : 15); - for (uint16_t lfsr = gb->apu.noise_channel.lfsr, i = 15; i--; lfsr <<= 1) { - GB_log(gb, "%u%s", (lfsr >> 14) & 1, i%4 ? "" : " "); - } - - if (gb->apu.noise_channel.length_enabled) { - GB_log(gb, " Channel will end in %u 256 Hz ticks\n", - gb->apu.noise_channel.pulse_length); - } - - - GB_log(gb, "\n\nReminder: APU ticks are @ 2 MiHz\n"); - - return true; -} - -static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - if (strlen(lstrip(arguments)) || (modifiers && !strchr("fcl", modifiers[0]))) { - print_usage(gb, command); - return true; - } - - uint8_t shift_amount = 1, mask; - if (modifiers) { - switch(modifiers[0]) { - case 'c': - shift_amount = 2; - break; - case 'l': - shift_amount = 8; - break; - } - } - mask = (0xf << (shift_amount - 1)) & 0xf; - - for (int8_t cur_val = 0xf & mask; cur_val >= 0; cur_val -= shift_amount) { - for (uint8_t i = 0; i < 32; i++) { - if ((gb->apu.wave_channel.wave_form[i] & mask) == cur_val) { - GB_log(gb, "%X", gb->apu.wave_channel.wave_form[i]); - } - else { - GB_log(gb, "%c", i%4 == 2 ? '-' : ' '); - } - } - GB_log(gb, "\n"); - } - - return true; -} - -static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command); - -#define HELP_NEWLINE "\n " - -/* Commands without implementations are aliases of the previous non-alias commands */ -static const debugger_command_t commands[] = { - {"continue", 1, cont, "Continue running until next stop"}, - {"next", 1, next, "Run the next instruction, skipping over function calls"}, - {"step", 1, step, "Run the next instruction, stepping into function calls"}, - {"finish", 1, finish, "Run until the current function returns"}, - {"backtrace", 2, backtrace, "Display the current call stack"}, - {"bt", 2, }, /* Alias */ - {"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected (Experimental)"}, - {"ticks", 2, ticks, "Display the number of CPU ticks since the last time 'ticks' was used"}, - {"registers", 1, registers, "Print values of processor registers and other important registers"}, - {"cartridge", 2, mbc, "Displays information about the MBC and cartridge"}, - {"mbc", 3, }, /* Alias */ - {"apu", 3, apu, "Displays information about the current state of the audio chip"}, - {"wave", 3, wave, "Prints a visual representation of the wave RAM." HELP_NEWLINE - "Modifiers can be used for a (f)ull print (the default)," HELP_NEWLINE - "a more (c)ompact one, or a one-(l)iner", "", "(f|c|l)"}, - {"lcd", 3, lcd, "Displays information about the current state of the LCD controller"}, - {"palettes", 3, palettes, "Displays the current CGB palettes"}, - {"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression" HELP_NEWLINE - "Can also modify the condition of existing breakpoints." HELP_NEWLINE - "If the j modifier is used, the breakpoint will occur just before" HELP_NEWLINE - "jumping to the target.", - "[ if ]", "j"}, - {"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[]"}, - {"watch", 1, watch, "Add a new watchpoint at the specified address/expression." HELP_NEWLINE - "Can also modify the condition and type of existing watchpoints." HELP_NEWLINE - "Default watchpoint type is write-only.", - "[ if ]", "(r|w|rw)"}, - {"unwatch", 3, unwatch, "Delete a watchpoint by its address, or all watchpoints", "[]"}, - {"list", 1, list, "List all set breakpoints and watchpoints"}, - {"print", 1, print, "Evaluate and print an expression" HELP_NEWLINE - "Use modifier to format as an address (a, default) or as a number in" HELP_NEWLINE - "decimal (d), hexadecimal (x), octal (o) or binary (b).", - "", "format"}, - {"eval", 2, }, /* Alias */ - {"examine", 2, examine, "Examine values at address", "", "count"}, - {"x", 1, }, /* Alias */ - {"disassemble", 1, disassemble, "Disassemble instructions at address", "", "count"}, - - - {"help", 1, help, "List available commands or show help for the specified command", "[]"}, - {NULL,}, /* Null terminator */ -}; - -static const debugger_command_t *find_command(const char *string) -{ - size_t length = strlen(string); - for (const debugger_command_t *command = commands; command->command; command++) { - if (command->min_length > length) continue; - if (memcmp(command->command, string, length) == 0) { /* Is a substring? */ - /* Aliases */ - while (!command->implementation) { - command--; - } - return command; - } - } - - return NULL; -} - -static void print_command_shortcut(GB_gameboy_t *gb, const debugger_command_t *command) -{ - GB_attributed_log(gb, GB_LOG_BOLD | GB_LOG_UNDERLINE, "%.*s", command->min_length, command->command); - GB_attributed_log(gb, GB_LOG_BOLD , "%s", command->command + command->min_length); -} - -static void print_command_description(GB_gameboy_t *gb, const debugger_command_t *command) -{ - print_command_shortcut(gb, command); - GB_log(gb, ": "); - GB_log(gb, (const char *)&" %s\n" + strlen(command->command), command->help_string); -} - -static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *ignored) -{ - const debugger_command_t *command = find_command(arguments); - if (command) { - print_command_description(gb, command); - GB_log(gb, "\n"); - print_usage(gb, command); - - command++; - if (command->command && !command->implementation) { /* Command has aliases*/ - GB_log(gb, "\nAliases: "); - do { - print_command_shortcut(gb, command); - GB_log(gb, " "); - command++; - } while (command->command && !command->implementation); - GB_log(gb, "\n"); - } - return true; - } - for (command = commands; command->command; command++) { - if (command->help_string) { - print_command_description(gb, command); - } - } - return true; -} - -void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr) -{ - /* Called just after the CPU calls a function/enters an interrupt/etc... */ - - if (gb->stack_leak_detection) { - if (gb->debug_call_depth >= sizeof(gb->sp_for_call_depth) / sizeof(gb->sp_for_call_depth[0])) { - GB_log(gb, "Potential stack overflow detected (Functions nest too much). \n"); - gb->debug_stopped = true; - } - else { - gb->sp_for_call_depth[gb->debug_call_depth] = gb->registers[GB_REGISTER_SP]; - gb->addr_for_call_depth[gb->debug_call_depth] = gb->pc; - } - } - - if (gb->backtrace_size < sizeof(gb->backtrace_sps) / sizeof(gb->backtrace_sps[0])) { - - while (gb->backtrace_size) { - if (gb->backtrace_sps[gb->backtrace_size - 1] < gb->registers[GB_REGISTER_SP]) { - gb->backtrace_size--; - } - else { - break; - } - } - - gb->backtrace_sps[gb->backtrace_size] = gb->registers[GB_REGISTER_SP]; - gb->backtrace_returns[gb->backtrace_size].bank = bank_for_addr(gb, call_addr); - gb->backtrace_returns[gb->backtrace_size].addr = call_addr; - gb->backtrace_size++; - } - - gb->debug_call_depth++; -} - -void GB_debugger_ret_hook(GB_gameboy_t *gb) -{ - /* Called just before the CPU runs ret/reti */ - - gb->debug_call_depth--; - - if (gb->stack_leak_detection) { - if (gb->debug_call_depth < 0) { - GB_log(gb, "Function finished without a stack leak.\n"); - gb->debug_stopped = true; - } - else { - if (gb->registers[GB_REGISTER_SP] != gb->sp_for_call_depth[gb->debug_call_depth]) { - GB_log(gb, "Stack leak detected for function %s!\n", value_to_string(gb, gb->addr_for_call_depth[gb->debug_call_depth], true)); - GB_log(gb, "SP is $%04x, should be $%04x.\n", gb->registers[GB_REGISTER_SP], - gb->sp_for_call_depth[gb->debug_call_depth]); - gb->debug_stopped = true; - } - } - } - - while (gb->backtrace_size) { - if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->registers[GB_REGISTER_SP]) { - gb->backtrace_size--; - } - else { - break; - } - } -} - -static bool _GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, value_t addr, uint8_t value) -{ - uint16_t index = find_watchpoint(gb, addr); - uint32_t key = WP_KEY(addr); - - if (index < gb->n_watchpoints && gb->watchpoints[index].key == key) { - if (!(gb->watchpoints[index].flags & GB_WATCHPOINT_W)) { - return false; - } - if (!gb->watchpoints[index].condition) { - gb->debug_stopped = true; - GB_log(gb, "Watchpoint: [%s] = $%02x\n", debugger_value_to_string(gb, addr, true), value); - return true; - } - bool error; - bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition, - (unsigned)strlen(gb->watchpoints[index].condition), &error, &addr.value, &value).value; - if (error) { - /* Should never happen */ - GB_log(gb, "An internal error has occured\n"); - return false; - } - if (condition) { - gb->debug_stopped = true; - GB_log(gb, "Watchpoint: [%s] = $%02x\n", debugger_value_to_string(gb, addr, true), value); - return true; - } - } - return false; -} - -void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (gb->debug_stopped) return; - - /* Try any-bank breakpoint */ - value_t full_addr = (VALUE_16(addr)); - if (_GB_debugger_test_write_watchpoint(gb, full_addr, value)) return; - - /* Try bank-specific breakpoint */ - full_addr.has_bank = true; - full_addr.bank = bank_for_addr(gb, addr); - _GB_debugger_test_write_watchpoint(gb, full_addr, value); -} - -static bool _GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, value_t addr) -{ - uint16_t index = find_watchpoint(gb, addr); - uint32_t key = WP_KEY(addr); - - if (index < gb->n_watchpoints && gb->watchpoints[index].key == key) { - if (!(gb->watchpoints[index].flags & GB_WATCHPOINT_R)) { - return false; - } - if (!gb->watchpoints[index].condition) { - gb->debug_stopped = true; - GB_log(gb, "Watchpoint: [%s]\n", debugger_value_to_string(gb, addr, true)); - return true; - } - bool error; - bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition, - (unsigned)strlen(gb->watchpoints[index].condition), &error, &addr.value, NULL).value; - if (error) { - /* Should never happen */ - GB_log(gb, "An internal error has occured\n"); - return false; - } - if (condition) { - gb->debug_stopped = true; - GB_log(gb, "Watchpoint: [%s]\n", debugger_value_to_string(gb, addr, true)); - return true; - } - } - return false; -} - -void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr) -{ - if (gb->debug_stopped) return; - - /* Try any-bank breakpoint */ - value_t full_addr = (VALUE_16(addr)); - if (_GB_debugger_test_read_watchpoint(gb, full_addr)) return; - - /* Try bank-specific breakpoint */ - full_addr.has_bank = true; - full_addr.bank = bank_for_addr(gb, addr); - _GB_debugger_test_read_watchpoint(gb, full_addr); -} - -/* Returns true if debugger waits for more commands */ -bool GB_debugger_execute_command(GB_gameboy_t *gb, char *input) -{ - if (!input[0]) { - return true; - } - - char *command_string = input; - char *arguments = strchr(input, ' '); - if (arguments) { - /* Actually "split" the string. */ - arguments[0] = 0; - arguments++; - } - else { - arguments = ""; - } - - char *modifiers = strchr(command_string, '/'); - if (modifiers) { - /* Actually "split" the string. */ - modifiers[0] = 0; - modifiers++; - } - - const debugger_command_t *command = find_command(command_string); - if (command) { - return command->implementation(gb, arguments, modifiers, command); - } - else { - GB_log(gb, "%s: no such command.\n", command_string); - return true; - } -} - -typedef enum { - JUMP_TO_NONE, - JUMP_TO_BREAK, - JUMP_TO_NONTRIVIAL, -} jump_to_return_t; - -static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *address); - -void GB_debugger_run(GB_gameboy_t *gb) -{ - if (gb->debug_disable) return; - - char *input = NULL; - if (gb->debug_next_command && gb->debug_call_depth <= 0) { - gb->debug_stopped = true; - } - if (gb->debug_fin_command && gb->debug_call_depth == -1) { - gb->debug_stopped = true; - } - if (gb->debug_stopped) { - GB_cpu_disassemble(gb, gb->pc, 5); - } -next_command: - if (input) { - free(input); - } - if (gb->breakpoints && !gb->debug_stopped && should_break(gb, gb->pc, false)) { - gb->debug_stopped = true; - GB_log(gb, "Breakpoint: PC = %s\n", value_to_string(gb, gb->pc, true)); - GB_cpu_disassemble(gb, gb->pc, 5); - } - - if (gb->breakpoints && !gb->debug_stopped) { - uint16_t address = 0; - jump_to_return_t jump_to_result = test_jump_to_breakpoints(gb, &address); - - bool should_delete_state = true; - if (gb->nontrivial_jump_state && should_break(gb, gb->pc, true)) { - if (gb->non_trivial_jump_breakpoint_occured) { - gb->non_trivial_jump_breakpoint_occured = false; - } - else { - gb->non_trivial_jump_breakpoint_occured = true; - GB_log(gb, "Jumping to breakpoint: PC = %s\n", value_to_string(gb, gb->pc, true)); - GB_cpu_disassemble(gb, gb->pc, 5); - GB_load_state_from_buffer(gb, gb->nontrivial_jump_state, -1); - gb->debug_stopped = true; - } - } - else if (jump_to_result == JUMP_TO_BREAK) { - gb->debug_stopped = true; - GB_log(gb, "Jumping to breakpoint: PC = %s\n", value_to_string(gb, address, true)); - GB_cpu_disassemble(gb, gb->pc, 5); - gb->non_trivial_jump_breakpoint_occured = false; - } - else if (jump_to_result == JUMP_TO_NONTRIVIAL) { - if (!gb->nontrivial_jump_state) { - gb->nontrivial_jump_state = malloc(GB_get_save_state_size(gb)); - } - GB_save_state_to_buffer(gb, gb->nontrivial_jump_state); - gb->non_trivial_jump_breakpoint_occured = false; - should_delete_state = false; - } - else { - gb->non_trivial_jump_breakpoint_occured = false; - } - - if (should_delete_state) { - if (gb->nontrivial_jump_state) { - free(gb->nontrivial_jump_state); - gb->nontrivial_jump_state = NULL; - } - } - } - - if (gb->debug_stopped && !gb->debug_disable) { - gb->debug_next_command = false; - gb->debug_fin_command = false; - gb->stack_leak_detection = false; - input = gb->input_callback(gb); - - if (input == NULL) { - /* Debugging is no currently available, continue running */ - gb->debug_stopped = false; - return; - } - - if (GB_debugger_execute_command(gb, input)) { - goto next_command; - } - - free(input); - } -} - -void GB_debugger_handle_async_commands(GB_gameboy_t *gb) -{ - char *input = NULL; - - while (gb->async_input_callback && (input = gb->async_input_callback(gb))) { - GB_debugger_execute_command(gb, input); - free(input); - } -} - -void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path) -{ - FILE *f = fopen(path, "r"); - if (!f) return; - - char *line = NULL; - size_t size = 0; - size_t length = 0; - while ((length = getline(&line, &size, f)) != -1) { - for (unsigned i = 0; i < length; i++) { - if (line[i] == ';' || line[i] == '\n' || line[i] == '\r') { - line[i] = 0; - length = i; - break; - } - } - if (length == 0) continue; - - unsigned bank, address; - char symbol[length]; - - if (sscanf(line, "%x:%x %s", &bank, &address, symbol) == 3) { - bank &= 0x1FF; - if (!gb->bank_symbols[bank]) { - gb->bank_symbols[bank] = GB_map_alloc(); - } - GB_bank_symbol_t *allocated_symbol = GB_map_add_symbol(gb->bank_symbols[bank], address, symbol); - if (allocated_symbol) { - GB_reversed_map_add_symbol(&gb->reversed_symbol_map, bank, allocated_symbol); - } - } - } - free(line); - fclose(f); -} - -void GB_debugger_clear_symbols(GB_gameboy_t *gb) -{ - for (int i = sizeof(gb->bank_symbols) / sizeof(gb->bank_symbols[0]); i--;) { - if (gb->bank_symbols[i]) { - GB_map_free(gb->bank_symbols[i]); - gb->bank_symbols[i] = 0; - } - } - for (int i = sizeof(gb->reversed_symbol_map.buckets) / sizeof(gb->reversed_symbol_map.buckets[0]); i--;) { - while (gb->reversed_symbol_map.buckets[i]) { - GB_symbol_t *next = gb->reversed_symbol_map.buckets[i]->next; - free(gb->reversed_symbol_map.buckets[i]); - gb->reversed_symbol_map.buckets[i] = next; - } - } -} - -const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr) -{ - uint16_t bank = bank_for_addr(gb, addr); - - const GB_bank_symbol_t *symbol = GB_map_find_symbol(gb->bank_symbols[bank], addr); - if (symbol) return symbol; - if (bank != 0) return GB_map_find_symbol(gb->bank_symbols[0], addr); /* Maybe the symbol incorrectly uses bank 0? */ - - return NULL; -} - -const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr) -{ - const GB_bank_symbol_t *symbol = GB_debugger_find_symbol(gb, addr); - if (symbol && symbol->addr == addr) return symbol->name; - return NULL; -} - -/* The public version of debugger_evaluate */ -bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank) -{ - bool error = false; - value_t value = debugger_evaluate(gb, string, strlen(string), &error, NULL, NULL); - if (result) { - *result = value.value; - } - if (result_bank) { - *result_bank = value.has_bank? value.value : -1; - } - return error; -} - -void GB_debugger_break(GB_gameboy_t *gb) -{ - gb->debug_stopped = true; -} - -bool GB_debugger_is_stopped(GB_gameboy_t *gb) -{ - return gb->debug_stopped; -} - -void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled) -{ - gb->debug_disable = disabled; -} - -/* Jump-to breakpoints */ - -static bool is_in_trivial_memory(uint16_t addr) -{ - /* ROM */ - if (addr < 0x8000) { - return true; - } - - /* HRAM */ - if (addr >= 0xFF80 && addr < 0xFFFF) { - return true; - } - - /* RAM */ - if (addr >= 0xC000 && addr < 0xE000) { - return true; - } - - return false; -} - -typedef uint16_t GB_opcode_address_getter_t(GB_gameboy_t *gb, uint8_t opcode); - -uint16_t trivial_1(GB_gameboy_t *gb, uint8_t opcode) -{ - return gb->pc + 1; -} - -uint16_t trivial_2(GB_gameboy_t *gb, uint8_t opcode) -{ - return gb->pc + 2; -} - -uint16_t trivial_3(GB_gameboy_t *gb, uint8_t opcode) -{ - return gb->pc + 3; -} - -static uint16_t jr_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - return gb->pc + 2 + (int8_t)GB_read_memory(gb, gb->pc + 1); -} - -static bool condition_code(GB_gameboy_t *gb, uint8_t opcode) -{ - switch ((opcode >> 3) & 0x3) { - case 0: - return !(gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG); - case 1: - return (gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG); - case 2: - return !(gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG); - case 3: - return (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG); - } - - return false; -} - -static uint16_t jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - if (!condition_code(gb, opcode)) { - return gb->pc + 2; - } - - return gb->pc + 2 + (int8_t)GB_read_memory(gb, gb->pc + 1); -} - -static uint16_t ret(GB_gameboy_t *gb, uint8_t opcode) -{ - return GB_read_memory(gb, gb->registers[GB_REGISTER_SP]) | - (GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8); -} - - -static uint16_t ret_cc(GB_gameboy_t *gb, uint8_t opcode) -{ - if (condition_code(gb, opcode)) { - return ret(gb, opcode); - } - else { - return gb->pc + 1; - } -} - -static uint16_t jp_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - return GB_read_memory(gb, gb->pc + 1) | - (GB_read_memory(gb, gb->pc + 2) << 8); -} - -static uint16_t jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - if (condition_code(gb, opcode)) { - return jp_a16(gb, opcode); - } - else { - return gb->pc + 3; - } -} - -static uint16_t rst(GB_gameboy_t *gb, uint8_t opcode) -{ - return opcode ^ 0xC7; -} - -static uint16_t jp_hl(GB_gameboy_t *gb, uint8_t opcode) -{ - return gb->hl; -} - -static GB_opcode_address_getter_t *opcodes[256] = { - /* X0 X1 X2 X3 X4 X5 X6 X7 */ - /* X8 X9 Xa Xb Xc Xd Xe Xf */ - trivial_1, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 0X */ - trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, - trivial_2, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 1X */ - jr_r8, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, - jr_cc_r8, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 2X */ - jr_cc_r8, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, - jr_cc_r8, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 3X */ - jr_cc_r8, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 4X */ - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 5X */ - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 6X */ - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, NULL, trivial_1, /* 7X */ - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 8X */ - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 9X */ - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* aX */ - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* bX */ - trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, - ret_cc, trivial_1, jp_cc_a16, jp_a16, jp_cc_a16, trivial_1, trivial_2, rst, /* cX */ - ret_cc, ret, jp_cc_a16, trivial_2, jp_cc_a16, jp_a16, trivial_2, rst, - ret_cc, trivial_1, jp_cc_a16, NULL, jp_cc_a16, trivial_1, trivial_2, rst, /* dX */ - ret_cc, ret, jp_cc_a16, NULL, jp_cc_a16, NULL, trivial_2, rst, - trivial_2, trivial_1, trivial_1, NULL, NULL, trivial_1, trivial_2, rst, /* eX */ - trivial_2, jp_hl, trivial_3, NULL, NULL, NULL, trivial_2, rst, - trivial_2, trivial_1, trivial_1, trivial_1, NULL, trivial_1, trivial_2, rst, /* fX */ - trivial_2, trivial_1, trivial_3, trivial_1, NULL, NULL, trivial_2, rst, -}; - -static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *address) -{ - if (!gb->has_jump_to_breakpoints) return JUMP_TO_NONE; - - if (!is_in_trivial_memory(gb->pc) || !is_in_trivial_memory(gb->pc + 2) || - !is_in_trivial_memory(gb->registers[GB_REGISTER_SP]) || !is_in_trivial_memory(gb->registers[GB_REGISTER_SP] + 1)) { - return JUMP_TO_NONTRIVIAL; - } - - /* Interrupts */ - if (gb->ime) { - for (unsigned i = 0; i < 5; i++) { - if ((gb->interrupt_enable & (1 << i)) && (gb->io_registers[GB_IO_IF] & (1 << i))) { - if (should_break(gb, 0x40 + i * 8, true)) { - if (address) { - *address = 0x40 + i * 8; - } - return JUMP_TO_BREAK; - } - } - } - } - - uint16_t n_watchpoints = gb->n_watchpoints; - gb->n_watchpoints = 0; - - uint8_t opcode = GB_read_memory(gb, gb->pc); - - if (opcode == 0x76) { - gb->n_watchpoints = n_watchpoints; - if (gb->ime) { /* Already handled in above */ - return JUMP_TO_NONE; - } - - if (gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F) { - return JUMP_TO_NONTRIVIAL; /* HALT bug could occur */ - } - - return JUMP_TO_NONE; - } - - GB_opcode_address_getter_t *getter = opcodes[opcode]; - if (!getter) { - gb->n_watchpoints = n_watchpoints; - return JUMP_TO_NONE; - } - - uint16_t new_pc = getter(gb, opcode); - - gb->n_watchpoints = n_watchpoints; - - if (address) { - *address = new_pc; - } - - return should_break(gb, new_pc, true) ? JUMP_TO_BREAK : JUMP_TO_NONE; -} diff --git a/bsnes/gb/Core-new/debugger.h b/bsnes/gb/Core-new/debugger.h deleted file mode 100644 index 2906ad9f..00000000 --- a/bsnes/gb/Core-new/debugger.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef debugger_h -#define debugger_h -#include -#include -#include "gb_struct_def.h" -#include "symbol_hash.h" - - -#ifdef GB_INTERNAL -#ifdef DISABLE_DEBUGGER -#define GB_debugger_run(gb) (void)0 -#define GB_debugger_handle_async_commands(gb) (void)0 -#define GB_debugger_ret_hook(gb) (void)0 -#define GB_debugger_call_hook(gb, addr) (void)addr -#define GB_debugger_test_write_watchpoint(gb, addr, value) ((void)addr, (void)value) -#define GB_debugger_test_read_watchpoint(gb, addr) (void)addr -#else -void GB_debugger_run(GB_gameboy_t *gb); -void GB_debugger_handle_async_commands(GB_gameboy_t *gb); -void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr); -void GB_debugger_ret_hook(GB_gameboy_t *gb); -void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value); -void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr); -const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr); -#endif /* DISABLE_DEBUGGER */ -#endif - -#ifdef GB_INTERNAL -bool /* Returns true if debugger waits for more commands. Not relevant for non-GB_INTERNAL */ -#else -void -#endif -GB_debugger_execute_command(GB_gameboy_t *gb, char *input); /* Destroys input. */ - - -void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path); -const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr); -bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */ -void GB_debugger_break(GB_gameboy_t *gb); -bool GB_debugger_is_stopped(GB_gameboy_t *gb); -void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled); -void GB_debugger_clear_symbols(GB_gameboy_t *gb); -#endif /* debugger_h */ diff --git a/bsnes/gb/Core-new/display.c b/bsnes/gb/Core-new/display.c deleted file mode 100644 index 5c1935c6..00000000 --- a/bsnes/gb/Core-new/display.c +++ /dev/null @@ -1,1227 +0,0 @@ -#include -#include -#include -#include -#include "gb.h" - -/* FIFO functions */ - -static inline unsigned fifo_size(GB_fifo_t *fifo) -{ - return (fifo->write_end - fifo->read_end) & (GB_FIFO_LENGTH - 1); -} - -static void fifo_clear(GB_fifo_t *fifo) -{ - fifo->read_end = fifo->write_end = 0; -} - -static GB_fifo_item_t *fifo_pop(GB_fifo_t *fifo) -{ - GB_fifo_item_t *ret = &fifo->fifo[fifo->read_end]; - fifo->read_end++; - fifo->read_end &= (GB_FIFO_LENGTH - 1); - return ret; -} - -static void fifo_push_bg_row(GB_fifo_t *fifo, uint8_t lower, uint8_t upper, uint8_t palette, bool bg_priority, bool flip_x) -{ - if (!flip_x) { - UNROLL - for (unsigned i = 8; i--;) { - fifo->fifo[fifo->write_end] = (GB_fifo_item_t) { - (lower >> 7) | ((upper >> 7) << 1), - palette, - 0, - bg_priority, - }; - lower <<= 1; - upper <<= 1; - - fifo->write_end++; - fifo->write_end &= (GB_FIFO_LENGTH - 1); - } - } - else { - UNROLL - for (unsigned i = 8; i--;) { - fifo->fifo[fifo->write_end] = (GB_fifo_item_t) { - (lower & 1) | ((upper & 1) << 1), - palette, - 0, - bg_priority, - }; - lower >>= 1; - upper >>= 1; - - fifo->write_end++; - fifo->write_end &= (GB_FIFO_LENGTH - 1); - } - } -} - -static void fifo_overlay_object_row(GB_fifo_t *fifo, uint8_t lower, uint8_t upper, uint8_t palette, bool bg_priority, uint8_t priority, bool flip_x) -{ - while (fifo_size(fifo) < 8) { - fifo->fifo[fifo->write_end] = (GB_fifo_item_t) {0,}; - fifo->write_end++; - fifo->write_end &= (GB_FIFO_LENGTH - 1); - } - - uint8_t flip_xor = flip_x? 0: 0x7; - - UNROLL - for (unsigned i = 8; i--;) { - uint8_t pixel = (lower >> 7) | ((upper >> 7) << 1); - GB_fifo_item_t *target = &fifo->fifo[(fifo->read_end + (i ^ flip_xor)) & (GB_FIFO_LENGTH - 1)]; - if (pixel != 0 && (target->pixel == 0 || target->priority > priority)) { - target->pixel = pixel; - target->palette = palette; - target->bg_priority = bg_priority; - target->priority = priority; - } - lower <<= 1; - upper <<= 1; - } -} - - -/* - Each line is 456 cycles. Without scrolling, sprites or a window: - Mode 2 - 80 cycles / OAM Transfer - Mode 3 - 172 cycles / Rendering - Mode 0 - 204 cycles / HBlank - - Mode 1 is VBlank - */ - -#define MODE2_LENGTH (80) -#define LINE_LENGTH (456) -#define LINES (144) -#define WIDTH (160) -#define FRAME_LENGTH (LCDC_PERIOD) -#define VIRTUAL_LINES (FRAME_LENGTH / LINE_LENGTH) // = 154 - -typedef struct __attribute__((packed)) { - uint8_t y; - uint8_t x; - uint8_t tile; - uint8_t flags; -} GB_object_t; - -static bool window_enabled(GB_gameboy_t *gb) -{ - if ((gb->io_registers[GB_IO_LCDC] & 0x1) == 0) { - if (!gb->cgb_mode) { - return false; - } - } - return (gb->io_registers[GB_IO_LCDC] & 0x20) && gb->io_registers[GB_IO_WX] < 167; -} - -static void display_vblank(GB_gameboy_t *gb) -{ - gb->vblank_just_occured = true; - - /* TODO: Slow in turbo mode! */ - if (GB_is_hle_sgb(gb)) { - GB_sgb_render(gb); - } - - if (gb->turbo) { - if (GB_timing_sync_turbo(gb)) { - return; - } - } - - if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) { - /* LCD is off, set screen to white or black (if LCD is on in stop mode) */ - if (gb->sgb) { - for (unsigned i = 0; i < WIDTH * LINES; i++) { - gb->sgb->screen_buffer[i] = 0x0; - } - } - else { - uint32_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped && GB_is_cgb(gb) ? - gb->rgb_encode_callback(gb, 0, 0, 0) : - gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF); - for (unsigned i = 0; i < WIDTH * LINES; i++) { - gb ->screen[i] = color; - } - } - } - - gb->vblank_callback(gb); - GB_timing_sync(gb); -} - -static inline uint8_t scale_channel(uint8_t x) -{ - return (x << 3) | (x >> 2); -} - -static inline uint8_t scale_channel_with_curve(uint8_t x) -{ - return (uint8_t[]){0,2,4,7,12,18,25,34,42,52,62,73,85,97,109,121,134,146,158,170,182,193,203,213,221,230,237,243,248,251,253,255}[x]; -} - -static inline uint8_t scale_channel_with_curve_agb(uint8_t x) -{ - return (uint8_t[]){0,2,5,10,15,20,26,32,38,45,52,60,68,76,84,92,101,110,119,128,138,148,158,168,178,189,199,210,221,232,244,255}[x]; -} - -static inline uint8_t scale_channel_with_curve_sgb(uint8_t x) -{ - return (uint8_t[]){0,2,5,9,15,20,27,34,42,50,58,67,76,85,94,104,114,123,133,143,153,163,173,182,192,202,211,220,229,238,247,255}[x]; -} - - -uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color) -{ - uint8_t r = (color) & 0x1F; - uint8_t g = (color >> 5) & 0x1F; - uint8_t b = (color >> 10) & 0x1F; - - if (gb->color_correction_mode == GB_COLOR_CORRECTION_DISABLED) { - r = scale_channel(r); - g = scale_channel(g); - b = scale_channel(b); - } - else { - if (GB_is_sgb(gb)) { - return gb->rgb_encode_callback(gb, - scale_channel_with_curve_sgb(r), - scale_channel_with_curve_sgb(g), - scale_channel_with_curve_sgb(b)); - } - bool agb = gb->model == GB_MODEL_AGB; - r = agb? scale_channel_with_curve_agb(r) : scale_channel_with_curve(r); - g = agb? scale_channel_with_curve_agb(g) : scale_channel_with_curve(g); - b = agb? scale_channel_with_curve_agb(b) : scale_channel_with_curve(b); - - if (gb->color_correction_mode != GB_COLOR_CORRECTION_CORRECT_CURVES) { - uint8_t new_r, new_g, new_b; - if (agb) { - new_r = (r * 7 + g * 1) / 8; - new_g = (g * 3 + b * 1) / 4; - new_b = (b * 7 + r * 1) / 8; - } - else { - new_g = (g * 3 + b) / 4; - new_r = r; - new_b = b; - } - if (gb->color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) { - uint8_t old_max = MAX(r, MAX(g, b)); - uint8_t new_max = MAX(new_r, MAX(new_g, new_b)); - - if (new_max != 0) { - new_r = new_r * old_max / new_max; - new_g = new_g * old_max / new_max; - new_b = new_b * old_max / new_max; - } - - uint8_t old_min = MIN(r, MIN(g, b)); - uint8_t new_min = MIN(new_r, MIN(new_g, new_b)); - - if (new_min != 0xff) { - new_r = 0xff - (0xff - new_r) * (0xff - old_min) / (0xff - new_min); - new_g = 0xff - (0xff - new_g) * (0xff - old_min) / (0xff - new_min); - new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min); - } - } - r = new_r; - g = new_g; - b = new_b; - } - } - - return gb->rgb_encode_callback(gb, r, g, b); -} - -void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index) -{ - if (!gb->rgb_encode_callback || !GB_is_cgb(gb)) return; - uint8_t *palette_data = background_palette? gb->background_palettes_data : gb->sprite_palettes_data; - uint16_t color = palette_data[index & ~1] | (palette_data[index | 1] << 8); - - (background_palette? gb->background_palettes_rgb : gb->sprite_palettes_rgb)[index / 2] = GB_convert_rgb15(gb, color); -} - -void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode) -{ - gb->color_correction_mode = mode; - if (GB_is_cgb(gb)) { - for (unsigned i = 0; i < 32; i++) { - GB_palette_changed(gb, false, i * 2); - GB_palette_changed(gb, true, i * 2); - } - } -} - -/* - STAT interrupt is implemented based on this finding: - http://board.byuu.org/phpbb3/viewtopic.php?p=25527#p25531 - - General timing is based on GiiBiiAdvance's documents: - https://github.com/AntonioND/giibiiadvance - - */ - -void GB_STAT_update(GB_gameboy_t *gb) -{ - if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) return; - - bool previous_interrupt_line = gb->stat_interrupt_line; - /* Set LY=LYC bit */ - /* TODO: This behavior might not be correct for CGB revisions other than C and E */ - if (gb->ly_for_comparison != (uint16_t)-1 || gb->model <= GB_MODEL_CGB_C) { - if (gb->ly_for_comparison == gb->io_registers[GB_IO_LYC]) { - gb->lyc_interrupt_line = true; - gb->io_registers[GB_IO_STAT] |= 4; - } - else { - if (gb->ly_for_comparison != (uint16_t)-1) { - gb->lyc_interrupt_line = false; - } - gb->io_registers[GB_IO_STAT] &= ~4; - } - } - - switch (gb->mode_for_interrupt) { - case 0: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 8; break; - case 1: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x10; break; - case 2: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x20; break; - default: gb->stat_interrupt_line = false; - } - - /* User requested a LY=LYC interrupt and the LY=LYC bit is on */ - if ((gb->io_registers[GB_IO_STAT] & 0x40) && gb->lyc_interrupt_line) { - gb->stat_interrupt_line = true; - } - - if (gb->stat_interrupt_line && !previous_interrupt_line) { - gb->io_registers[GB_IO_IF] |= 2; - } -} - -void GB_lcd_off(GB_gameboy_t *gb) -{ - gb->display_state = 0; - gb->display_cycles = 0; - /* When the LCD is disabled, state is constant */ - - /* When the LCD is off, LY is 0 and STAT mode is 0. */ - gb->io_registers[GB_IO_LY] = 0; - gb->io_registers[GB_IO_STAT] &= ~3; - if (gb->hdma_on_hblank) { - gb->hdma_on_hblank = false; - gb->hdma_on = false; - - /* Todo: is this correct? */ - gb->hdma_steps_left = 0xff; - } - - gb->oam_read_blocked = false; - gb->vram_read_blocked = false; - gb->oam_write_blocked = false; - gb->vram_write_blocked = false; - gb->cgb_palettes_blocked = false; - - /* Reset window rendering state */ - gb->wy_diff = 0; - gb->window_disabled_while_active = false; - gb->current_line = 0; - gb->ly_for_comparison = 0; - - gb->accessed_oam_row = -1; -} - -static void add_object_from_index(GB_gameboy_t *gb, unsigned index) -{ - if (gb->n_visible_objs == 10) return; - - /* TODO: It appears that DMA blocks PPU access to OAM, but it needs verification. */ - if (gb->dma_steps_left && (gb->dma_cycles >= 0 || gb->is_dma_restarting)) { - return; - } - - /* This reverse sorts the visible objects by location and priority */ - GB_object_t *objects = (GB_object_t *) &gb->oam; - bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; - signed y = objects[index].y - 16; - if (y <= gb->current_line && y + (height_16? 16 : 8) > gb->current_line) { - unsigned j = 0; - for (; j < gb->n_visible_objs; j++) { - if (gb->obj_comparators[j] <= objects[index].x) break; - } - memmove(gb->visible_objs + j + 1, gb->visible_objs + j, gb->n_visible_objs - j); - memmove(gb->obj_comparators + j + 1, gb->obj_comparators + j, gb->n_visible_objs - j); - gb->visible_objs[j] = index; - gb->obj_comparators[j] = objects[index].x; - gb->n_visible_objs++; - } -} - -static void render_pixel_if_possible(GB_gameboy_t *gb) -{ - GB_fifo_item_t *fifo_item = NULL; - GB_fifo_item_t *oam_fifo_item = NULL; - bool draw_oam = false; - bool bg_enabled = true, bg_priority = false; - - if (!gb->bg_fifo_paused) { - fifo_item = fifo_pop(&gb->bg_fifo); - bg_priority = fifo_item->bg_priority; - } - - if (!gb->oam_fifo_paused && fifo_size(&gb->oam_fifo)) { - oam_fifo_item = fifo_pop(&gb->oam_fifo); - /* Todo: Verify access timings */ - if (oam_fifo_item->pixel && (gb->io_registers[GB_IO_LCDC] & 2)) { - draw_oam = true; - bg_priority |= oam_fifo_item->bg_priority; - } - } - - /* Drop pixels for scrollings */ - if (gb->position_in_line >= 160 || gb->disable_rendering) { - gb->position_in_line++; - return; - } - if (gb->bg_fifo_paused) return; - - /* Mixing */ - - /* Todo: Verify access timings */ - if ((gb->io_registers[GB_IO_LCDC] & 0x1) == 0) { - if (gb->cgb_mode) { - bg_priority = false; - } - else { - bg_enabled = false; - } - } - - uint8_t icd_pixel = 0; - { - uint8_t pixel = bg_enabled? fifo_item->pixel : 0; - if (pixel && bg_priority) { - draw_oam = false; - } - if (!gb->cgb_mode) { - pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3); - } - if (gb->sgb) { - if (gb->current_lcd_line < LINES) { - gb->sgb->screen_buffer[gb->position_in_line + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel; - } - } - else if (gb->model & GB_MODEL_NO_SFC_BIT) { - if (gb->icd_pixel_callback) { - icd_pixel = pixel; - } - } - else { - gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel]; - } - } - - if (draw_oam) { - uint8_t pixel = oam_fifo_item->pixel; - if (!gb->cgb_mode) { - /* Todo: Verify access timings */ - pixel = ((gb->io_registers[oam_fifo_item->palette? GB_IO_OBP1 : GB_IO_OBP0] >> (pixel << 1)) & 3); - } - if (gb->sgb) { - if (gb->current_lcd_line < LINES) { - gb->sgb->screen_buffer[gb->position_in_line + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel; - } - } - else if (gb->model & GB_MODEL_NO_SFC_BIT) { - if (gb->icd_pixel_callback) { - icd_pixel = pixel; - //gb->icd_pixel_callback(gb, pixel); - } - } - else { - gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel]; - } - } - - if (gb->model & GB_MODEL_NO_SFC_BIT) { - if (gb->icd_pixel_callback) { - gb->icd_pixel_callback(gb, icd_pixel); - } - } - - gb->position_in_line++; -} - -/* All verified CGB timings are based on CGB CPU E. CGB CPUs >= D are known to have - slightly different timings than CPUs <= C. - - Todo: Add support to CPU C and older */ - -static inline uint8_t fetcher_y(GB_gameboy_t *gb) -{ - return gb->current_line + (gb->in_window? - gb->io_registers[GB_IO_WY] - gb->wy_diff : gb->io_registers[GB_IO_SCY]); -} - -static void advance_fetcher_state_machine(GB_gameboy_t *gb) -{ - typedef enum { - GB_FETCHER_GET_TILE, - GB_FETCHER_GET_TILE_DATA_LOWER, - GB_FETCHER_GET_TILE_DATA_HIGH, - GB_FETCHER_PUSH, - GB_FETCHER_SLEEP, - } fetcher_step_t; - - fetcher_step_t fetcher_state_machine [8] = { - GB_FETCHER_SLEEP, - GB_FETCHER_GET_TILE, - GB_FETCHER_SLEEP, - GB_FETCHER_GET_TILE_DATA_LOWER, - GB_FETCHER_SLEEP, - GB_FETCHER_GET_TILE_DATA_HIGH, - GB_FETCHER_SLEEP, - GB_FETCHER_PUSH, - }; - - switch (fetcher_state_machine[gb->fetcher_state]) { - case GB_FETCHER_GET_TILE: { - uint16_t map = 0x1800; - - /* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */ - if (gb->io_registers[GB_IO_LCDC] & 0x08 && !gb->in_window) { - map = 0x1C00; - } - else if (gb->io_registers[GB_IO_LCDC] & 0x40 && gb->in_window) { - map = 0x1C00; - } - - /* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */ - uint8_t y = fetcher_y(gb); - if (gb->model > GB_MODEL_CGB_C) { - /* This value is cached on the CGB-D and newer, so it cannot be used to mix tiles together */ - gb->fetcher_y = y; - } - gb->current_tile = gb->vram[map + gb->fetcher_x + y / 8 * 32]; - if (GB_is_cgb(gb)) { - /* The CGB actually accesses both the tile index AND the attributes in the same T-cycle. - This probably means the CGB has a 16-bit data bus for the VRAM. */ - gb->current_tile_attributes = gb->vram[map + gb->fetcher_x + y / 8 * 32 + 0x2000]; - } - gb->fetcher_x++; - gb->fetcher_x &= 0x1f; - } - gb->fetcher_state++; - break; - - case GB_FETCHER_GET_TILE_DATA_LOWER: { - uint8_t y_flip = 0; - uint16_t tile_address = 0; - uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb); - - /* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */ - if (gb->io_registers[GB_IO_LCDC] & 0x10) { - tile_address = gb->current_tile * 0x10; - } - else { - tile_address = (int8_t)gb->current_tile * 0x10 + 0x1000; - } - if (gb->current_tile_attributes & 8) { - tile_address += 0x2000; - } - if (gb->current_tile_attributes & 0x40) { - y_flip = 0x7; - } - gb->current_tile_data[0] = - gb->vram[tile_address + ((y & 7) ^ y_flip) * 2]; - } - gb->fetcher_state++; - break; - - case GB_FETCHER_GET_TILE_DATA_HIGH: { - /* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. - Additionally, on CGB-D and newer mixing two tiles by changing the tileset - bit mid-fetching causes a glitched mixing of the two, in comparison to the - more logical DMG version. */ - uint16_t tile_address = 0; - uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb); - - if (gb->io_registers[GB_IO_LCDC] & 0x10) { - tile_address = gb->current_tile * 0x10; - } - else { - tile_address = (int8_t)gb->current_tile * 0x10 + 0x1000; - } - if (gb->current_tile_attributes & 8) { - tile_address += 0x2000; - } - uint8_t y_flip = 0; - if (gb->current_tile_attributes & 0x40) { - y_flip = 0x7; - } - gb->current_tile_data[1] = - gb->vram[tile_address + ((y & 7) ^ y_flip) * 2 + 1]; - } - gb->fetcher_state++; - break; - - case GB_FETCHER_PUSH: { - if (fifo_size(&gb->bg_fifo) > 0) break; - fifo_push_bg_row(&gb->bg_fifo, gb->current_tile_data[0], gb->current_tile_data[1], - gb->current_tile_attributes & 7, gb->current_tile_attributes & 0x80, gb->current_tile_attributes & 0x20); - gb->bg_fifo_paused = false; - gb->oam_fifo_paused = false; - gb->fetcher_state++; - } - break; - - case GB_FETCHER_SLEEP: - { - gb->fetcher_state++; - } - break; - } - - gb->fetcher_state &= 7; -} - -/* - TODO: It seems that the STAT register's mode bits are always "late" by 4 T-cycles. - The PPU logic can be greatly simplified if that delay is simply emulated. - */ -void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) -{ - /* The PPU does not advance while in STOP mode on the DMG */ - if (gb->stopped && !GB_is_cgb(gb)) { - gb->cycles_in_stop_mode += cycles; - if (gb->cycles_in_stop_mode >= LCDC_PERIOD) { - gb->cycles_in_stop_mode -= LCDC_PERIOD; - display_vblank(gb); - } - return; - } - GB_object_t *objects = (GB_object_t *) &gb->oam; - - GB_STATE_MACHINE(gb, display, cycles, 2) { - GB_STATE(gb, display, 1); - GB_STATE(gb, display, 2); - // GB_STATE(gb, display, 3); - // GB_STATE(gb, display, 4); - // GB_STATE(gb, display, 5); - GB_STATE(gb, display, 6); - GB_STATE(gb, display, 7); - GB_STATE(gb, display, 8); - // GB_STATE(gb, display, 9); - GB_STATE(gb, display, 10); - GB_STATE(gb, display, 11); - GB_STATE(gb, display, 12); - GB_STATE(gb, display, 13); - GB_STATE(gb, display, 14); - GB_STATE(gb, display, 15); - GB_STATE(gb, display, 16); - GB_STATE(gb, display, 17); - // GB_STATE(gb, display, 19); - GB_STATE(gb, display, 20); - GB_STATE(gb, display, 21); - GB_STATE(gb, display, 22); - GB_STATE(gb, display, 23); - // GB_STATE(gb, display, 24); - GB_STATE(gb, display, 25); - GB_STATE(gb, display, 26); - GB_STATE(gb, display, 27); - GB_STATE(gb, display, 28); - GB_STATE(gb, display, 29); - GB_STATE(gb, display, 30); - // GB_STATE(gb, display, 31); - GB_STATE(gb, display, 32); - GB_STATE(gb, display, 33); - GB_STATE(gb, display, 34); - GB_STATE(gb, display, 35); - GB_STATE(gb, display, 36); - GB_STATE(gb, display, 37); - GB_STATE(gb, display, 38); - - } - - if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) { - while (true) { - GB_SLEEP(gb, display, 1, LCDC_PERIOD); - display_vblank(gb); - } - return; - } - - if (!GB_is_cgb(gb)) { - GB_SLEEP(gb, display, 23, 1); - } - - /* Handle mode 2 on the very first line 0 */ - gb->current_line = 0; - gb->ly_for_comparison = 0; - gb->io_registers[GB_IO_STAT] &= ~3; - gb->mode_for_interrupt = -1; - gb->oam_read_blocked = false; - gb->vram_read_blocked = false; - gb->oam_write_blocked = false; - gb->vram_write_blocked = false; - gb->cgb_palettes_blocked = false; - gb->cycles_for_line = MODE2_LENGTH - 4; - GB_STAT_update(gb); - GB_SLEEP(gb, display, 2, MODE2_LENGTH - 4); - - gb->oam_write_blocked = true; - gb->cycles_for_line += 2; - GB_STAT_update(gb); - GB_SLEEP(gb, display, 34, 2); - - gb->n_visible_objs = 0; - gb->cycles_for_line += 8; // Mode 0 is shorter on the first line 0, so we augment cycles_for_line by 8 extra cycles. - - gb->io_registers[GB_IO_STAT] &= ~3; - gb->io_registers[GB_IO_STAT] |= 3; - gb->mode_for_interrupt = 3; - - gb->oam_write_blocked = true; - gb->oam_read_blocked = true; - gb->vram_read_blocked = gb->cgb_double_speed; - gb->vram_write_blocked = gb->cgb_double_speed; - if (!GB_is_cgb(gb)) { - gb->vram_read_blocked = true; - gb->vram_write_blocked = true; - } - gb->cycles_for_line += 2; - GB_SLEEP(gb, display, 37, 2); - - gb->cgb_palettes_blocked = true; - gb->cycles_for_line += 2; - GB_SLEEP(gb, display, 38, 2); - - gb->vram_read_blocked = true; - gb->vram_write_blocked = true; - goto mode_3_start; - - - while (true) { - /* Lines 0 - 143 */ - for (; gb->current_line < LINES; gb->current_line++) { - gb->oam_write_blocked = GB_is_cgb(gb) && !gb->cgb_double_speed; - gb->accessed_oam_row = 0; - - GB_SLEEP(gb, display, 35, 2); - gb->oam_write_blocked = GB_is_cgb(gb); - - GB_SLEEP(gb, display, 6, 1); - gb->io_registers[GB_IO_LY] = gb->current_line; - gb->oam_read_blocked = true; - gb->ly_for_comparison = gb->current_line? -1 : 0; - - /* The OAM STAT interrupt occurs 1 T-cycle before STAT actually changes, except on line 0. - PPU glitch? */ - if (gb->current_line != 0) { - gb->mode_for_interrupt = 2; - gb->io_registers[GB_IO_STAT] &= ~3; - } - else if (!GB_is_cgb(gb)) { - gb->io_registers[GB_IO_STAT] &= ~3; - } - GB_STAT_update(gb); - - GB_SLEEP(gb, display, 7, 1); - - gb->io_registers[GB_IO_STAT] &= ~3; - gb->io_registers[GB_IO_STAT] |= 2; - gb->mode_for_interrupt = 2; - gb->oam_write_blocked = true; - gb->ly_for_comparison = gb->current_line; - GB_STAT_update(gb); - gb->mode_for_interrupt = -1; - GB_STAT_update(gb); - gb->n_visible_objs = 0; - - for (gb->oam_search_index = 0; gb->oam_search_index < 40; gb->oam_search_index++) { - if (GB_is_cgb(gb)) { - add_object_from_index(gb, gb->oam_search_index); - /* The CGB does not care about the accessed OAM row as there's no OAM bug */ - } - GB_SLEEP(gb, display, 8, 2); - if (!GB_is_cgb(gb)) { - add_object_from_index(gb, gb->oam_search_index); - gb->accessed_oam_row = (gb->oam_search_index & ~1) * 4 + 8; - } - if (gb->oam_search_index == 37) { - gb->vram_read_blocked = !GB_is_cgb(gb); - gb->vram_write_blocked = false; - gb->cgb_palettes_blocked = false; - gb->oam_write_blocked = GB_is_cgb(gb); - GB_STAT_update(gb); - } - } - gb->cycles_for_line = MODE2_LENGTH + 4; - - gb->accessed_oam_row = -1; - gb->io_registers[GB_IO_STAT] &= ~3; - gb->io_registers[GB_IO_STAT] |= 3; - gb->mode_for_interrupt = 3; - gb->vram_read_blocked = true; - gb->vram_write_blocked = true; - gb->cgb_palettes_blocked = false; - gb->oam_write_blocked = true; - gb->oam_read_blocked = true; - - GB_STAT_update(gb); - - - uint8_t idle_cycles = 3; - if (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C) { - idle_cycles = 2; - } - gb->cycles_for_line += idle_cycles; - GB_SLEEP(gb, display, 10, idle_cycles); - - gb->cgb_palettes_blocked = true; - gb->cycles_for_line += 2; - GB_SLEEP(gb, display, 32, 2); - mode_3_start: - - fifo_clear(&gb->bg_fifo); - fifo_clear(&gb->oam_fifo); - /* Fill the FIFO with 8 pixels of "junk", it's going to be dropped anyway. */ - fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false); - /* Todo: find out actual access time of SCX */ - gb->position_in_line = - (gb->io_registers[GB_IO_SCX] & 7) - 8; - - gb->fetcher_x = ((gb->io_registers[GB_IO_SCX]) / 8) & 0x1f; - gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7); - - - /* The actual rendering cycle */ - gb->fetcher_state = 0; - gb->bg_fifo_paused = false; - gb->oam_fifo_paused = false; - gb->in_window = false; - while (true) { - /* Handle objects */ - /* When the sprite enabled bit is off, this proccess is skipped entirely on the DMG, but not on the CGB. - On the CGB, this bit is checked only when the pixel is actually popped from the FIFO. */ - - while (gb->n_visible_objs != 0 && - (gb->position_in_line < 160 || gb->position_in_line >= (uint8_t)(-8)) && - gb->obj_comparators[gb->n_visible_objs - 1] < (uint8_t)(gb->position_in_line + 8)) { - gb->n_visible_objs--; - } - while (gb->n_visible_objs != 0 && - (gb->io_registers[GB_IO_LCDC] & 2 || GB_is_cgb(gb)) && - gb->obj_comparators[gb->n_visible_objs - 1] == (uint8_t)(gb->position_in_line + 8)) { - - while (gb->fetcher_state < 5) { - advance_fetcher_state_machine(gb); - gb->cycles_for_line++; - GB_SLEEP(gb, display, 27, 1); - } - - /* Todo: Measure if penalty occurs before or after waiting for the fetcher. */ - if (gb->extra_penalty_for_sprite_at_0 != 0) { - if (gb->obj_comparators[gb->n_visible_objs - 1] == 0) { - gb->cycles_for_line += gb->extra_penalty_for_sprite_at_0; - GB_SLEEP(gb, display, 28, gb->extra_penalty_for_sprite_at_0); - gb->extra_penalty_for_sprite_at_0 = 0; - } - } - - gb->cycles_for_line += 6; - GB_SLEEP(gb, display, 20, 6); - /* TODO: what does the PPU read if DMA is active? */ - GB_object_t *object = &objects[gb->visible_objs[gb->n_visible_objs - 1]]; - - bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; /* Todo: Which T-cycle actually reads this? */ - uint8_t tile_y = (gb->current_line - object->y) & (height_16? 0xF : 7); - - if (object->flags & 0x40) { /* Flip Y */ - tile_y ^= height_16? 0xF : 7; - } - - /* Todo: I'm not 100% sure an access to OAM can't trigger the OAM bug while we're accessing this */ - uint16_t line_address = (height_16? object->tile & 0xFE : object->tile) * 0x10 + tile_y * 2; - - if (gb->cgb_mode && (object->flags & 0x8)) { /* Use VRAM bank 2 */ - line_address += 0x2000; - } - - uint8_t palette = (object->flags & 0x10) ? 1 : 0; - if (gb->cgb_mode) { - palette = object->flags & 0x7; - } - - fifo_overlay_object_row(&gb->oam_fifo, - gb->vram[line_address], - gb->vram[line_address + 1], - palette, - object->flags & 0x80, - gb->cgb_mode? gb->visible_objs[gb->n_visible_objs - 1] : 0, - object->flags & 0x20); - - gb->n_visible_objs--; - } - - /* Handle window */ - /* Todo: Timing (Including penalty and access timings) not verified by test ROM */ - if (!gb->in_window && window_enabled(gb) && - gb->current_line >= gb->io_registers[GB_IO_WY] + gb->wy_diff && - (uint8_t)(gb->position_in_line + 7) == gb->io_registers[GB_IO_WX]) { - gb->in_window = true; - fifo_clear(&gb->bg_fifo); - gb->bg_fifo_paused = true; - gb->oam_fifo_paused = true; - gb->fetcher_x = 0; - gb->fetcher_state = 0; - } - - render_pixel_if_possible(gb); - advance_fetcher_state_machine(gb); - - if (gb->position_in_line == 160) break; - gb->cycles_for_line++; - GB_SLEEP(gb, display, 21, 1); - } - - if (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C) { - gb->cycles_for_line++; - GB_SLEEP(gb, display, 30, 1); - } - - if (!gb->cgb_double_speed) { - gb->io_registers[GB_IO_STAT] &= ~3; - gb->mode_for_interrupt = 0; - gb->oam_read_blocked = false; - gb->vram_read_blocked = false; - gb->oam_write_blocked = false; - gb->vram_write_blocked = false; - } - - gb->cycles_for_line++; - GB_SLEEP(gb, display, 22, 1); - - gb->io_registers[GB_IO_STAT] &= ~3; - gb->mode_for_interrupt = 0; - gb->oam_read_blocked = false; - gb->vram_read_blocked = false; - gb->oam_write_blocked = false; - gb->vram_write_blocked = false; - GB_STAT_update(gb); - - /* Todo: Measure this value */ - gb->cycles_for_line += 2; - GB_SLEEP(gb, display, 33, 2); - gb->cgb_palettes_blocked = !gb->cgb_double_speed; - - gb->cycles_for_line += 2; - GB_SLEEP(gb, display, 36, 2); - gb->cgb_palettes_blocked = false; - - gb->cycles_for_line += 8; - GB_SLEEP(gb, display, 25, 8); - - if (gb->hdma_on_hblank) { - gb->hdma_starting = true; - } - GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line); - gb->mode_for_interrupt = 2; - - // Todo: unverified timing - gb->current_lcd_line++; - if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) { - display_vblank(gb); - } - - if (gb->icd_hreset_callback) { - gb->icd_hreset_callback(gb); - } - } - - /* Lines 144 - 152 */ - for (; gb->current_line < VIRTUAL_LINES - 1; gb->current_line++) { - gb->io_registers[GB_IO_LY] = gb->current_line; - gb->ly_for_comparison = -1; - GB_SLEEP(gb, display, 26, 2); - if (gb->current_line == LINES) { - gb->mode_for_interrupt = 2; - } - GB_STAT_update(gb); - GB_SLEEP(gb, display, 12, 2); - gb->ly_for_comparison = gb->current_line; - - if (gb->current_line == LINES) { - /* Entering VBlank state triggers the OAM interrupt */ - gb->io_registers[GB_IO_STAT] &= ~3; - gb->io_registers[GB_IO_STAT] |= 1; - gb->io_registers[GB_IO_IF] |= 1; - gb->mode_for_interrupt = 2; - GB_STAT_update(gb); - gb->mode_for_interrupt = 1; - GB_STAT_update(gb); - - if (gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON) { - if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) { - display_vblank(gb); - } - gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED; - } - else { - gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED; - if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) { - display_vblank(gb); - } - } - } - - GB_STAT_update(gb); - GB_SLEEP(gb, display, 13, LINE_LENGTH - 4); - } - - /* TODO: Verified on SGB2 and CGB-E. Actual interrupt timings not tested. */ - /* Lines 153 */ - gb->io_registers[GB_IO_LY] = 153; - gb->ly_for_comparison = -1; - GB_STAT_update(gb); - GB_SLEEP(gb, display, 14, (gb->model > GB_MODEL_CGB_C)? 4: 6); - - if (!GB_is_cgb(gb)) { - gb->io_registers[GB_IO_LY] = 0; - } - gb->ly_for_comparison = 153; - GB_STAT_update(gb); - GB_SLEEP(gb, display, 15, (gb->model > GB_MODEL_CGB_C)? 4: 2); - - gb->io_registers[GB_IO_LY] = 0; - gb->ly_for_comparison = (gb->model > GB_MODEL_CGB_C)? 153 : -1; - GB_STAT_update(gb); - GB_SLEEP(gb, display, 16, 4); - - gb->ly_for_comparison = 0; - GB_STAT_update(gb); - GB_SLEEP(gb, display, 29, 12); /* Writing to LYC during this period on a CGB has side effects */ - GB_SLEEP(gb, display, 17, LINE_LENGTH - 24); - - - /* Reset window rendering state */ - gb->wy_diff = 0; - gb->window_disabled_while_active = false; - gb->current_line = 0; - // TODO: not the correct timing - gb->current_lcd_line = 0; - if (gb->icd_vreset_callback) { - gb->icd_vreset_callback(gb); - } - } -} - -void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index) -{ - uint32_t none_palette[4]; - uint32_t *palette = NULL; - - switch (GB_is_cgb(gb)? palette_type : GB_PALETTE_NONE) { - default: - case GB_PALETTE_NONE: - none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF); - none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA); - none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55); - none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 ); - palette = none_palette; - break; - case GB_PALETTE_BACKGROUND: - palette = gb->background_palettes_rgb + (4 * (palette_index & 7)); - break; - case GB_PALETTE_OAM: - palette = gb->sprite_palettes_rgb + (4 * (palette_index & 7)); - break; - } - - for (unsigned y = 0; y < 192; y++) { - for (unsigned x = 0; x < 256; x++) { - if (x >= 128 && !GB_is_cgb(gb)) { - *(dest++) = gb->background_palettes_rgb[0]; - continue; - } - uint16_t tile = (x % 128) / 8 + y / 8 * 16; - uint16_t tile_address = tile * 0x10 + (x >= 128? 0x2000 : 0); - uint8_t pixel = (((gb->vram[tile_address + (y & 7) * 2 ] >> ((~x)&7)) & 1 ) | - ((gb->vram[tile_address + (y & 7) * 2 + 1] >> ((~x)&7)) & 1) << 1); - - if (!gb->cgb_mode) { - if (palette_type == GB_PALETTE_BACKGROUND) { - pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3); - } - else if (!gb->cgb_mode) { - if (palette_type == GB_PALETTE_OAM) { - pixel = ((gb->io_registers[palette_index == 0? GB_IO_OBP0 : GB_IO_OBP1] >> (pixel << 1)) & 3); - } - } - } - - - *(dest++) = palette[pixel]; - } - } -} - -void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type) -{ - uint32_t none_palette[4]; - uint32_t *palette = NULL; - uint16_t map = 0x1800; - - switch (GB_is_cgb(gb)? palette_type : GB_PALETTE_NONE) { - case GB_PALETTE_NONE: - none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF); - none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA); - none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55); - none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 ); - palette = none_palette; - break; - case GB_PALETTE_BACKGROUND: - palette = gb->background_palettes_rgb + (4 * (palette_index & 7)); - break; - case GB_PALETTE_OAM: - palette = gb->sprite_palettes_rgb + (4 * (palette_index & 7)); - break; - case GB_PALETTE_AUTO: - break; - } - - if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb->io_registers[GB_IO_LCDC] & 0x08)) { - map = 0x1c00; - } - - if (tileset_type == GB_TILESET_AUTO) { - tileset_type = (gb->io_registers[GB_IO_LCDC] & 0x10)? GB_TILESET_8800 : GB_TILESET_8000; - } - - for (unsigned y = 0; y < 256; y++) { - for (unsigned x = 0; x < 256; x++) { - uint8_t tile = gb->vram[map + x/8 + y/8 * 32]; - uint16_t tile_address; - uint8_t attributes = 0; - - if (tileset_type == GB_TILESET_8800) { - tile_address = tile * 0x10; - } - else { - tile_address = (int8_t) tile * 0x10 + 0x1000; - } - - if (gb->cgb_mode) { - attributes = gb->vram[map + x/8 + y/8 * 32 + 0x2000]; - } - - if (attributes & 0x8) { - tile_address += 0x2000; - } - - uint8_t pixel = (((gb->vram[tile_address + (((attributes & 0x40)? ~y : y) & 7) * 2 ] >> (((attributes & 0x20)? x : ~x)&7)) & 1 ) | - ((gb->vram[tile_address + (((attributes & 0x40)? ~y : y) & 7) * 2 + 1] >> (((attributes & 0x20)? x : ~x)&7)) & 1) << 1); - - if (!gb->cgb_mode && (palette_type == GB_PALETTE_BACKGROUND || palette_type == GB_PALETTE_AUTO)) { - pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3); - } - - if (palette) { - *(dest++) = palette[pixel]; - } - else { - *(dest++) = gb->background_palettes_rgb[(attributes & 7) * 4 + pixel]; - } - } - } -} - -uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height) -{ - uint8_t count = 0; - *sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8; - uint8_t oam_to_dest_index[40] = {0,}; - for (unsigned y = 0; y < LINES; y++) { - GB_object_t *sprite = (GB_object_t *) &gb->oam; - uint8_t sprites_in_line = 0; - for (uint8_t i = 0; i < 40; i++, sprite++) { - int sprite_y = sprite->y - 16; - bool obscured = false; - // Is sprite not in this line? - if (sprite_y > y || sprite_y + *sprite_height <= y) continue; - if (++sprites_in_line == 11) obscured = true; - - GB_oam_info_t *info = NULL; - if (!oam_to_dest_index[i]) { - info = dest + count; - oam_to_dest_index[i] = ++count; - info->x = sprite->x; - info->y = sprite->y; - info->tile = *sprite_height == 16? sprite->tile & 0xFE : sprite->tile; - info->flags = sprite->flags; - info->obscured_by_line_limit = false; - info->oam_addr = 0xFE00 + i * sizeof(*sprite); - } - else { - info = dest + oam_to_dest_index[i] - 1; - } - info->obscured_by_line_limit |= obscured; - } - } - - for (unsigned i = 0; i < count; i++) { - uint16_t vram_address = dest[i].tile * 0x10; - uint8_t flags = dest[i].flags; - uint8_t palette = gb->cgb_mode? (flags & 7) : ((flags & 0x10)? 1 : 0); - if (GB_is_cgb(gb) && (flags & 0x8)) { - vram_address += 0x2000; - } - - for (unsigned y = 0; y < *sprite_height; y++) { - UNROLL - for (unsigned x = 0; x < 8; x++) { - uint8_t color = (((gb->vram[vram_address ] >> ((~x)&7)) & 1 ) | - ((gb->vram[vram_address + 1] >> ((~x)&7)) & 1) << 1 ); - - if (!gb->cgb_mode) { - color = (gb->io_registers[palette? GB_IO_OBP1:GB_IO_OBP0] >> (color << 1)) & 3; - } - dest[i].image[((flags & 0x20)?7-x:x) + ((flags & 0x40)?*sprite_height - 1 -y:y) * 8] = gb->sprite_palettes_rgb[palette * 4 + color]; - } - vram_address += 2; - } - } - return count; -} - -/* Called when a write might enable or disable the window */ -void GB_window_related_write(GB_gameboy_t *gb, uint8_t addr, uint8_t value) -{ - bool before = window_enabled(gb); - gb->io_registers[addr] = value; - bool after = window_enabled(gb); - - if (before != after && gb->current_line < LINES) { - /* Window was disabled or enabled outside of vblank */ - if (gb->current_line >= gb->io_registers[GB_IO_WY]) { - if (after) { - if (!gb->window_disabled_while_active) { - /* Window was turned on for the first time this frame while LY > WY, - should start window in the next line */ - gb->wy_diff = gb->current_line + 1 - gb->io_registers[GB_IO_WY]; - } - else { - gb->wy_diff += gb->current_line; - } - } - else { - gb->wy_diff -= gb->current_line; - gb->window_disabled_while_active = true; - } - } - } -} diff --git a/bsnes/gb/Core-new/display.h b/bsnes/gb/Core-new/display.h deleted file mode 100644 index 9d1d1b47..00000000 --- a/bsnes/gb/Core-new/display.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef display_h -#define display_h - -#include "gb.h" -#include -#include - -#ifdef GB_INTERNAL -void GB_display_run(GB_gameboy_t *gb, uint8_t cycles); -void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index); -void GB_window_related_write(GB_gameboy_t *gb, uint8_t addr, uint8_t value); -void GB_STAT_update(GB_gameboy_t *gb); -void GB_lcd_off(GB_gameboy_t *gb); -#endif - -typedef enum { - GB_PALETTE_NONE, - GB_PALETTE_BACKGROUND, - GB_PALETTE_OAM, - GB_PALETTE_AUTO, -} GB_palette_type_t; - -typedef enum { - GB_MAP_AUTO, - GB_MAP_9800, - GB_MAP_9C00, -} GB_map_type_t; - -typedef enum { - GB_TILESET_AUTO, - GB_TILESET_8800, - GB_TILESET_8000, -} GB_tileset_type_t; - -typedef struct { - uint32_t image[128]; - uint8_t x, y, tile, flags; - uint16_t oam_addr; - bool obscured_by_line_limit; -} GB_oam_info_t; - -typedef enum { - GB_COLOR_CORRECTION_DISABLED, - GB_COLOR_CORRECTION_CORRECT_CURVES, - GB_COLOR_CORRECTION_EMULATE_HARDWARE, - GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS, -} GB_color_correction_mode_t; - -void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index); -void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type); -uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height); -uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color); -void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode); -#endif /* display_h */ diff --git a/bsnes/gb/Core-new/gb.c b/bsnes/gb/Core-new/gb.c deleted file mode 100644 index 93ac9d72..00000000 --- a/bsnes/gb/Core-new/gb.c +++ /dev/null @@ -1,1077 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#include -#endif -#include "random.h" -#include "gb.h" - - -#ifdef DISABLE_REWIND -#define GB_rewind_free(...) -#define GB_rewind_push(...) -#endif - -void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args) -{ - char *string = NULL; - vasprintf(&string, fmt, args); - if (string) { - if (gb->log_callback) { - gb->log_callback(gb, string, attributes); - } - else { - /* Todo: Add ANSI escape sequences for attributed text */ - printf("%s", string); - } - } - free(string); -} - -void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - GB_attributed_logv(gb, attributes, fmt, args); - va_end(args); -} - -void GB_log(GB_gameboy_t *gb, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - GB_attributed_logv(gb, 0, fmt, args); - va_end(args); -} - -#ifndef DISABLE_DEBUGGER -static char *default_input_callback(GB_gameboy_t *gb) -{ - char *expression = NULL; - size_t size = 0; - - if (getline(&expression, &size, stdin) == -1) { - /* The user doesn't have STDIN or used ^D. We make sure the program keeps running. */ - GB_set_async_input_callback(gb, NULL); /* Disable async input */ - return strdup("c"); - } - - if (!expression) { - return strdup(""); - } - - size_t length = strlen(expression); - if (expression[length - 1] == '\n') { - expression[length - 1] = 0; - } - return expression; -} - -static char *default_async_input_callback(GB_gameboy_t *gb) -{ -#ifndef _WIN32 - fd_set set; - FD_ZERO(&set); - FD_SET(STDIN_FILENO, &set); - struct timeval time = {0,}; - if (select(1, &set, NULL, NULL, &time) == 1) { - if (feof(stdin)) { - GB_set_async_input_callback(gb, NULL); /* Disable async input */ - return NULL; - } - return default_input_callback(gb); - } -#endif - return NULL; -} -#endif - -void GB_init(GB_gameboy_t *gb, GB_model_t model) -{ - memset(gb, 0, sizeof(*gb)); - gb->model = model; - if (GB_is_cgb(gb)) { - gb->ram = malloc(gb->ram_size = 0x1000 * 8); - gb->vram = malloc(gb->vram_size = 0x2000 * 2); - } - else { - gb->ram = malloc(gb->ram_size = 0x2000); - gb->vram = malloc(gb->vram_size = 0x2000); - } - -#ifndef DISABLE_DEBUGGER - gb->input_callback = default_input_callback; - gb->async_input_callback = default_async_input_callback; -#endif - gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type - gb->clock_multiplier = 1.0; - - if (model & GB_MODEL_NO_SFC_BIT) { - /* Disable time syncing. Timing should be done by the SFC emulator. */ - gb->turbo = true; - } - - GB_reset(gb); -} - -GB_model_t GB_get_model(GB_gameboy_t *gb) -{ - return gb->model; -} - -void GB_free(GB_gameboy_t *gb) -{ - gb->magic = 0; - if (gb->ram) { - free(gb->ram); - } - if (gb->vram) { - free(gb->vram); - } - if (gb->mbc_ram) { - free(gb->mbc_ram); - } - if (gb->rom) { - free(gb->rom); - } - if (gb->breakpoints) { - free(gb->breakpoints); - } - if (gb->sgb) { - free(gb->sgb); - } - if (gb->nontrivial_jump_state) { - free(gb->nontrivial_jump_state); - } -#ifndef DISABLE_DEBUGGER - GB_debugger_clear_symbols(gb); -#endif - GB_rewind_free(gb); - memset(gb, 0, sizeof(*gb)); -} - -int GB_load_boot_rom(GB_gameboy_t *gb, const char *path) -{ - FILE *f = fopen(path, "rb"); - if (!f) { - GB_log(gb, "Could not open boot ROM: %s.\n", strerror(errno)); - return errno; - } - fread(gb->boot_rom, sizeof(gb->boot_rom), 1, f); - fclose(f); - return 0; -} - -void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size) -{ - if (size > sizeof(gb->boot_rom)) { - size = sizeof(gb->boot_rom); - } - memset(gb->boot_rom, 0xFF, sizeof(gb->boot_rom)); - memcpy(gb->boot_rom, buffer, size); -} - -int GB_load_rom(GB_gameboy_t *gb, const char *path) -{ - FILE *f = fopen(path, "rb"); - if (!f) { - GB_log(gb, "Could not open ROM: %s.\n", strerror(errno)); - return errno; - } - fseek(f, 0, SEEK_END); - gb->rom_size = (ftell(f) + 0x3FFF) & ~0x3FFF; /* Round to bank */ - /* And then round to a power of two */ - while (gb->rom_size & (gb->rom_size - 1)) { - /* I promise this works. */ - gb->rom_size |= gb->rom_size >> 1; - gb->rom_size++; - } - fseek(f, 0, SEEK_SET); - if (gb->rom) { - free(gb->rom); - } - gb->rom = malloc(gb->rom_size); - memset(gb->rom, 0xFF, gb->rom_size); /* Pad with 0xFFs */ - fread(gb->rom, 1, gb->rom_size, f); - fclose(f); - GB_configure_cart(gb); - - return 0; -} - -void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size) -{ - gb->rom_size = (size + 0x3fff) & ~0x3fff; - while (gb->rom_size & (gb->rom_size - 1)) { - gb->rom_size |= gb->rom_size >> 1; - gb->rom_size++; - } - if (gb->rom) { - free(gb->rom); - } - gb->rom = malloc(gb->rom_size); - memset(gb->rom, 0xff, gb->rom_size); - memcpy(gb->rom, buffer, size); - GB_configure_cart(gb); -} - -typedef struct { - uint8_t seconds; - uint8_t padding1[3]; - uint8_t minutes; - uint8_t padding2[3]; - uint8_t hours; - uint8_t padding3[3]; - uint8_t days; - uint8_t padding4[3]; - uint8_t high; - uint8_t padding5[3]; -} GB_vba_rtc_time_t; - -typedef union { - struct __attribute__((packed)) { - GB_rtc_time_t rtc_real; - time_t last_rtc_second; /* Platform specific endianess and size */ - } sameboy_legacy; - struct { - /* Used by VBA versions with 32-bit timestamp*/ - GB_vba_rtc_time_t rtc_real, rtc_latched; - uint32_t last_rtc_second; /* Always little endian */ - } vba32; - struct { - /* Used by BGB and VBA versions with 64-bit timestamp*/ - GB_vba_rtc_time_t rtc_real, rtc_latched; - uint64_t last_rtc_second; /* Always little endian */ - } vba64; -} GB_rtc_save_t; - -int GB_save_battery_size(GB_gameboy_t *gb) -{ - if (!gb->cartridge_type->has_battery) return 0; // Nothing to save. - if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */ - - GB_rtc_save_t rtc_save_size; - return gb->mbc_ram_size + (gb->cartridge_type->has_rtc ? sizeof(rtc_save_size.vba64) : 0); -} - -int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size) -{ - if (!gb->cartridge_type->has_battery) return 0; // Nothing to save. - if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */ - - if (size < GB_save_battery_size(gb)) return EIO; - - memcpy(buffer, gb->mbc_ram, gb->mbc_ram_size); - - if (gb->cartridge_type->has_rtc) { - GB_rtc_save_t rtc_save = {{{{0,}},},}; - rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds; - rtc_save.vba64.rtc_real.minutes = gb->rtc_real.minutes; - rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours; - rtc_save.vba64.rtc_real.days = gb->rtc_real.days; - rtc_save.vba64.rtc_real.high = gb->rtc_real.high; - rtc_save.vba64.rtc_latched.seconds = gb->rtc_latched.seconds; - rtc_save.vba64.rtc_latched.minutes = gb->rtc_latched.minutes; - rtc_save.vba64.rtc_latched.hours = gb->rtc_latched.hours; - rtc_save.vba64.rtc_latched.days = gb->rtc_latched.days; - rtc_save.vba64.rtc_latched.high = gb->rtc_latched.high; -#ifdef GB_BIG_ENDIAN - rtc_save.vba64.last_rtc_second = __builtin_bswap64(gb->last_rtc_second); -#else - rtc_save.vba64.last_rtc_second = gb->last_rtc_second; -#endif - memcpy(buffer + gb->mbc_ram_size, &rtc_save.vba64, sizeof(rtc_save.vba64)); - } - - errno = 0; - return errno; -} - -int GB_save_battery(GB_gameboy_t *gb, const char *path) -{ - if (!gb->cartridge_type->has_battery) return 0; // Nothing to save. - if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */ - FILE *f = fopen(path, "wb"); - if (!f) { - GB_log(gb, "Could not open battery save: %s.\n", strerror(errno)); - return errno; - } - - if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) { - fclose(f); - return EIO; - } - if (gb->cartridge_type->has_rtc) { - GB_rtc_save_t rtc_save = {{{{0,}},},}; - rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds; - rtc_save.vba64.rtc_real.minutes = gb->rtc_real.minutes; - rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours; - rtc_save.vba64.rtc_real.days = gb->rtc_real.days; - rtc_save.vba64.rtc_real.high = gb->rtc_real.high; - rtc_save.vba64.rtc_latched.seconds = gb->rtc_latched.seconds; - rtc_save.vba64.rtc_latched.minutes = gb->rtc_latched.minutes; - rtc_save.vba64.rtc_latched.hours = gb->rtc_latched.hours; - rtc_save.vba64.rtc_latched.days = gb->rtc_latched.days; - rtc_save.vba64.rtc_latched.high = gb->rtc_latched.high; -#ifdef GB_BIG_ENDIAN - rtc_save.vba64.last_rtc_second = __builtin_bswap64(gb->last_rtc_second); -#else - rtc_save.vba64.last_rtc_second = gb->last_rtc_second; -#endif - if (fwrite(&rtc_save.vba64, 1, sizeof(rtc_save.vba64), f) != sizeof(rtc_save.vba64)) { - fclose(f); - return EIO; - } - - } - - errno = 0; - fclose(f); - return errno; -} - -void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size) -{ - memcpy(gb->mbc_ram, buffer, MIN(gb->mbc_ram_size, size)); - if (size <= gb->mbc_ram_size) { - goto reset_rtc; - } - - GB_rtc_save_t rtc_save; - memcpy(&rtc_save, buffer + gb->mbc_ram_size, MIN(sizeof(rtc_save), size)); - switch (size - gb->mbc_ram_size) { - case sizeof(rtc_save.sameboy_legacy): - memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real)); - memcpy(&gb->rtc_latched, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real)); - gb->last_rtc_second = rtc_save.sameboy_legacy.last_rtc_second; - break; - - case sizeof(rtc_save.vba32): - gb->rtc_real.seconds = rtc_save.vba32.rtc_real.seconds; - gb->rtc_real.minutes = rtc_save.vba32.rtc_real.minutes; - gb->rtc_real.hours = rtc_save.vba32.rtc_real.hours; - gb->rtc_real.days = rtc_save.vba32.rtc_real.days; - gb->rtc_real.high = rtc_save.vba32.rtc_real.high; - gb->rtc_latched.seconds = rtc_save.vba32.rtc_latched.seconds; - gb->rtc_latched.minutes = rtc_save.vba32.rtc_latched.minutes; - gb->rtc_latched.hours = rtc_save.vba32.rtc_latched.hours; - gb->rtc_latched.days = rtc_save.vba32.rtc_latched.days; - gb->rtc_latched.high = rtc_save.vba32.rtc_latched.high; -#ifdef GB_BIG_ENDIAN - gb->last_rtc_second = __builtin_bswap32(rtc_save.vba32.last_rtc_second); -#else - gb->last_rtc_second = rtc_save.vba32.last_rtc_second; -#endif - break; - - case sizeof(rtc_save.vba64): - gb->rtc_real.seconds = rtc_save.vba64.rtc_real.seconds; - gb->rtc_real.minutes = rtc_save.vba64.rtc_real.minutes; - gb->rtc_real.hours = rtc_save.vba64.rtc_real.hours; - gb->rtc_real.days = rtc_save.vba64.rtc_real.days; - gb->rtc_real.high = rtc_save.vba64.rtc_real.high; - gb->rtc_latched.seconds = rtc_save.vba64.rtc_latched.seconds; - gb->rtc_latched.minutes = rtc_save.vba64.rtc_latched.minutes; - gb->rtc_latched.hours = rtc_save.vba64.rtc_latched.hours; - gb->rtc_latched.days = rtc_save.vba64.rtc_latched.days; - gb->rtc_latched.high = rtc_save.vba64.rtc_latched.high; -#ifdef GB_BIG_ENDIAN - gb->last_rtc_second = __builtin_bswap64(rtc_save.vba64.last_rtc_second); -#else - gb->last_rtc_second = rtc_save.vba64.last_rtc_second; -#endif - break; - - default: - goto reset_rtc; - } - if (gb->last_rtc_second > time(NULL)) { - /* We must reset RTC here, or it will not advance. */ - goto reset_rtc; - } - - if (gb->last_rtc_second < 852076800) { /* 1/1/97. There weren't any RTC games that time, - so if the value we read is lower it means it wasn't - really RTC data. */ - goto reset_rtc; - } - goto exit; -reset_rtc: - gb->last_rtc_second = time(NULL); - gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ -exit: - return; -} - -/* Loading will silently stop if the format is incomplete */ -void GB_load_battery(GB_gameboy_t *gb, const char *path) -{ - FILE *f = fopen(path, "rb"); - if (!f) { - return; - } - - if (fread(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) { - goto reset_rtc; - } - - GB_rtc_save_t rtc_save; - switch (fread(&rtc_save, 1, sizeof(rtc_save), f)) { - case sizeof(rtc_save.sameboy_legacy): - memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real)); - memcpy(&gb->rtc_latched, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real)); - gb->last_rtc_second = rtc_save.sameboy_legacy.last_rtc_second; - break; - - case sizeof(rtc_save.vba32): - gb->rtc_real.seconds = rtc_save.vba32.rtc_real.seconds; - gb->rtc_real.minutes = rtc_save.vba32.rtc_real.minutes; - gb->rtc_real.hours = rtc_save.vba32.rtc_real.hours; - gb->rtc_real.days = rtc_save.vba32.rtc_real.days; - gb->rtc_real.high = rtc_save.vba32.rtc_real.high; - gb->rtc_latched.seconds = rtc_save.vba32.rtc_latched.seconds; - gb->rtc_latched.minutes = rtc_save.vba32.rtc_latched.minutes; - gb->rtc_latched.hours = rtc_save.vba32.rtc_latched.hours; - gb->rtc_latched.days = rtc_save.vba32.rtc_latched.days; - gb->rtc_latched.high = rtc_save.vba32.rtc_latched.high; -#ifdef GB_BIG_ENDIAN - gb->last_rtc_second = __builtin_bswap32(rtc_save.vba32.last_rtc_second); -#else - gb->last_rtc_second = rtc_save.vba32.last_rtc_second; -#endif - break; - - case sizeof(rtc_save.vba64): - gb->rtc_real.seconds = rtc_save.vba64.rtc_real.seconds; - gb->rtc_real.minutes = rtc_save.vba64.rtc_real.minutes; - gb->rtc_real.hours = rtc_save.vba64.rtc_real.hours; - gb->rtc_real.days = rtc_save.vba64.rtc_real.days; - gb->rtc_real.high = rtc_save.vba64.rtc_real.high; - gb->rtc_latched.seconds = rtc_save.vba64.rtc_latched.seconds; - gb->rtc_latched.minutes = rtc_save.vba64.rtc_latched.minutes; - gb->rtc_latched.hours = rtc_save.vba64.rtc_latched.hours; - gb->rtc_latched.days = rtc_save.vba64.rtc_latched.days; - gb->rtc_latched.high = rtc_save.vba64.rtc_latched.high; -#ifdef GB_BIG_ENDIAN - gb->last_rtc_second = __builtin_bswap64(rtc_save.vba64.last_rtc_second); -#else - gb->last_rtc_second = rtc_save.vba64.last_rtc_second; -#endif - break; - - default: - goto reset_rtc; - } - if (gb->last_rtc_second > time(NULL)) { - /* We must reset RTC here, or it will not advance. */ - goto reset_rtc; - } - - if (gb->last_rtc_second < 852076800) { /* 1/1/97. There weren't any RTC games that time, - so if the value we read is lower it means it wasn't - really RTC data. */ - goto reset_rtc; - } - goto exit; -reset_rtc: - gb->last_rtc_second = time(NULL); - gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ -exit: - fclose(f); - return; -} - -uint8_t GB_run(GB_gameboy_t *gb) -{ - gb->vblank_just_occured = false; - - if (gb->sgb && gb->sgb->intro_animation < 140) { - /* On the SGB, the GB is halted after finishing the boot ROM. - Then, after the boot animation is almost done, it's reset. - Since the SGB HLE does not perform any header validity checks, - we just halt the CPU (with hacky code) until the correct time. - This ensures the Nintendo logo doesn't flash on screen, and - the game does "run in background" while the animation is playing. */ - GB_display_run(gb, 228); - gb->cycles_since_last_sync += 228; - return 228; - } - - GB_debugger_run(gb); - gb->cycles_since_run = 0; - GB_cpu_run(gb); - if (gb->vblank_just_occured) { - GB_rtc_run(gb); - GB_debugger_handle_async_commands(gb); - GB_rewind_push(gb); - } - return gb->cycles_since_run; -} - -uint64_t GB_run_frame(GB_gameboy_t *gb) -{ - /* Configure turbo temporarily, the user wants to handle FPS capping manually. */ - bool old_turbo = gb->turbo; - bool old_dont_skip = gb->turbo_dont_skip; - gb->turbo = true; - gb->turbo_dont_skip = true; - - gb->cycles_since_last_sync = 0; - while (true) { - GB_run(gb); - if (gb->vblank_just_occured) { - break; - } - } - gb->turbo = old_turbo; - gb->turbo_dont_skip = old_dont_skip; - return gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */ -} - -void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output) -{ - gb->screen = output; -} - -void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback) -{ - gb->vblank_callback = callback; -} - -void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback) -{ - gb->log_callback = callback; -} - -void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback) -{ -#ifndef DISABLE_DEBUGGER - if (gb->input_callback == default_input_callback) { - gb->async_input_callback = NULL; - } - gb->input_callback = callback; -#endif -} - -void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback) -{ -#ifndef DISABLE_DEBUGGER - gb->async_input_callback = callback; -#endif -} - - -void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback) -{ - if (!gb->rgb_encode_callback && !GB_is_cgb(gb)) { - gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] = - callback(gb, 0xFF, 0xFF, 0xFF); - gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] = - callback(gb, 0xAA, 0xAA, 0xAA); - gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] = - callback(gb, 0x55, 0x55, 0x55); - gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] = - callback(gb, 0, 0, 0); - } - - gb->rgb_encode_callback = callback; - - for (unsigned i = 0; i < 32; i++) { - GB_palette_changed(gb, true, i * 2); - GB_palette_changed(gb, false, i * 2); - } -} - -void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback) -{ - gb->infrared_callback = 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, long 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) -{ - gb->rumble_callback = callback; -} - -void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback) -{ - gb->serial_transfer_bit_start_callback = callback; -} - -void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback) -{ - gb->serial_transfer_bit_end_callback = callback; -} - -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] & 0x80; -} -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] <<= 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_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)); -} - -bool GB_is_inited(GB_gameboy_t *gb) -{ - return gb->magic == 'SAME'; -} - -bool GB_is_cgb(GB_gameboy_t *gb) -{ - return (gb->model & GB_MODEL_FAMILY_MASK) == GB_MODEL_CGB_FAMILY; -} - -bool GB_is_sgb(GB_gameboy_t *gb) -{ - return (gb->model & ~GB_MODEL_PAL_BIT & ~GB_MODEL_NO_SFC_BIT) == GB_MODEL_SGB || (gb->model & ~GB_MODEL_NO_SFC_BIT) == GB_MODEL_SGB2; -} - -bool GB_is_hle_sgb(GB_gameboy_t *gb) -{ - return (gb->model & ~GB_MODEL_PAL_BIT) == GB_MODEL_SGB || gb->model == GB_MODEL_SGB2; -} - -void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip) -{ - gb->turbo = on; - gb->turbo_dont_skip = no_frame_skip; -} - -void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled) -{ - gb->disable_rendering = disabled; -} - -void *GB_get_user_data(GB_gameboy_t *gb) -{ - return gb->user_data; -} - -void GB_set_user_data(GB_gameboy_t *gb, void *data) -{ - gb->user_data = data; -} - -static void reset_ram(GB_gameboy_t *gb) -{ - switch (gb->model) { - case GB_MODEL_CGB_E: - case GB_MODEL_AGB: /* Unverified */ - for (unsigned i = 0; i < gb->ram_size; i++) { - gb->ram[i] = GB_random(); - } - break; - - case GB_MODEL_DMG_B: - case GB_MODEL_SGB_NTSC: /* Unverified*/ - case GB_MODEL_SGB_PAL: /* Unverified */ - case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */ - case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */ - for (unsigned i = 0; i < gb->ram_size; i++) { - gb->ram[i] = GB_random(); - if (i & 0x100) { - gb->ram[i] &= GB_random(); - } - else { - gb->ram[i] |= GB_random(); - } - } - break; - - case GB_MODEL_SGB2: - case GB_MODEL_SGB2_NO_SFC: - for (unsigned i = 0; i < gb->ram_size; i++) { - gb->ram[i] = 0x55; - gb->ram[i] ^= GB_random() & GB_random() & GB_random(); - } - break; - - case GB_MODEL_CGB_C: - for (unsigned i = 0; i < gb->ram_size; i++) { - if ((i & 0x808) == 0x800 || (i & 0x808) == 0x008) { - gb->ram[i] = 0; - } - else { - gb->ram[i] = GB_random() | GB_random() | GB_random() | GB_random(); - } - } - break; - } - - /* HRAM */ - switch (gb->model) { - case GB_MODEL_CGB_C: - // case GB_MODEL_CGB_D: - case GB_MODEL_CGB_E: - case GB_MODEL_AGB: - for (unsigned i = 0; i < sizeof(gb->hram); i++) { - gb->hram[i] = GB_random(); - } - break; - - case GB_MODEL_DMG_B: - case GB_MODEL_SGB_NTSC: /* Unverified*/ - case GB_MODEL_SGB_PAL: /* Unverified */ - case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */ - case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */ - case GB_MODEL_SGB2: - case GB_MODEL_SGB2_NO_SFC: - for (unsigned i = 0; i < sizeof(gb->hram); i++) { - if (i & 1) { - gb->hram[i] = GB_random() | GB_random() | GB_random(); - } - else { - gb->hram[i] = GB_random() & GB_random() & GB_random(); - } - } - break; - } - - /* OAM */ - switch (gb->model) { - case GB_MODEL_CGB_C: - case GB_MODEL_CGB_E: - case GB_MODEL_AGB: - /* Zero'd out by boot ROM anyway*/ - break; - - case GB_MODEL_DMG_B: - case GB_MODEL_SGB_NTSC: /* Unverified */ - case GB_MODEL_SGB_PAL: /* Unverified */ - case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */ - case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */ - case GB_MODEL_SGB2: - case GB_MODEL_SGB2_NO_SFC: - for (unsigned i = 0; i < 8; i++) { - if (i & 2) { - gb->oam[i] = GB_random() & GB_random() & GB_random(); - } - else { - gb->oam[i] = GB_random() | GB_random() | GB_random(); - } - } - for (unsigned i = 8; i < sizeof(gb->oam); i++) { - gb->oam[i] = gb->oam[i - 8]; - } - break; - } - - /* Wave RAM */ - switch (gb->model) { - case GB_MODEL_CGB_C: - case GB_MODEL_CGB_E: - case GB_MODEL_AGB: - /* Initialized by CGB-A and newer, 0s in CGB-0*/ - break; - - case GB_MODEL_DMG_B: - case GB_MODEL_SGB_NTSC: /* Unverified*/ - case GB_MODEL_SGB_PAL: /* Unverified */ - case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */ - case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */ - case GB_MODEL_SGB2: - case GB_MODEL_SGB2_NO_SFC: { - uint8_t temp; - for (unsigned i = 0; i < GB_IO_WAV_END - GB_IO_WAV_START; i++) { - if (i & 1) { - temp = GB_random() & GB_random() & GB_random(); - } - else { - temp = GB_random() | GB_random() | GB_random(); - } - gb->apu.wave_channel.wave_form[i * 2] = temp >> 4; - gb->apu.wave_channel.wave_form[i * 2 + 1] = temp & 0xF; - gb->io_registers[GB_IO_WAV_START + i] = temp; - - } - break; - } - } - - for (unsigned i = 0; i < sizeof(gb->extra_oam); i++) { - gb->extra_oam[i] = GB_random(); - } - - if (GB_is_cgb(gb)) { - for (unsigned i = 0; i < 64; i++) { - gb->background_palettes_data[i] = GB_random(); /* Doesn't really matter as the boot ROM overrides it anyway*/ - gb->sprite_palettes_data[i] = GB_random(); - } - for (unsigned i = 0; i < 32; i++) { - GB_palette_changed(gb, true, i * 2); - GB_palette_changed(gb, false, i * 2); - } - } -} - -void GB_reset(GB_gameboy_t *gb) -{ - uint32_t mbc_ram_size = gb->mbc_ram_size; - GB_model_t model = gb->model; - memset(gb, 0, (size_t)GB_GET_SECTION((GB_gameboy_t *) 0, unsaved)); - gb->model = model; - gb->version = GB_STRUCT_VERSION; - - gb->mbc_rom_bank = 1; - gb->last_rtc_second = time(NULL); - gb->cgb_ram_bank = 1; - gb->io_registers[GB_IO_JOYP] = 0xCF; - gb->mbc_ram_size = mbc_ram_size; - if (GB_is_cgb(gb)) { - gb->ram_size = 0x1000 * 8; - gb->vram_size = 0x2000 * 2; - memset(gb->vram, 0, gb->vram_size); - gb->cgb_mode = true; - } - else { - gb->ram_size = 0x2000; - gb->vram_size = 0x2000; - memset(gb->vram, 0, gb->vram_size); - - if (gb->rgb_encode_callback) { - gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] = - gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF); - gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] = - gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA); - gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] = - gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55); - gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] = - gb->rgb_encode_callback(gb, 0, 0, 0); - } - } - reset_ram(gb); - - /* The serial interrupt always occur on the 0xF7th cycle of every 0x100 cycle since boot. */ - gb->serial_cycles = 0x100-0xF7; - gb->io_registers[GB_IO_SC] = 0x7E; - - /* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */ - gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = GB_is_cgb(gb)? 0x00 : 0xFF; - - gb->accessed_oam_row = -1; - - - if (GB_is_hle_sgb(gb)) { - if (!gb->sgb) { - gb->sgb = malloc(sizeof(*gb->sgb)); - } - memset(gb->sgb, 0, sizeof(*gb->sgb)); - memset(gb->sgb_intro_jingle_phases, 0, sizeof(gb->sgb_intro_jingle_phases)); - gb->sgb_intro_sweep_phase = 0; - gb->sgb_intro_sweep_previous_sample = 0; - gb->sgb->intro_animation = -10; - - gb->sgb->player_count = 1; - GB_sgb_load_default_data(gb); - - } - else { - if (gb->sgb) { - free(gb->sgb); - gb->sgb = NULL; - } - } - - /* Todo: Ugly, fixme, see comment in the timer state machine */ - gb->div_state = 3; - - GB_apu_update_cycles_per_sample(gb); - - if (gb->nontrivial_jump_state) { - free(gb->nontrivial_jump_state); - gb->nontrivial_jump_state = NULL; - } - - gb->magic = (uintptr_t)'SAME'; -} - -void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model) -{ - gb->model = model; - if (GB_is_cgb(gb)) { - gb->ram = realloc(gb->ram, gb->ram_size = 0x1000 * 8); - gb->vram = realloc(gb->vram, gb->vram_size = 0x2000 * 2); - } - else { - gb->ram = realloc(gb->ram, gb->ram_size = 0x2000); - gb->vram = realloc(gb->vram, gb->vram_size = 0x2000); - } - GB_rewind_free(gb); - GB_reset(gb); -} - -void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank) -{ - /* Set size and bank to dummy pointers if not set */ - size_t dummy_size; - uint16_t dummy_bank; - if (!size) { - size = &dummy_size; - } - - if (!bank) { - bank = &dummy_bank; - } - - - switch (access) { - case GB_DIRECT_ACCESS_ROM: - *size = gb->rom_size; - *bank = gb->mbc_rom_bank; - return gb->rom; - case GB_DIRECT_ACCESS_RAM: - *size = gb->ram_size; - *bank = gb->cgb_ram_bank; - return gb->ram; - case GB_DIRECT_ACCESS_CART_RAM: - *size = gb->mbc_ram_size; - *bank = gb->mbc_ram_bank; - return gb->mbc_ram; - case GB_DIRECT_ACCESS_VRAM: - *size = gb->vram_size; - *bank = gb->cgb_vram_bank; - return gb->vram; - case GB_DIRECT_ACCESS_HRAM: - *size = sizeof(gb->hram); - *bank = 0; - return &gb->hram; - case GB_DIRECT_ACCESS_IO: - *size = sizeof(gb->io_registers); - *bank = 0; - return &gb->io_registers; - case GB_DIRECT_ACCESS_BOOTROM: - *size = GB_is_cgb(gb)? sizeof(gb->boot_rom) : 0x100; - *bank = 0; - return &gb->boot_rom; - case GB_DIRECT_ACCESS_OAM: - *size = sizeof(gb->oam); - *bank = 0; - return &gb->oam; - case GB_DIRECT_ACCESS_BGP: - *size = sizeof(gb->background_palettes_data); - *bank = 0; - return &gb->background_palettes_data; - case GB_DIRECT_ACCESS_OBP: - *size = sizeof(gb->sprite_palettes_data); - *bank = 0; - return &gb->sprite_palettes_data; - case GB_DIRECT_ACCESS_IE: - *size = sizeof(gb->interrupt_enable); - *bank = 0; - return &gb->interrupt_enable; - default: - *size = 0; - *bank = 0; - return NULL; - } -} - -void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier) -{ - gb->clock_multiplier = multiplier; - GB_apu_update_cycles_per_sample(gb); -} - -uint32_t GB_get_clock_rate(GB_gameboy_t *gb) -{ - if (gb->model & GB_MODEL_PAL_BIT) { - return SGB_PAL_FREQUENCY * gb->clock_multiplier; - } - if ((gb->model & ~GB_MODEL_NO_SFC_BIT) == GB_MODEL_SGB) { - return SGB_NTSC_FREQUENCY * gb->clock_multiplier; - } - return CPU_FREQUENCY * gb->clock_multiplier; -} - -unsigned GB_get_screen_width(GB_gameboy_t *gb) -{ - return GB_is_hle_sgb(gb)? 256 : 160; -} - -unsigned GB_get_screen_height(GB_gameboy_t *gb) -{ - return GB_is_hle_sgb(gb)? 224 : 144; -} - -unsigned GB_get_player_count(GB_gameboy_t *gb) -{ - return GB_is_hle_sgb(gb)? gb->sgb->player_count : 1; -} - -void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback) -{ - gb->update_input_hint_callback = callback; -} - -double GB_get_usual_frame_rate(GB_gameboy_t *gb) -{ - return GB_get_clock_rate(gb) / (double)LCDC_PERIOD; -} - -void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback) -{ - gb->joyp_write_callback = callback; -} - -void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback) -{ - gb->icd_pixel_callback = callback; -} - -void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback) -{ - gb->icd_hreset_callback = callback; -} - - -void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback) -{ - gb->icd_vreset_callback = callback; -} diff --git a/bsnes/gb/Core-new/gb.h b/bsnes/gb/Core-new/gb.h deleted file mode 100644 index dbd9e165..00000000 --- a/bsnes/gb/Core-new/gb.h +++ /dev/null @@ -1,729 +0,0 @@ -#ifndef GB_h -#define GB_h -#define typeof __typeof__ -#include -#include -#include - -#include "gb_struct_def.h" -#include "save_state.h" - -#include "apu.h" -#include "camera.h" -#include "debugger.h" -#include "display.h" -#include "joypad.h" -#include "mbc.h" -#include "memory.h" -#include "printer.h" -#include "timing.h" -#include "rewind.h" -#include "sm83_cpu.h" -#include "symbol_hash.h" -#include "sgb.h" - -#define GB_STRUCT_VERSION 13 - -#define GB_MODEL_FAMILY_MASK 0xF00 -#define GB_MODEL_DMG_FAMILY 0x000 -#define GB_MODEL_MGB_FAMILY 0x100 -#define GB_MODEL_CGB_FAMILY 0x200 -#define GB_MODEL_PAL_BIT 0x1000 -#define GB_MODEL_NO_SFC_BIT 0x2000 - -#ifdef GB_INTERNAL -#if __clang__ -#define UNROLL _Pragma("unroll") -#elif __GNUC__ -#define UNROLL _Pragma("GCC unroll 8") -#else -#define UNROLL -#endif - -#endif - -#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#define GB_BIG_ENDIAN -#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define GB_LITTLE_ENDIAN -#else -#error Unable to detect endianess -#endif - -typedef union { - struct { - uint8_t seconds; - uint8_t minutes; - uint8_t hours; - uint8_t days; - uint8_t high; - }; - uint8_t data[5]; -} GB_rtc_time_t; - - -typedef enum { - // GB_MODEL_DMG_0 = 0x000, - // GB_MODEL_DMG_A = 0x001, - GB_MODEL_DMG_B = 0x002, - // GB_MODEL_DMG_C = 0x003, - GB_MODEL_SGB = 0x004, - GB_MODEL_SGB_NTSC = GB_MODEL_SGB, - GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT, - GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT, - GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC, - GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT, - // GB_MODEL_MGB = 0x100, - GB_MODEL_SGB2 = 0x101, - GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT, - // GB_MODEL_CGB_0 = 0x200, - // GB_MODEL_CGB_A = 0x201, - // GB_MODEL_CGB_B = 0x202, - GB_MODEL_CGB_C = 0x203, - // GB_MODEL_CGB_D = 0x204, - GB_MODEL_CGB_E = 0x205, - GB_MODEL_AGB = 0x206, -} GB_model_t; - -enum { - GB_REGISTER_AF, - GB_REGISTER_BC, - GB_REGISTER_DE, - GB_REGISTER_HL, - GB_REGISTER_SP, - GB_REGISTERS_16_BIT /* Count */ -}; - -/* Todo: Actually use these! */ -enum { - GB_CARRY_FLAG = 16, - GB_HALF_CARRY_FLAG = 32, - GB_SUBSTRACT_FLAG = 64, - GB_ZERO_FLAG = 128, -}; - -#define GB_MAX_IR_QUEUE 256 - -enum { - /* Joypad and Serial */ - GB_IO_JOYP = 0x00, // Joypad (R/W) - GB_IO_SB = 0x01, // Serial transfer data (R/W) - GB_IO_SC = 0x02, // Serial Transfer Control (R/W) - - /* Missing */ - - /* Timers */ - GB_IO_DIV = 0x04, // Divider Register (R/W) - GB_IO_TIMA = 0x05, // Timer counter (R/W) - GB_IO_TMA = 0x06, // Timer Modulo (R/W) - GB_IO_TAC = 0x07, // Timer Control (R/W) - - /* Missing */ - - GB_IO_IF = 0x0f, // Interrupt Flag (R/W) - - /* Sound */ - GB_IO_NR10 = 0x10, // Channel 1 Sweep register (R/W) - GB_IO_NR11 = 0x11, // Channel 1 Sound length/Wave pattern duty (R/W) - GB_IO_NR12 = 0x12, // Channel 1 Volume Envelope (R/W) - GB_IO_NR13 = 0x13, // Channel 1 Frequency lo (Write Only) - GB_IO_NR14 = 0x14, // Channel 1 Frequency hi (R/W) - /* NR20 does not exist */ - GB_IO_NR21 = 0x16, // Channel 2 Sound Length/Wave Pattern Duty (R/W) - GB_IO_NR22 = 0x17, // Channel 2 Volume Envelope (R/W) - GB_IO_NR23 = 0x18, // Channel 2 Frequency lo data (W) - GB_IO_NR24 = 0x19, // Channel 2 Frequency hi data (R/W) - GB_IO_NR30 = 0x1a, // Channel 3 Sound on/off (R/W) - GB_IO_NR31 = 0x1b, // Channel 3 Sound Length - GB_IO_NR32 = 0x1c, // Channel 3 Select output level (R/W) - GB_IO_NR33 = 0x1d, // Channel 3 Frequency's lower data (W) - GB_IO_NR34 = 0x1e, // Channel 3 Frequency's higher data (R/W) - /* NR40 does not exist */ - GB_IO_NR41 = 0x20, // Channel 4 Sound Length (R/W) - GB_IO_NR42 = 0x21, // Channel 4 Volume Envelope (R/W) - GB_IO_NR43 = 0x22, // Channel 4 Polynomial Counter (R/W) - GB_IO_NR44 = 0x23, // Channel 4 Counter/consecutive, Inital (R/W) - GB_IO_NR50 = 0x24, // Channel control / ON-OFF / Volume (R/W) - GB_IO_NR51 = 0x25, // Selection of Sound output terminal (R/W) - GB_IO_NR52 = 0x26, // Sound on/off - - /* Missing */ - - GB_IO_WAV_START = 0x30, // Wave pattern start - GB_IO_WAV_END = 0x3f, // Wave pattern end - - /* Graphics */ - GB_IO_LCDC = 0x40, // LCD Control (R/W) - GB_IO_STAT = 0x41, // LCDC Status (R/W) - GB_IO_SCY = 0x42, // Scroll Y (R/W) - GB_IO_SCX = 0x43, // Scroll X (R/W) - GB_IO_LY = 0x44, // LCDC Y-Coordinate (R) - GB_IO_LYC = 0x45, // LY Compare (R/W) - GB_IO_DMA = 0x46, // DMA Transfer and Start Address (W) - GB_IO_BGP = 0x47, // BG Palette Data (R/W) - Non CGB Mode Only - GB_IO_OBP0 = 0x48, // Object Palette 0 Data (R/W) - Non CGB Mode Only - GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only - GB_IO_WY = 0x4a, // Window Y Position (R/W) - GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W) - // Has some undocumented compatibility flags written at boot. - // Unfortunately it is not readable or writable after boot has finished, so research of this - // register is quite limited. The value written to this register, however, can be controlled - // in some cases. - GB_IO_DMG_EMULATION = 0x4c, - - /* General CGB features */ - GB_IO_KEY1 = 0x4d, // CGB Mode Only - Prepare Speed Switch - - /* Missing */ - - GB_IO_VBK = 0x4f, // CGB Mode Only - VRAM Bank - GB_IO_BIOS = 0x50, // Write to disable the BIOS mapping - - /* CGB DMA */ - GB_IO_HDMA1 = 0x51, // CGB Mode Only - New DMA Source, High - GB_IO_HDMA2 = 0x52, // CGB Mode Only - New DMA Source, Low - GB_IO_HDMA3 = 0x53, // CGB Mode Only - New DMA Destination, High - GB_IO_HDMA4 = 0x54, // CGB Mode Only - New DMA Destination, Low - GB_IO_HDMA5 = 0x55, // CGB Mode Only - New DMA Length/Mode/Start - - /* IR */ - GB_IO_RP = 0x56, // CGB Mode Only - Infrared Communications Port - - /* Missing */ - - /* CGB Paletts */ - GB_IO_BGPI = 0x68, // CGB Mode Only - Background Palette Index - GB_IO_BGPD = 0x69, // CGB Mode Only - Background Palette Data - GB_IO_OBPI = 0x6a, // CGB Mode Only - Sprite Palette Index - GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data - - // 1 is written for DMG ROMs on a CGB. Does not appear to have an effect. - GB_IO_DMG_EMULATION_INDICATION = 0x6c, // (FEh) Bit 0 (Read/Write) - - /* Missing */ - - GB_IO_SVBK = 0x70, // CGB Mode Only - WRAM Bank - GB_IO_UNKNOWN2 = 0x72, // (00h) - Bit 0-7 (Read/Write) - GB_IO_UNKNOWN3 = 0x73, // (00h) - Bit 0-7 (Read/Write) - GB_IO_UNKNOWN4 = 0x74, // (00h) - Bit 0-7 (Read/Write) - CGB Mode Only - GB_IO_UNKNOWN5 = 0x75, // (8Fh) - Bit 4-6 (Read/Write) - GB_IO_PCM_12 = 0x76, // Channels 1 and 2 amplitudes - GB_IO_PCM_34 = 0x77, // Channels 3 and 4 amplitudes - GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only -}; - -typedef enum { - GB_LOG_BOLD = 1, - GB_LOG_DASHED_UNDERLINE = 2, - GB_LOG_UNDERLINE = 4, - GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE -} GB_log_attributes; - -#ifdef GB_INTERNAL -#define LCDC_PERIOD 70224 -#define CPU_FREQUENCY 0x400000 -#define SGB_NTSC_FREQUENCY (21477272 / 5) -#define SGB_PAL_FREQUENCY (21281370 / 5) -#define DIV_CYCLES (0x100) -#define INTERNAL_DIV_CYCLES (0x40000) - -#if !defined(MIN) -#define MIN(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; }) -#endif - -#if !defined(MAX) -#define MAX(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; }) -#endif -#endif - -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, long cycles_since_last_update); -typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, bool rumble_on); -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 void (*GB_update_input_hint_callback_t)(GB_gameboy_t *gb); -typedef void (*GB_joyp_write_callback_t)(GB_gameboy_t *gb, uint8_t value); -typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row); -typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb); -typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb); - -typedef struct { - bool state; - long delay; -} GB_ir_queue_item_t; - -struct GB_breakpoint_s; -struct GB_watchpoint_s; - -typedef struct { - uint8_t pixel; // Color, 0-3 - uint8_t palette; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG) - uint8_t priority; // Sprite priority – 0 in DMG, OAM index in CGB - bool bg_priority; // For sprite FIFO – the BG priority bit. For the BG FIFO – the CGB attributes priority bit -} GB_fifo_item_t; - -#define GB_FIFO_LENGTH 16 -typedef struct { - GB_fifo_item_t fifo[GB_FIFO_LENGTH]; - uint8_t read_end; - uint8_t write_end; -} GB_fifo_t; - -/* When state saving, each section is dumped independently of other sections. - This allows adding data to the end of the section without worrying about future compatibility. - Some other changes might be "safe" as well. - This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64 - bit platforms. */ - -/* We make sure bool is 1 for cross-platform save state compatibility. */ -/* Todo: We might want to typedef our own bool if this prevents SameBoy from working on specific platforms. */ -_Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1"); - -#ifdef GB_INTERNAL -struct GB_gameboy_s { -#else -struct GB_gameboy_internal_s { -#endif - GB_SECTION(header, - /* The magic makes sure a state file is: - - Indeed a SameBoy state file. - - Has the same endianess has the current platform. */ - volatile uint32_t magic; - /* The version field makes sure we don't load save state files with a completely different structure. - This happens when struct fields are removed/resized in an backward incompatible manner. */ - uint32_t version; - ); - - GB_SECTION(core_state, - /* Registers */ - uint16_t pc; - union { - uint16_t registers[GB_REGISTERS_16_BIT]; - struct { - uint16_t af, - bc, - de, - hl, - sp; - }; - struct { -#ifdef GB_BIG_ENDIAN - uint8_t a, f, - b, c, - d, e, - h, l; -#else - uint8_t f, a, - c, b, - e, d, - l, h; -#endif - }; - - }; - uint8_t ime; - uint8_t interrupt_enable; - uint8_t cgb_ram_bank; - - /* CPU and General Hardware Flags*/ - GB_model_t model; - bool cgb_mode; - bool cgb_double_speed; - bool halted; - bool stopped; - bool boot_rom_finished; - bool ime_toggle; /* ei has delayed a effect.*/ - bool halt_bug; - bool just_halted; - - /* Misc state */ - bool infrared_input; - GB_printer_t printer; - uint8_t extra_oam[0xff00 - 0xfea0]; - uint32_t ram_size; // Different between CGB and DMG - ); - - /* DMA and HDMA */ - GB_SECTION(dma, - bool hdma_on; - bool hdma_on_hblank; - uint8_t hdma_steps_left; - int16_t hdma_cycles; // in 8MHz units - uint16_t hdma_current_src, hdma_current_dest; - - uint8_t dma_steps_left; - uint8_t dma_current_dest; - uint16_t dma_current_src; - int16_t dma_cycles; - bool is_dma_restarting; - uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */ - bool hdma_starting; - ); - - /* MBC */ - GB_SECTION(mbc, - uint16_t mbc_rom_bank; - uint8_t mbc_ram_bank; - uint32_t mbc_ram_size; - bool mbc_ram_enable; - union { - struct { - uint8_t bank_low:5; - uint8_t bank_high:2; - uint8_t mode:1; - } mbc1; - - struct { - uint8_t rom_bank:4; - } mbc2; - - struct { - uint8_t rom_bank:7; - uint8_t padding:1; - uint8_t ram_bank:4; - } mbc3; - - struct { - uint8_t rom_bank_low; - uint8_t rom_bank_high:1; - uint8_t ram_bank:4; - } mbc5; - - struct { - uint8_t bank_low:6; - uint8_t bank_high:3; - uint8_t mode:1; - } huc1; - - struct { - uint8_t rom_bank; - uint8_t ram_bank; - } huc3; - }; - uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */ - bool camera_registers_mapped; - uint8_t camera_registers[0x36]; - bool rumble_state; - ); - - - /* HRAM and HW Registers */ - GB_SECTION(hram, - uint8_t hram[0xFFFF - 0xFF80]; - uint8_t io_registers[0x80]; - ); - - /* Timing */ - GB_SECTION(timing, - GB_UNIT(display); - GB_UNIT(div); - uint16_t div_counter; - uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */ - uint16_t serial_cycles; - uint16_t serial_length; - uint8_t double_speed_alignment; - uint8_t serial_count; - ); - - /* APU */ - GB_SECTION(apu, - GB_apu_t apu; - ); - - /* RTC */ - GB_SECTION(rtc, - GB_rtc_time_t rtc_real, rtc_latched; - uint64_t last_rtc_second; - bool rtc_latch; - ); - - /* Video Display */ - GB_SECTION(video, - uint32_t vram_size; // Different between CGB and DMG - uint8_t cgb_vram_bank; - uint8_t oam[0xA0]; - uint8_t background_palettes_data[0x40]; - uint8_t sprite_palettes_data[0x40]; - uint8_t position_in_line; - bool stat_interrupt_line; - uint8_t effective_scx; - uint8_t wy_diff; - /* The LCDC will skip the first frame it renders after turning it on. - On the CGB, a frame is not skipped if the previous frame was skipped as well. - See https://www.reddit.com/r/EmuDev/comments/6exyxu/ */ - - /* TODO: Drop this and properly emulate the dropped vreset signal*/ - enum { - GB_FRAMESKIP_LCD_TURNED_ON, // On a DMG, the LCD renders a blank screen during this state, - // on a CGB, the previous frame is repeated (which might be - // blank if the LCD was off for more than a few cycles) - GB_FRAMESKIP_FIRST_FRAME_SKIPPED, // This state is 'skipped' when emulating a DMG - GB_FRAMESKIP_SECOND_FRAME_RENDERED, - } frame_skip_state; - bool oam_read_blocked; - bool vram_read_blocked; - bool oam_write_blocked; - bool vram_write_blocked; - bool window_disabled_while_active; - uint8_t current_line; - uint16_t ly_for_comparison; - GB_fifo_t bg_fifo, oam_fifo; - uint8_t fetcher_x; - uint8_t fetcher_y; - uint16_t cycles_for_line; - uint8_t current_tile; - uint8_t current_tile_attributes; - uint8_t current_tile_data[2]; - uint8_t fetcher_state; - bool bg_fifo_paused; - bool oam_fifo_paused; - bool in_window; - uint8_t visible_objs[10]; - uint8_t obj_comparators[10]; - uint8_t n_visible_objs; - uint8_t oam_search_index; - uint8_t accessed_oam_row; - uint8_t extra_penalty_for_sprite_at_0; - uint8_t mode_for_interrupt; - bool lyc_interrupt_line; - bool cgb_palettes_blocked; - uint8_t current_lcd_line; // The LCD can go out of sync since the vsync signal is skipped in some cases. - uint32_t cycles_in_stop_mode; - ); - - /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */ - /* This data is reserved on reset and must come last in the struct */ - GB_SECTION(unsaved, - /* ROM */ - uint8_t *rom; - uint32_t rom_size; - const GB_cartridge_t *cartridge_type; - enum { - GB_STANDARD_MBC1_WIRING, - GB_MBC1M_WIRING, - } mbc1_wiring; - - unsigned pending_cycles; - - /* Various RAMs */ - uint8_t *ram; - uint8_t *vram; - uint8_t *mbc_ram; - - /* I/O */ - uint32_t *screen; - uint32_t background_palettes_rgb[0x20]; - uint32_t sprite_palettes_rgb[0x20]; - GB_color_correction_mode_t color_correction_mode; - bool keys[4][GB_KEY_MAX]; - - /* Timing */ - uint64_t last_sync; - uint64_t cycles_since_last_sync; // In 8MHz units - - /* Audio */ - GB_apu_output_t apu_output; - - /* Callbacks */ - void *user_data; - GB_log_callback_t log_callback; - GB_input_callback_t input_callback; - GB_input_callback_t async_input_callback; - GB_rgb_encode_callback_t rgb_encode_callback; - GB_vblank_callback_t vblank_callback; - GB_infrared_callback_t infrared_callback; - 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_bit_start_callback_t serial_transfer_bit_start_callback; - GB_serial_transfer_bit_end_callback_t serial_transfer_bit_end_callback; - GB_update_input_hint_callback_t update_input_hint_callback; - GB_joyp_write_callback_t joyp_write_callback; - GB_icd_pixel_callback_t icd_pixel_callback; - GB_icd_vreset_callback_t icd_hreset_callback; - GB_icd_vreset_callback_t icd_vreset_callback; - GB_read_memory_callback_t read_memory_callback; - - /* IR */ - long cycles_since_ir_change; // In 8MHz units - long 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; - - /* Breakpoints */ - uint16_t n_breakpoints; - struct GB_breakpoint_s *breakpoints; - bool has_jump_to_breakpoints; - void *nontrivial_jump_state; - bool non_trivial_jump_breakpoint_occured; - - /* SLD (Todo: merge with backtrace) */ - bool stack_leak_detection; - int debug_call_depth; - uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */ - uint16_t addr_for_call_depth[0x200]; - - /* Backtrace */ - unsigned backtrace_size; - uint16_t backtrace_sps[0x200]; - struct { - uint16_t bank; - uint16_t addr; - } backtrace_returns[0x200]; - - /* Watchpoints */ - uint16_t n_watchpoints; - struct GB_watchpoint_s *watchpoints; - - /* Symbol tables */ - GB_symbol_map_t *bank_symbols[0x200]; - GB_reversed_symbol_map_t reversed_symbol_map; - - /* Ticks command */ - unsigned long debugger_ticks; - - /* Rewind */ -#define GB_REWIND_FRAMES_PER_KEY 255 - size_t rewind_buffer_length; - struct { - uint8_t *key_state; - uint8_t *compressed_states[GB_REWIND_FRAMES_PER_KEY]; - unsigned pos; - } *rewind_sequences; // lasts about 4 seconds - size_t rewind_pos; - - /* SGB - saved and allocated optionally */ - GB_sgb_t *sgb; - - double sgb_intro_jingle_phases[7]; - double sgb_intro_sweep_phase; - double sgb_intro_sweep_previous_sample; - - /* Misc */ - bool turbo; - bool turbo_dont_skip; - bool disable_rendering; - uint8_t boot_rom[0x900]; - bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank - uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units - double clock_multiplier; - ); -}; - -#ifndef GB_INTERNAL -struct GB_gameboy_s { - char __internal[sizeof(struct GB_gameboy_internal_s)]; -}; -#endif - - -#ifndef __printflike -/* Missing from Linux headers. */ -#define __printflike(fmtarg, firstvararg) \ -__attribute__((__format__ (__printf__, fmtarg, firstvararg))) -#endif - -void GB_init(GB_gameboy_t *gb, GB_model_t model); -bool GB_is_inited(GB_gameboy_t *gb); -bool GB_is_cgb(GB_gameboy_t *gb); -bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 -bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd -GB_model_t GB_get_model(GB_gameboy_t *gb); -void GB_free(GB_gameboy_t *gb); -void GB_reset(GB_gameboy_t *gb); -void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model); - -/* Returns the time passed, in 8MHz ticks. */ -uint8_t GB_run(GB_gameboy_t *gb); -/* Returns the time passed since the last frame, in nanoseconds */ -uint64_t GB_run_frame(GB_gameboy_t *gb); - -typedef enum { - GB_DIRECT_ACCESS_ROM, - GB_DIRECT_ACCESS_RAM, - GB_DIRECT_ACCESS_CART_RAM, - GB_DIRECT_ACCESS_VRAM, - GB_DIRECT_ACCESS_HRAM, - GB_DIRECT_ACCESS_IO, /* Warning: Some registers can only be read/written correctly via GB_memory_read/write. */ - GB_DIRECT_ACCESS_BOOTROM, - GB_DIRECT_ACCESS_OAM, - GB_DIRECT_ACCESS_BGP, - GB_DIRECT_ACCESS_OBP, - GB_DIRECT_ACCESS_IE, -} GB_direct_access_t; - -/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank - is returned at *bank, even if only a portion of the memory is banked. */ -void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank); - -void *GB_get_user_data(GB_gameboy_t *gb); -void GB_set_user_data(GB_gameboy_t *gb, void *data); - - - -int GB_load_boot_rom(GB_gameboy_t *gb, const char *path); -void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size); -int GB_load_rom(GB_gameboy_t *gb, const char *path); -void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size); - -int GB_save_battery_size(GB_gameboy_t *gb); -int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size); -int GB_save_battery(GB_gameboy_t *gb, const char *path); - -void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size); -void GB_load_battery(GB_gameboy_t *gb, const char *path); - -void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip); -void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled); - -void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3); -void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4); - -void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output); - -void GB_set_infrared_input(GB_gameboy_t *gb, bool state); -void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long 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); -void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback); -void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback); -void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback); -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_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback); - -/* These APIs are used when using internal clock */ -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 */ -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); - -/* For integration with SFC/SNES emulators */ -void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback); -void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback); -void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback); -void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback); - -#ifdef GB_INTERNAL -uint32_t GB_get_clock_rate(GB_gameboy_t *gb); -#endif -void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier); - -unsigned GB_get_screen_width(GB_gameboy_t *gb); -unsigned GB_get_screen_height(GB_gameboy_t *gb); -double GB_get_usual_frame_rate(GB_gameboy_t *gb); -unsigned GB_get_player_count(GB_gameboy_t *gb); - -#endif /* GB_h */ diff --git a/bsnes/gb/Core-new/gb_struct_def.h b/bsnes/gb/Core-new/gb_struct_def.h deleted file mode 100644 index 0e0ebd12..00000000 --- a/bsnes/gb/Core-new/gb_struct_def.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef gb_struct_def_h -#define gb_struct_def_h -struct GB_gameboy_s; -typedef struct GB_gameboy_s GB_gameboy_t; -#endif diff --git a/bsnes/gb/Core-new/joypad.c b/bsnes/gb/Core-new/joypad.c deleted file mode 100644 index b8d4fdb4..00000000 --- a/bsnes/gb/Core-new/joypad.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "gb.h" -#include - -void GB_update_joyp(GB_gameboy_t *gb) -{ - if (gb->model & GB_MODEL_NO_SFC_BIT) return; - - uint8_t key_selection = 0; - uint8_t previous_state = 0; - - /* Todo: add delay to key selection */ - previous_state = gb->io_registers[GB_IO_JOYP] & 0xF; - key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3; - gb->io_registers[GB_IO_JOYP] &= 0xF0; - uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0; - switch (key_selection) { - case 3: - if (gb->sgb && gb->sgb->player_count > 1) { - gb->io_registers[GB_IO_JOYP] |= 0xF - current_player; - } - else { - /* Nothing is wired, all up */ - gb->io_registers[GB_IO_JOYP] |= 0x0F; - } - break; - - case 2: - /* Direction keys */ - for (uint8_t i = 0; i < 4; i++) { - gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i]) << i; - } - /* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */ - if (!(gb->io_registers[GB_IO_JOYP] & 1)) { - gb->io_registers[GB_IO_JOYP] |= 2; - } - if (!(gb->io_registers[GB_IO_JOYP] & 4)) { - gb->io_registers[GB_IO_JOYP] |= 8; - } - break; - - case 1: - /* Other keys */ - for (uint8_t i = 0; i < 4; i++) { - gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i + 4]) << i; - } - break; - - case 0: - for (uint8_t i = 0; i < 4; i++) { - gb->io_registers[GB_IO_JOYP] |= (!(gb->keys[current_player][i] || gb->keys[current_player][i + 4])) << i; - } - break; - - default: - break; - } - - /* Todo: This assumes the keys *always* bounce, which is incorrect when emulating an SGB */ - if (previous_state != (gb->io_registers[GB_IO_JOYP] & 0xF)) { - /* The joypad interrupt DOES occur on CGB (Tested on CGB-E), unlike what some documents say. */ - gb->io_registers[GB_IO_IF] |= 0x10; - } - - gb->io_registers[GB_IO_JOYP] |= 0xC0; -} - -void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value) -{ - uint8_t previous_state = gb->io_registers[GB_IO_JOYP] & 0xF; - gb->io_registers[GB_IO_JOYP] &= 0xF0; - gb->io_registers[GB_IO_JOYP] |= value & 0xF; - - if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) { - gb->io_registers[GB_IO_IF] |= 0x10; - } - gb->io_registers[GB_IO_JOYP] |= 0xC0; -} - -void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed) -{ - assert(index >= 0 && index < GB_KEY_MAX); - gb->keys[0][index] = pressed; - GB_update_joyp(gb); -} - -void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed) -{ - assert(index >= 0 && index < GB_KEY_MAX); - assert(player < 4); - gb->keys[player][index] = pressed; - GB_update_joyp(gb); -} diff --git a/bsnes/gb/Core-new/joypad.h b/bsnes/gb/Core-new/joypad.h deleted file mode 100644 index 21fad534..00000000 --- a/bsnes/gb/Core-new/joypad.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef joypad_h -#define joypad_h -#include "gb_struct_def.h" -#include - -typedef enum { - GB_KEY_RIGHT, - GB_KEY_LEFT, - GB_KEY_UP, - GB_KEY_DOWN, - GB_KEY_A, - GB_KEY_B, - GB_KEY_SELECT, - GB_KEY_START, - GB_KEY_MAX -} GB_key_t; - -void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed); -void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed); -void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value); - -#ifdef GB_INTERNAL -void GB_update_joyp(GB_gameboy_t *gb); -#endif -#endif /* joypad_h */ diff --git a/bsnes/gb/Core-new/mbc.c b/bsnes/gb/Core-new/mbc.c deleted file mode 100644 index d3791a18..00000000 --- a/bsnes/gb/Core-new/mbc.c +++ /dev/null @@ -1,154 +0,0 @@ -#include -#include -#include -#include "gb.h" - -const GB_cartridge_t GB_cart_defs[256] = { - // From http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header#0147_-_Cartridge_Type - /* MBC SUBTYPE RAM BAT. RTC RUMB. */ - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 00h ROM ONLY - { GB_MBC1 , GB_STANDARD_MBC, false, false, false, false}, // 01h MBC1 - { GB_MBC1 , GB_STANDARD_MBC, true , false, false, false}, // 02h MBC1+RAM - { GB_MBC1 , GB_STANDARD_MBC, true , true , false, false}, // 03h MBC1+RAM+BATTERY - [5] = - { GB_MBC2 , GB_STANDARD_MBC, true , false, false, false}, // 05h MBC2 - { GB_MBC2 , GB_STANDARD_MBC, true , true , false, false}, // 06h MBC2+BATTERY - [8] = - { GB_NO_MBC, GB_STANDARD_MBC, true , false, false, false}, // 08h ROM+RAM - { GB_NO_MBC, GB_STANDARD_MBC, true , true , false, false}, // 09h ROM+RAM+BATTERY - [0xB] = - /* Todo: Not supported yet */ - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Bh MMM01 - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Ch MMM01+RAM - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Dh MMM01+RAM+BATTERY - [0xF] = - { GB_MBC3 , GB_STANDARD_MBC, false, true, true , false}, // 0Fh MBC3+TIMER+BATTERY - { GB_MBC3 , GB_STANDARD_MBC, true , true, true , false}, // 10h MBC3+TIMER+RAM+BATTERY - { GB_MBC3 , GB_STANDARD_MBC, false, false, false, false}, // 11h MBC3 - { GB_MBC3 , GB_STANDARD_MBC, true , false, false, false}, // 12h MBC3+RAM - { GB_MBC3 , GB_STANDARD_MBC, true , true , false, false}, // 13h MBC3+RAM+BATTERY - [0x19] = - { GB_MBC5 , GB_STANDARD_MBC, false, false, false, false}, // 19h MBC5 - { GB_MBC5 , GB_STANDARD_MBC, true , false, false, false}, // 1Ah MBC5+RAM - { GB_MBC5 , GB_STANDARD_MBC, true , true , false, false}, // 1Bh MBC5+RAM+BATTERY - { GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE - { GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM - { GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY - [0xFC] = - { GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported) - { GB_HUC3 , GB_STANDARD_MBC, true , true , false, false}, // FEh HuC3 (Todo: Mapper support only) - { GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY (Todo: No IR bindings) -}; - -void GB_update_mbc_mappings(GB_gameboy_t *gb) -{ - switch (gb->cartridge_type->mbc_type) { - case GB_NO_MBC: return; - case GB_MBC1: - switch (gb->mbc1_wiring) { - case GB_STANDARD_MBC1_WIRING: - gb->mbc_rom_bank = gb->mbc1.bank_low | (gb->mbc1.bank_high << 5); - if (gb->mbc1.mode == 0) { - gb->mbc_ram_bank = 0; - gb->mbc_rom0_bank = 0; - } - else { - gb->mbc_ram_bank = gb->mbc1.bank_high; - gb->mbc_rom0_bank = gb->mbc1.bank_high << 5; - } - if ((gb->mbc_rom_bank & 0x1F) == 0) { - gb->mbc_rom_bank++; - } - break; - case GB_MBC1M_WIRING: - gb->mbc_rom_bank = (gb->mbc1.bank_low & 0xF) | (gb->mbc1.bank_high << 4); - if (gb->mbc1.mode == 0) { - gb->mbc_ram_bank = 0; - gb->mbc_rom0_bank = 0; - } - else { - gb->mbc_rom0_bank = gb->mbc1.bank_high << 4; - gb->mbc_ram_bank = 0; - } - if ((gb->mbc1.bank_low & 0x1F) == 0) { - gb->mbc_rom_bank++; - } - break; - } - break; - case GB_MBC2: - gb->mbc_rom_bank = gb->mbc2.rom_bank; - if ((gb->mbc_rom_bank & 0xF) == 0) { - gb->mbc_rom_bank = 1; - } - break; - case GB_MBC3: - gb->mbc_rom_bank = gb->mbc3.rom_bank; - gb->mbc_ram_bank = gb->mbc3.ram_bank; - if (gb->mbc_rom_bank == 0) { - gb->mbc_rom_bank = 1; - } - break; - case GB_MBC5: - gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8); - gb->mbc_ram_bank = gb->mbc5.ram_bank; - break; - case GB_HUC1: - if (gb->huc1.mode == 0) { - gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6); - gb->mbc_ram_bank = 0; - } - else { - gb->mbc_rom_bank = gb->huc1.bank_low; - gb->mbc_ram_bank = gb->huc1.bank_high; - } - break; - case GB_HUC3: - gb->mbc_rom_bank = gb->huc3.rom_bank; - gb->mbc_ram_bank = gb->huc3.ram_bank; - break; - } -} - -void GB_configure_cart(GB_gameboy_t *gb) -{ - gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]]; - - if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) { - GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n"); - gb->cartridge_type = &GB_cart_defs[0x11]; - } - else if (gb->rom[0x147] != 0 && memcmp(gb->cartridge_type, &GB_cart_defs[0], sizeof(GB_cart_defs[0])) == 0) { - GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]); - } - - if (gb->cartridge_type->has_ram) { - if (gb->cartridge_type->mbc_type == GB_MBC2) { - gb->mbc_ram_size = 0x200; - } - else { - static const int ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000}; - gb->mbc_ram_size = ram_sizes[gb->rom[0x149]]; - } - gb->mbc_ram = malloc(gb->mbc_ram_size); - - /* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */ - memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size); - } - - /* MBC1 has at least 3 types of wiring (We currently support two (Standard and 4bit-MBC1M) of these). - See http://forums.nesdev.com/viewtopic.php?f=20&t=14099 */ - - /* Attempt to "guess" wiring */ - if (gb->cartridge_type->mbc_type == GB_MBC1) { - if (gb->rom_size >= 0x44000 && memcmp(gb->rom + 0x104, gb->rom + 0x40104, 0x30) == 0) { - gb->mbc1_wiring = GB_MBC1M_WIRING; - } - } - - /* Set MBC5's bank to 1 correctly */ - if (gb->cartridge_type->mbc_type == GB_MBC5) { - gb->mbc5.rom_bank_low = 1; - } -} diff --git a/bsnes/gb/Core-new/mbc.h b/bsnes/gb/Core-new/mbc.h deleted file mode 100644 index 7e9b47f0..00000000 --- a/bsnes/gb/Core-new/mbc.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MBC_h -#define MBC_h -#include "gb_struct_def.h" -#include - -typedef struct { - enum { - GB_NO_MBC, - GB_MBC1, - GB_MBC2, - GB_MBC3, - GB_MBC5, - GB_HUC1, /* Todo: HUC1 features are not emulated. Should be unified with the CGB IR sensor API. */ - GB_HUC3, - } mbc_type; - enum { - GB_STANDARD_MBC, - GB_CAMERA, - } mbc_subtype; - bool has_ram; - bool has_battery; - bool has_rtc; - bool has_rumble; -} GB_cartridge_t; - -#ifdef GB_INTERNAL -extern const GB_cartridge_t GB_cart_defs[256]; -void GB_update_mbc_mappings(GB_gameboy_t *gb); -void GB_configure_cart(GB_gameboy_t *gb); -#endif - -#endif /* MBC_h */ diff --git a/bsnes/gb/Core-new/memory.c b/bsnes/gb/Core-new/memory.c deleted file mode 100644 index 7451277a..00000000 --- a/bsnes/gb/Core-new/memory.c +++ /dev/null @@ -1,1010 +0,0 @@ -#include -#include -#include "gb.h" - -typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr); -typedef void GB_write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value); - -typedef enum { - GB_BUS_MAIN, /* In DMG: Cart and RAM. In CGB: Cart only */ - GB_BUS_RAM, /* In CGB only. */ - GB_BUS_VRAM, - GB_BUS_INTERNAL, /* Anything in highram. Might not be the most correct name. */ -} GB_bus_t; - -static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr) -{ - if (addr < 0x8000) { - return GB_BUS_MAIN; - } - if (addr < 0xA000) { - return GB_BUS_VRAM; - } - if (addr < 0xC000) { - return GB_BUS_MAIN; - } - if (addr < 0xFE00) { - return GB_is_cgb(gb)? GB_BUS_RAM : GB_BUS_MAIN; - } - return GB_BUS_INTERNAL; -} - -static uint8_t bitwise_glitch(uint8_t a, uint8_t b, uint8_t c) -{ - return ((a ^ c) & (b ^ c)) ^ c; -} - -static uint8_t bitwise_glitch_read(uint8_t a, uint8_t b, uint8_t c) -{ - return b | (a & c); -} - -static uint8_t bitwise_glitch_read_increase(uint8_t a, uint8_t b, uint8_t c, uint8_t d) -{ - return (b & (a | c | d)) | (a & c & d); -} - -void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address) -{ - if (GB_is_cgb(gb)) return; - - if (address >= 0xFE00 && address < 0xFF00) { - if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) { - gb->oam[gb->accessed_oam_row] = bitwise_glitch(gb->oam[gb->accessed_oam_row], - gb->oam[gb->accessed_oam_row - 8], - gb->oam[gb->accessed_oam_row - 4]); - gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch(gb->oam[gb->accessed_oam_row + 1], - gb->oam[gb->accessed_oam_row - 7], - gb->oam[gb->accessed_oam_row - 3]); - for (unsigned i = 2; i < 8; i++) { - gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i]; - } - } - } -} - -void GB_trigger_oam_bug_read(GB_gameboy_t *gb, uint16_t address) -{ - if (GB_is_cgb(gb)) return; - - if (address >= 0xFE00 && address < 0xFF00) { - if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) { - gb->oam[gb->accessed_oam_row - 8] = - gb->oam[gb->accessed_oam_row] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row], - gb->oam[gb->accessed_oam_row - 8], - gb->oam[gb->accessed_oam_row - 4]); - gb->oam[gb->accessed_oam_row - 7] = - gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row + 1], - gb->oam[gb->accessed_oam_row - 7], - gb->oam[gb->accessed_oam_row - 3]); - for (unsigned i = 2; i < 8; i++) { - gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i]; - } - } - } -} - -void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address) -{ - if (GB_is_cgb(gb)) return; - - if (address >= 0xFE00 && address < 0xFF00) { - if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 0x20 && gb->accessed_oam_row < 0x98) { - gb->oam[gb->accessed_oam_row - 0x8] = bitwise_glitch_read_increase(gb->oam[gb->accessed_oam_row - 0x10], - gb->oam[gb->accessed_oam_row - 0x08], - gb->oam[gb->accessed_oam_row ], - gb->oam[gb->accessed_oam_row - 0x04] - ); - gb->oam[gb->accessed_oam_row - 0x7] = bitwise_glitch_read_increase(gb->oam[gb->accessed_oam_row - 0x0f], - gb->oam[gb->accessed_oam_row - 0x07], - gb->oam[gb->accessed_oam_row + 0x01], - gb->oam[gb->accessed_oam_row - 0x03] - ); - for (unsigned i = 0; i < 8; i++) { - gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 0x10 + i] = gb->oam[gb->accessed_oam_row - 0x08 + i]; - } - } - } -} - -static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr) -{ - if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xFE00) return false; - return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src); -} - -static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr) -{ - if (addr < 0x100 && !gb->boot_rom_finished) { - return gb->boot_rom[addr]; - } - - if (addr >= 0x200 && addr < 0x900 && GB_is_cgb(gb) && !gb->boot_rom_finished) { - return gb->boot_rom[addr]; - } - - if (!gb->rom_size) { - return 0xFF; - } - unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000; - return gb->rom[effective_address & (gb->rom_size - 1)]; -} - -static uint8_t read_mbc_rom(GB_gameboy_t *gb, uint16_t addr) -{ - unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000; - return gb->rom[effective_address & (gb->rom_size - 1)]; -} - -static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr) -{ - if (gb->vram_read_blocked) { - return 0xFF; - } - return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000]; -} - -static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) -{ - if ((!gb->mbc_ram_enable || !gb->mbc_ram_size) && - gb->cartridge_type->mbc_subtype != GB_CAMERA && - gb->cartridge_type->mbc_type != GB_HUC1) return 0xFF; - - if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) { - /* RTC read */ - gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */ - return gb->rtc_latched.data[gb->mbc_ram_bank - 8]; - } - - if (gb->camera_registers_mapped) { - return GB_camera_read_register(gb, addr); - } - - if (!gb->mbc_ram) { - return 0xFF; - } - - if (gb->cartridge_type->mbc_subtype == GB_CAMERA && gb->mbc_ram_bank == 0 && addr >= 0xa100 && addr < 0xaf00) { - return GB_camera_read_image(gb, addr - 0xa100); - } - - uint8_t ret = gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)]; - if (gb->cartridge_type->mbc_type == GB_MBC2) { - ret |= 0xF0; - } - return ret; -} - -static uint8_t read_ram(GB_gameboy_t *gb, uint16_t addr) -{ - return gb->ram[addr & 0x0FFF]; -} - -static uint8_t read_banked_ram(GB_gameboy_t *gb, uint16_t addr) -{ - return gb->ram[(addr & 0x0FFF) + gb->cgb_ram_bank * 0x1000]; -} - -static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) -{ - - if (gb->hdma_on) { - return gb->last_opcode_read; - } - - if (addr < 0xFE00) { - return read_banked_ram(gb, addr); - } - - if (addr < 0xFF00) { - if (gb->oam_write_blocked && !GB_is_cgb(gb)) { - GB_trigger_oam_bug_read(gb, addr); - return 0xff; - } - - if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { - /* Todo: Does reading from OAM during DMA causes the OAM bug? */ - return 0xff; - } - - if (gb->oam_read_blocked) { - if (!GB_is_cgb(gb)) { - if (addr < 0xFEA0) { - if (gb->accessed_oam_row == 0) { - gb->oam[(addr & 0xf8)] = - gb->oam[0] = bitwise_glitch_read(gb->oam[0], - gb->oam[(addr & 0xf8)], - gb->oam[(addr & 0xfe)]); - gb->oam[(addr & 0xf8) + 1] = - gb->oam[1] = bitwise_glitch_read(gb->oam[1], - gb->oam[(addr & 0xf8) + 1], - gb->oam[(addr & 0xfe) | 1]); - for (unsigned i = 2; i < 8; i++) { - gb->oam[i] = gb->oam[(addr & 0xf8) + i]; - } - } - else if (gb->accessed_oam_row == 0xa0) { - gb->oam[0x9e] = bitwise_glitch_read(gb->oam[0x9c], - gb->oam[0x9e], - gb->oam[(addr & 0xf8) | 6]); - gb->oam[0x9f] = bitwise_glitch_read(gb->oam[0x9d], - gb->oam[0x9f], - gb->oam[(addr & 0xf8) | 7]); - - for (unsigned i = 0; i < 8; i++) { - gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i]; - } - } - } - } - return 0xff; - } - - if (addr < 0xFEA0) { - return gb->oam[addr & 0xFF]; - } - - if (gb->oam_read_blocked) { - return 0xFF; - } - - switch (gb->model) { - case GB_MODEL_CGB_E: - case GB_MODEL_AGB: - return (addr & 0xF0) | ((addr >> 4) & 0xF); - - /* - case GB_MODEL_CGB_D: - if (addr > 0xfec0) { - addr |= 0xf0; - } - return gb->extra_oam[addr - 0xfea0]; - */ - - case GB_MODEL_CGB_C: - /* - case GB_MODEL_CGB_B: - case GB_MODEL_CGB_A: - case GB_MODEL_CGB_0: - */ - addr &= ~0x18; - return gb->extra_oam[addr - 0xfea0]; - - case GB_MODEL_DMG_B: - case GB_MODEL_SGB_NTSC: - case GB_MODEL_SGB_PAL: - case GB_MODEL_SGB_NTSC_NO_SFC: - case GB_MODEL_SGB_PAL_NO_SFC: - case GB_MODEL_SGB2: - case GB_MODEL_SGB2_NO_SFC: - ; - } - } - - if (addr < 0xFF00) { - - return 0; - - } - - if (addr < 0xFF80) { - switch (addr & 0xFF) { - case GB_IO_IF: - return gb->io_registers[GB_IO_IF] | 0xE0; - case GB_IO_TAC: - return gb->io_registers[GB_IO_TAC] | 0xF8; - case GB_IO_STAT: - return gb->io_registers[GB_IO_STAT] | 0x80; - case GB_IO_DMG_EMULATION_INDICATION: - if (!gb->cgb_mode) { - return 0xFF; - } - return gb->io_registers[GB_IO_DMG_EMULATION_INDICATION] | 0xFE; - - case GB_IO_PCM_12: - if (!GB_is_cgb(gb)) return 0xFF; - return (gb->apu.is_active[GB_SQUARE_2] ? (gb->apu.samples[GB_SQUARE_2] << 4) : 0) | - (gb->apu.is_active[GB_SQUARE_1] ? (gb->apu.samples[GB_SQUARE_1]) : 0); - case GB_IO_PCM_34: - if (!GB_is_cgb(gb)) return 0xFF; - return (gb->apu.is_active[GB_NOISE] ? (gb->apu.samples[GB_NOISE] << 4) : 0) | - (gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0); - case GB_IO_JOYP: - GB_timing_sync(gb); - case GB_IO_TMA: - case GB_IO_LCDC: - case GB_IO_SCY: - case GB_IO_SCX: - case GB_IO_LY: - case GB_IO_LYC: - case GB_IO_BGP: - case GB_IO_OBP0: - case GB_IO_OBP1: - case GB_IO_WY: - case GB_IO_WX: - case GB_IO_SC: - case GB_IO_SB: - case GB_IO_DMA: - return gb->io_registers[addr & 0xFF]; - case GB_IO_TIMA: - if (gb->tima_reload_state == GB_TIMA_RELOADING) { - return 0; - } - return gb->io_registers[GB_IO_TIMA]; - case GB_IO_DIV: - return gb->div_counter >> 8; - case GB_IO_HDMA5: - if (!gb->cgb_mode) return 0xFF; - return ((gb->hdma_on || gb->hdma_on_hblank)? 0 : 0x80) | ((gb->hdma_steps_left - 1) & 0x7F); - case GB_IO_SVBK: - if (!gb->cgb_mode) { - return 0xFF; - } - return gb->cgb_ram_bank | ~0x7; - case GB_IO_VBK: - if (!GB_is_cgb(gb)) { - return 0xFF; - } - return gb->cgb_vram_bank | ~0x1; - - /* Todo: It seems that a CGB in DMG mode can access BGPI and OBPI, but not BGPD and OBPD? */ - case GB_IO_BGPI: - case GB_IO_OBPI: - if (!GB_is_cgb(gb)) { - return 0xFF; - } - return gb->io_registers[addr & 0xFF] | 0x40; - - case GB_IO_BGPD: - case GB_IO_OBPD: - { - if (!gb->cgb_mode && gb->boot_rom_finished) { - return 0xFF; - } - if (gb->cgb_palettes_blocked) { - return 0xFF; - } - uint8_t index_reg = (addr & 0xFF) - 1; - return ((addr & 0xFF) == GB_IO_BGPD? - gb->background_palettes_data : - gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F]; - } - - case GB_IO_KEY1: - if (!gb->cgb_mode) { - return 0xFF; - } - return (gb->io_registers[GB_IO_KEY1] & 0x7F) | (gb->cgb_double_speed? 0xFE : 0x7E); - - case GB_IO_RP: { - if (!gb->cgb_mode) return 0xFF; - /* You will read your own IR LED if it's on. */ - bool read_value = gb->infrared_input || (gb->io_registers[GB_IO_RP] & 1); - uint8_t ret = (gb->io_registers[GB_IO_RP] & 0xC1) | 0x3C; - if ((gb->io_registers[GB_IO_RP] & 0xC0) == 0xC0 && read_value) { - ret |= 2; - } - return ret; - } - case GB_IO_UNKNOWN2: - case GB_IO_UNKNOWN3: - return GB_is_cgb(gb)? gb->io_registers[addr & 0xFF] : 0xFF; - case GB_IO_UNKNOWN4: - return gb->cgb_mode? gb->io_registers[addr & 0xFF] : 0xFF; - case GB_IO_UNKNOWN5: - return GB_is_cgb(gb)? gb->io_registers[addr & 0xFF] | 0x8F : 0xFF; - default: - if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) { - return GB_apu_read(gb, addr & 0xFF); - } - return 0xFF; - } - /* Hardware registers */ - return 0; - } - - if (addr == 0xFFFF) { - /* Interrupt Mask */ - return gb->interrupt_enable; - } - - /* HRAM */ - return gb->hram[addr - 0xFF80]; -} - -static GB_read_function_t * const read_map[] = -{ - read_rom, read_rom, read_rom, read_rom, /* 0XXX, 1XXX, 2XXX, 3XXX */ - read_mbc_rom, read_mbc_rom, read_mbc_rom, read_mbc_rom, /* 4XXX, 5XXX, 6XXX, 7XXX */ - read_vram, read_vram, /* 8XXX, 9XXX */ - read_mbc_ram, read_mbc_ram, /* AXXX, BXXX */ - read_ram, read_banked_ram, /* CXXX, DXXX */ - read_ram, read_high_memory, /* EXXX FXXX */ -}; - -uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr) -{ - if (gb->n_watchpoints) { - GB_debugger_test_read_watchpoint(gb, addr); - } - if (is_addr_in_dma_use(gb, addr)) { - addr = gb->dma_current_src; - } - if (gb->read_memory_callback) { - uint8_t data = read_map[addr >> 12](gb, addr); - data = gb->read_memory_callback(gb, addr, data); - return data; - } - return read_map[addr >> 12](gb, addr); -} - -void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback) { - gb->read_memory_callback = callback; -} - -static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - switch (gb->cartridge_type->mbc_type) { - case GB_NO_MBC: return; - case GB_MBC1: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: gb->mbc1.bank_low = value; break; - case 0x4000: case 0x5000: gb->mbc1.bank_high = value; break; - case 0x6000: case 0x7000: gb->mbc1.mode = value; break; - } - break; - case GB_MBC2: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: if (!(addr & 0x100)) gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: if ( addr & 0x100) gb->mbc2.rom_bank = value; break; - } - break; - case GB_MBC3: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break; - case 0x4000: case 0x5000: gb->mbc3.ram_bank = value; break; - case 0x6000: case 0x7000: - if (!gb->rtc_latch && (value & 1)) { /* Todo: verify condition is correct */ - memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real)); - } - gb->rtc_latch = value & 1; - break; - } - break; - case GB_MBC5: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: gb->mbc5.rom_bank_low = value; break; - case 0x3000: gb->mbc5.rom_bank_high = value; break; - case 0x4000: case 0x5000: - if (gb->cartridge_type->has_rumble) { - if (!!(value & 8) != gb->rumble_state) { - gb->rumble_state = !gb->rumble_state; - if (gb->rumble_callback) { - gb->rumble_callback(gb, gb->rumble_state); - } - } - value &= 7; - } - gb->mbc5.ram_bank = value; - gb->camera_registers_mapped = (value & 0x10) && gb->cartridge_type->mbc_subtype == GB_CAMERA; - break; - } - break; - case GB_HUC1: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: gb->huc1.bank_low = value; break; - case 0x4000: case 0x5000: gb->huc1.bank_high = value; break; - case 0x6000: case 0x7000: gb->huc1.mode = value; break; - } - break; - case GB_HUC3: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break; - case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break; - } - break; - } - GB_update_mbc_mappings(gb); -} - -static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (gb->vram_write_blocked) { - //GB_log(gb, "Wrote %02x to %04x (VRAM) during mode 3\n", value, addr); - return; - } - gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value; -} - -static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (gb->camera_registers_mapped) { - GB_camera_write_register(gb, addr, value); - return; - } - - if (!gb->mbc_ram_enable || !gb->mbc_ram_size) return; - - if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) { - gb->rtc_latched.data[gb->mbc_ram_bank - 8] = gb->rtc_real.data[gb->mbc_ram_bank - 8] = value; - return; - } - - if (!gb->mbc_ram) { - return; - } - - gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)] = value; -} - -static void write_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - gb->ram[addr & 0x0FFF] = value; -} - -static void write_banked_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - gb->ram[(addr & 0x0FFF) + gb->cgb_ram_bank * 0x1000] = value; -} - -static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (addr < 0xFE00) { - GB_log(gb, "Wrote %02x to %04x (RAM Mirror)\n", value, addr); - write_banked_ram(gb, addr, value); - return; - } - - if (addr < 0xFF00) { - if (gb->oam_write_blocked) { - GB_trigger_oam_bug(gb, addr); - return; - } - - if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { - /* Todo: Does writing to OAM during DMA causes the OAM bug? */ - return; - } - - if (GB_is_cgb(gb)) { - if (addr < 0xFEA0) { - gb->oam[addr & 0xFF] = value; - } - switch (gb->model) { - /* - case GB_MODEL_CGB_D: - if (addr > 0xfec0) { - addr |= 0xf0; - } - gb->extra_oam[addr - 0xfea0] = value; - break; - */ - case GB_MODEL_CGB_C: - /* - case GB_MODEL_CGB_B: - case GB_MODEL_CGB_A: - case GB_MODEL_CGB_0: - */ - addr &= ~0x18; - gb->extra_oam[addr - 0xfea0] = value; - break; - case GB_MODEL_DMG_B: - case GB_MODEL_SGB_NTSC: - case GB_MODEL_SGB_PAL: - case GB_MODEL_SGB_NTSC_NO_SFC: - case GB_MODEL_SGB_PAL_NO_SFC: - case GB_MODEL_SGB2: - case GB_MODEL_SGB2_NO_SFC: - case GB_MODEL_CGB_E: - case GB_MODEL_AGB: - break; - } - return; - } - - if (addr < 0xFEA0) { - if (gb->accessed_oam_row == 0xa0) { - for (unsigned i = 0; i < 8; i++) { - if ((i & 6) != (addr & 6)) { - gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i]; - } - else { - gb->oam[(addr & 0xf8) + i] = bitwise_glitch(gb->oam[(addr & 0xf8) + i], gb->oam[0x9c], gb->oam[0x98 + i]); - } - } - } - - gb->oam[addr & 0xFF] = value; - - if (gb->accessed_oam_row == 0) { - gb->oam[0] = bitwise_glitch(gb->oam[0], - gb->oam[(addr & 0xf8)], - gb->oam[(addr & 0xfe)]); - gb->oam[1] = bitwise_glitch(gb->oam[1], - gb->oam[(addr & 0xf8) + 1], - gb->oam[(addr & 0xfe) | 1]); - for (unsigned i = 2; i < 8; i++) { - gb->oam[i] = gb->oam[(addr & 0xf8) + i]; - } - } - } - else if (gb->accessed_oam_row == 0) { - gb->oam[addr & 0x7] = value; - } - return; - } - - /* Todo: Clean this code up: use a function table and move relevant code to display.c and timing.c - (APU read and writes are already at apu.c) */ - if (addr < 0xFF80) { - /* Hardware registers */ - switch (addr & 0xFF) { - case GB_IO_WX: - GB_window_related_write(gb, addr & 0xFF, value); - break; - case GB_IO_IF: - case GB_IO_SCX: - case GB_IO_SCY: - case GB_IO_BGP: - case GB_IO_OBP0: - case GB_IO_OBP1: - case GB_IO_WY: - case GB_IO_SB: - case GB_IO_DMG_EMULATION_INDICATION: - case GB_IO_UNKNOWN2: - case GB_IO_UNKNOWN3: - case GB_IO_UNKNOWN4: - case GB_IO_UNKNOWN5: - gb->io_registers[addr & 0xFF] = value; - return; - case GB_IO_LYC: - - /* TODO: Probably completely wrong in double speed mode */ - - /* TODO: This hack is disgusting */ - if (gb->display_state == 29 && GB_is_cgb(gb)) { - gb->ly_for_comparison = 153; - GB_STAT_update(gb); - gb->ly_for_comparison = 0; - } - - gb->io_registers[addr & 0xFF] = value; - - /* These are the states when LY changes, let the display routine call GB_STAT_update for use - so it correctly handles T-cycle accurate LYC writes */ - if (!GB_is_cgb(gb) || ( - gb->display_state != 35 && - gb->display_state != 26 && - gb->display_state != 15 && - gb->display_state != 16)) { - - /* More hacks to make LYC write conflicts work */ - if (gb->display_state == 14 && GB_is_cgb(gb)) { - gb->ly_for_comparison = 153; - GB_STAT_update(gb); - gb->ly_for_comparison = -1; - } - else { - GB_STAT_update(gb); - } - } - return; - - case GB_IO_TIMA: - if (gb->tima_reload_state != GB_TIMA_RELOADED) { - gb->io_registers[GB_IO_TIMA] = value; - } - return; - - case GB_IO_TMA: - gb->io_registers[GB_IO_TMA] = value; - if (gb->tima_reload_state != GB_TIMA_RUNNING) { - gb->io_registers[GB_IO_TIMA] = value; - } - return; - - case GB_IO_TAC: - GB_emulate_timer_glitch(gb, gb->io_registers[GB_IO_TAC], value); - gb->io_registers[GB_IO_TAC] = value; - return; - - - case GB_IO_LCDC: - if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) { - gb->display_cycles = 0; - gb->display_state = 0; - if (GB_is_sgb(gb)) { - gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED; - } - else if (gb->frame_skip_state == GB_FRAMESKIP_SECOND_FRAME_RENDERED) { - gb->frame_skip_state = GB_FRAMESKIP_LCD_TURNED_ON; - } - } - else if (!(value & 0x80) && (gb->io_registers[GB_IO_LCDC] & 0x80)) { - /* Sync after turning off LCD */ - GB_timing_sync(gb); - GB_lcd_off(gb); - } - /* Writing to LCDC might enable to disable the window, so we write it via GB_window_related_write */ - GB_window_related_write(gb, addr & 0xFF, value); - return; - - case GB_IO_STAT: - /* Delete previous R/W bits */ - gb->io_registers[GB_IO_STAT] &= 7; - /* Set them by value */ - gb->io_registers[GB_IO_STAT] |= value & ~7; - /* Set unused bit to 1 */ - gb->io_registers[GB_IO_STAT] |= 0x80; - - GB_STAT_update(gb); - return; - - case GB_IO_DIV: - /* Reset the div state machine */ - gb->div_state = 0; - gb->div_cycles = 0; - return; - - case GB_IO_JOYP: - if ((gb->io_registers[GB_IO_JOYP] & 0x30) != (value & 0x30)) { - GB_sgb_write(gb, value); - gb->io_registers[GB_IO_JOYP] = value & 0xF0; - GB_update_joyp(gb); - } - return; - - case GB_IO_BIOS: - gb->boot_rom_finished = true; - return; - - case GB_IO_DMG_EMULATION: - if (GB_is_cgb(gb) && !gb->boot_rom_finished) { - gb->cgb_mode = !(value & 0xC); /* The real "contents" of this register aren't quite known yet. */ - } - return; - - case GB_IO_DMA: - if (gb->dma_steps_left) { - /* This is not correct emulation, since we're not really delaying the second DMA. - One write that should have happened in the first DMA will not happen. However, - since that byte will be overwritten by the second DMA before it can actually be - read, it doesn't actually matter. */ - gb->is_dma_restarting = true; - } - gb->dma_cycles = -7; - gb->dma_current_dest = 0; - gb->dma_current_src = value << 8; - gb->dma_steps_left = 0xa0; - gb->io_registers[GB_IO_DMA] = value; - return; - case GB_IO_SVBK: - if (!gb->cgb_mode) { - return; - } - gb->cgb_ram_bank = value & 0x7; - if (!gb->cgb_ram_bank) { - gb->cgb_ram_bank++; - } - return; - case GB_IO_VBK: - if (!gb->cgb_mode) { - return; - } - gb->cgb_vram_bank = value & 0x1; - return; - - case GB_IO_BGPI: - case GB_IO_OBPI: - if (!GB_is_cgb(gb)) { - return; - } - gb->io_registers[addr & 0xFF] = value; - return; - case GB_IO_BGPD: - case GB_IO_OBPD: - if (!gb->cgb_mode && gb->boot_rom_finished) { - /* Todo: Due to the behavior of a broken Game & Watch Gallery 2 ROM on a real CGB. A proper test ROM - is required. */ - return; - } - - uint8_t index_reg = (addr & 0xFF) - 1; - if (gb->cgb_palettes_blocked) { - if (gb->io_registers[index_reg] & 0x80) { - gb->io_registers[index_reg]++; - gb->io_registers[index_reg] |= 0x80; - } - return; - } - ((addr & 0xFF) == GB_IO_BGPD? - gb->background_palettes_data : - gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F] = value; - GB_palette_changed(gb, (addr & 0xFF) == GB_IO_BGPD, gb->io_registers[index_reg] & 0x3F); - if (gb->io_registers[index_reg] & 0x80) { - gb->io_registers[index_reg]++; - gb->io_registers[index_reg] |= 0x80; - } - return; - case GB_IO_KEY1: - if (!gb->cgb_mode) { - return; - } - gb->io_registers[GB_IO_KEY1] = value; - return; - case GB_IO_HDMA1: - if (gb->cgb_mode) { - gb->hdma_current_src &= 0xF0; - gb->hdma_current_src |= value << 8; - } - return; - case GB_IO_HDMA2: - if (gb->cgb_mode) { - gb->hdma_current_src &= 0xFF00; - gb->hdma_current_src |= value & 0xF0; - } - return; - case GB_IO_HDMA3: - if (gb->cgb_mode) { - gb->hdma_current_dest &= 0xF0; - gb->hdma_current_dest |= value << 8; - } - return; - case GB_IO_HDMA4: - if (gb->cgb_mode) { - gb->hdma_current_dest &= 0x1F00; - gb->hdma_current_dest |= value & 0xF0; - } - return; - case GB_IO_HDMA5: - if (!gb->cgb_mode) return; - if ((value & 0x80) == 0 && gb->hdma_on_hblank) { - gb->hdma_on_hblank = false; - return; - } - gb->hdma_on = (value & 0x80) == 0; - gb->hdma_on_hblank = (value & 0x80) != 0; - if (gb->hdma_on_hblank && (gb->io_registers[GB_IO_STAT] & 3) == 0) { - gb->hdma_on = true; - } - gb->io_registers[GB_IO_HDMA5] = value; - gb->hdma_steps_left = (gb->io_registers[GB_IO_HDMA5] & 0x7F) + 1; - /* Todo: Verify this. Gambatte's DMA tests require this. */ - if (gb->hdma_current_dest + (gb->hdma_steps_left << 4) > 0xFFFF) { - gb->hdma_steps_left = (0x10000 - gb->hdma_current_dest) >> 4; - } - gb->hdma_cycles = -12; - return; - - /* Todo: what happens when starting a transfer during a transfer? - What happens when starting a transfer during external clock? - */ - case GB_IO_SC: - if (!gb->cgb_mode) { - value |= 2; - } - gb->io_registers[GB_IO_SC] = value | (~0x83); - if ((value & 0x80) && (value & 0x1) ) { - 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_bit_start_callback) { - gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80); - } - } - else { - gb->serial_length = 0; - } - return; - - case GB_IO_RP: { - if (!GB_is_cgb(gb)) { - return; - } - if ((value & 1) != (gb->io_registers[GB_IO_RP] & 1)) { - if (gb->infrared_callback) { - gb->infrared_callback(gb, value & 1, gb->cycles_since_ir_change); - gb->cycles_since_ir_change = 0; - } - } - gb->io_registers[GB_IO_RP] = value; - return; - } - - default: - if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) { - GB_apu_write(gb, addr & 0xFF, value); - return; - } - GB_log(gb, "Wrote %02x to %04x (HW Register)\n", value, addr); - return; - } - } - - if (addr == 0xFFFF) { - /* Interrupt mask */ - gb->interrupt_enable = value; - return; - } - - /* HRAM */ - gb->hram[addr - 0xFF80] = value; -} - - - -static GB_write_function_t * const write_map[] = -{ - write_mbc, write_mbc, write_mbc, write_mbc, /* 0XXX, 1XXX, 2XXX, 3XXX */ - write_mbc, write_mbc, write_mbc, write_mbc, /* 4XXX, 5XXX, 6XXX, 7XXX */ - write_vram, write_vram, /* 8XXX, 9XXX */ - write_mbc_ram, write_mbc_ram, /* AXXX, BXXX */ - write_ram, write_banked_ram, /* CXXX, DXXX */ - write_ram, write_high_memory, /* EXXX FXXX */ -}; - -void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (gb->n_watchpoints) { - GB_debugger_test_write_watchpoint(gb, addr, value); - } - if (is_addr_in_dma_use(gb, addr)) { - /* Todo: What should happen? Will this affect DMA? Will data be written? What and where? */ - return; - } - write_map[addr >> 12](gb, addr, value); -} - -void GB_dma_run(GB_gameboy_t *gb) -{ - while (gb->dma_cycles >= 4 && gb->dma_steps_left) { - /* Todo: measure this value */ - gb->dma_cycles -= 4; - gb->dma_steps_left--; - - if (gb->dma_current_src < 0xe000) { - gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src); - } - else { - /* Todo: Not correct on the CGB */ - gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src & ~0x2000); - } - - /* dma_current_src must be the correct value during GB_read_memory */ - gb->dma_current_src++; - if (!gb->dma_steps_left) { - gb->is_dma_restarting = false; - } - } -} - -void GB_hdma_run(GB_gameboy_t *gb) -{ - if (!gb->hdma_on) return; - - while (gb->hdma_cycles >= 0x4) { - gb->hdma_cycles -= 0x4; - - GB_write_memory(gb, 0x8000 | (gb->hdma_current_dest++ & 0x1FFF), GB_read_memory(gb, (gb->hdma_current_src++))); - - if ((gb->hdma_current_dest & 0xf) == 0) { - if (--gb->hdma_steps_left == 0) { - gb->hdma_on = false; - gb->hdma_on_hblank = false; - gb->hdma_starting = false; - gb->io_registers[GB_IO_HDMA5] &= 0x7F; - break; - } - if (gb->hdma_on_hblank) { - gb->hdma_on = false; - break; - } - } - } -} diff --git a/bsnes/gb/Core-new/memory.h b/bsnes/gb/Core-new/memory.h deleted file mode 100644 index f0d03907..00000000 --- a/bsnes/gb/Core-new/memory.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef memory_h -#define memory_h -#include "gb_struct_def.h" -#include - -typedef uint8_t (*GB_read_memory_callback_t)(GB_gameboy_t *gb, uint16_t addr, uint8_t data); -void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback); - -uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr); -void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value); -#ifdef GB_INTERNAL -void GB_dma_run(GB_gameboy_t *gb); -void GB_hdma_run(GB_gameboy_t *gb); -void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address); -void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address); -#endif - -#endif /* memory_h */ diff --git a/bsnes/gb/Core-new/printer.c b/bsnes/gb/Core-new/printer.c deleted file mode 100644 index add1f86e..00000000 --- a/bsnes/gb/Core-new/printer.c +++ /dev/null @@ -1,216 +0,0 @@ -#include "gb.h" - -/* TODO: Emulation is VERY basic and assumes the ROM correctly uses the printer's interface. - Incorrect usage is not correctly emulated, as it's not well documented, nor do I - have my own GB Printer to figure it out myself. - - It also does not currently emulate communication timeout, which means that a bug - might prevent the printer operation until the GameBoy is restarted. - - Also, field mask values are assumed. */ - -static void handle_command(GB_gameboy_t *gb) -{ - - switch (gb->printer.command_id) { - case GB_PRINTER_INIT_COMMAND: - gb->printer.status = 0; - gb->printer.image_offset = 0; - break; - - case GB_PRINTER_START_COMMAND: - if (gb->printer.command_length == 4) { - gb->printer.status = 6; /* Printing */ - uint32_t image[gb->printer.image_offset]; - uint8_t palette = gb->printer.command_data[2]; - uint32_t colors[4] = {gb->rgb_encode_callback(gb, 0xff, 0xff, 0xff), - gb->rgb_encode_callback(gb, 0xaa, 0xaa, 0xaa), - gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55), - gb->rgb_encode_callback(gb, 0x00, 0x00, 0x00)}; - for (unsigned i = 0; i < gb->printer.image_offset; i++) { - image[i] = colors[(palette >> (gb->printer.image[i] * 2)) & 3]; - } - - if (gb->printer.callback) { - gb->printer.callback(gb, image, gb->printer.image_offset / 160, - gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7, - gb->printer.command_data[3] & 0x7F); - } - - gb->printer.image_offset = 0; - } - break; - - case GB_PRINTER_DATA_COMMAND: - if (gb->printer.command_length == GB_PRINTER_DATA_SIZE) { - gb->printer.image_offset %= sizeof(gb->printer.image); - gb->printer.status = 8; /* Received 0x280 bytes */ - - uint8_t *byte = gb->printer.command_data; - - for (unsigned row = 2; row--; ) { - for (unsigned tile_x = 0; tile_x < 160 / 8; tile_x++) { - for (unsigned y = 0; y < 8; y++, byte += 2) { - for (unsigned x_pixel = 0; x_pixel < 8; x_pixel++) { - gb->printer.image[gb->printer.image_offset + tile_x * 8 + x_pixel + y * 160] = - ((*byte) >> 7) | (((*(byte + 1)) >> 7) << 1); - (*byte) <<= 1; - (*(byte + 1)) <<= 1; - } - } - } - - gb->printer.image_offset += 8 * 160; - } - } - - case GB_PRINTER_NOP_COMMAND: - default: - break; - } -} - - -static void byte_reieve_completed(GB_gameboy_t *gb, uint8_t byte_received) -{ - gb->printer.byte_to_send = 0; - switch (gb->printer.command_state) { - case GB_PRINTER_COMMAND_MAGIC1: - if (byte_received != 0x88) { - return; - } - gb->printer.status &= ~1; - gb->printer.command_length = 0; - gb->printer.checksum = 0; - break; - - case GB_PRINTER_COMMAND_MAGIC2: - if (byte_received != 0x33) { - if (byte_received != 0x88) { - gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1; - } - return; - } - break; - - case GB_PRINTER_COMMAND_ID: - gb->printer.command_id = byte_received & 0xF; - break; - - case GB_PRINTER_COMMAND_COMPRESSION: - gb->printer.compression = byte_received & 1; - break; - - case GB_PRINTER_COMMAND_LENGTH_LOW: - gb->printer.length_left = byte_received; - break; - - case GB_PRINTER_COMMAND_LENGTH_HIGH: - gb->printer.length_left |= (byte_received & 3) << 8; - break; - - case GB_PRINTER_COMMAND_DATA: - if (gb->printer.command_length != GB_PRINTER_MAX_COMMAND_LENGTH) { - if (gb->printer.compression) { - if (!gb->printer.compression_run_lenth) { - gb->printer.compression_run_is_compressed = byte_received & 0x80; - gb->printer.compression_run_lenth = (byte_received & 0x7F) + 1 + gb->printer.compression_run_is_compressed; - } - else if (gb->printer.compression_run_is_compressed) { - while (gb->printer.compression_run_lenth) { - gb->printer.command_data[gb->printer.command_length++] = byte_received; - gb->printer.compression_run_lenth--; - if (gb->printer.command_length == GB_PRINTER_MAX_COMMAND_LENGTH) { - gb->printer.compression_run_lenth = 0; - } - } - } - else { - gb->printer.command_data[gb->printer.command_length++] = byte_received; - gb->printer.compression_run_lenth--; - } - } - else { - gb->printer.command_data[gb->printer.command_length++] = byte_received; - } - } - gb->printer.length_left--; - break; - - case GB_PRINTER_COMMAND_CHECKSUM_LOW: - gb->printer.checksum ^= byte_received; - break; - - case GB_PRINTER_COMMAND_CHECKSUM_HIGH: - gb->printer.checksum ^= byte_received << 8; - if (gb->printer.checksum) { - gb->printer.status |= 1; /* Checksum error*/ - gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1; - return; - } - gb->printer.byte_to_send = 0x81; - - break; - case GB_PRINTER_COMMAND_ACTIVE: - if ((gb->printer.command_id & 0xF) == GB_PRINTER_INIT_COMMAND) { - /* Games expect INIT commands to return 0? */ - gb->printer.byte_to_send = 0; - } - 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) { - gb->printer.status = 4; /* Done */ - } - - gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1; - handle_command(gb); - return; - } - - if (gb->printer.command_state >= GB_PRINTER_COMMAND_ID && gb->printer.command_state < GB_PRINTER_COMMAND_CHECKSUM_LOW) { - gb->printer.checksum += byte_received; - } - - if (gb->printer.command_state != GB_PRINTER_COMMAND_DATA) { - gb->printer.command_state++; - } - - if (gb->printer.command_state == GB_PRINTER_COMMAND_DATA) { - if (gb->printer.length_left == 0) { - gb->printer.command_state++; - } - } -} - -static void serial_start(GB_gameboy_t *gb, bool bit_received) -{ - 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_bit_start_callback(gb, serial_start); - GB_set_serial_transfer_bit_end_callback(gb, serial_end); - gb->printer.callback = callback; -} diff --git a/bsnes/gb/Core-new/printer.h b/bsnes/gb/Core-new/printer.h deleted file mode 100644 index 7cf179ed..00000000 --- a/bsnes/gb/Core-new/printer.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef printer_h -#define printer_h -#include -#include -#include "gb_struct_def.h" -#define GB_PRINTER_MAX_COMMAND_LENGTH 0x280 -#define GB_PRINTER_DATA_SIZE 0x280 - -typedef void (*GB_print_image_callback_t)(GB_gameboy_t *gb, - uint32_t *image, - uint8_t height, - uint8_t top_margin, - uint8_t bottom_margin, - uint8_t exposure); - - -typedef struct -{ - /* Communication state machine */ - - enum { - GB_PRINTER_COMMAND_MAGIC1, - GB_PRINTER_COMMAND_MAGIC2, - GB_PRINTER_COMMAND_ID, - GB_PRINTER_COMMAND_COMPRESSION, - GB_PRINTER_COMMAND_LENGTH_LOW, - GB_PRINTER_COMMAND_LENGTH_HIGH, - GB_PRINTER_COMMAND_DATA, - GB_PRINTER_COMMAND_CHECKSUM_LOW, - GB_PRINTER_COMMAND_CHECKSUM_HIGH, - GB_PRINTER_COMMAND_ACTIVE, - GB_PRINTER_COMMAND_STATUS, - } command_state : 8; - enum { - GB_PRINTER_INIT_COMMAND = 1, - GB_PRINTER_START_COMMAND = 2, - GB_PRINTER_DATA_COMMAND = 4, - GB_PRINTER_NOP_COMMAND = 0xF, - } command_id : 8; - bool compression; - uint16_t length_left; - uint8_t command_data[GB_PRINTER_MAX_COMMAND_LENGTH]; - uint16_t command_length; - uint16_t checksum; - uint8_t status; - uint8_t byte_to_send; - - uint8_t image[160 * 200]; - uint16_t image_offset; - - GB_print_image_callback_t callback; - - 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; - - -void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback); -#endif diff --git a/bsnes/gb/Core-new/random.c b/bsnes/gb/Core-new/random.c deleted file mode 100644 index cc4d4d3a..00000000 --- a/bsnes/gb/Core-new/random.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "random.h" -#include - -static uint64_t seed; -static bool enabled = true; - -uint8_t GB_random(void) -{ - if (!enabled) return 0; - - seed *= 0x27BB2EE687B0B0FDL; - seed += 0xB504F32D; - return seed >> 56; -} - -uint32_t GB_random32(void) -{ - GB_random(); - return seed >> 32; -} - -void GB_random_seed(uint64_t new_seed) -{ - seed = new_seed; -} - -void GB_random_set_enabled(bool enable) -{ - enabled = enable; -} - -static void __attribute__((constructor)) init_seed(void) -{ - seed = time(NULL); - for (unsigned i = 64; i--;) { - GB_random(); - } -} diff --git a/bsnes/gb/Core-new/random.h b/bsnes/gb/Core-new/random.h deleted file mode 100644 index 8ab0e502..00000000 --- a/bsnes/gb/Core-new/random.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef random_h -#define random_h - -#include -#include - -uint8_t GB_random(void); -uint32_t GB_random32(void); -void GB_random_seed(uint64_t seed); -void GB_random_set_enabled(bool enable); - -#endif /* random_h */ diff --git a/bsnes/gb/Core-new/rewind.c b/bsnes/gb/Core-new/rewind.c deleted file mode 100644 index c3900d60..00000000 --- a/bsnes/gb/Core-new/rewind.c +++ /dev/null @@ -1,208 +0,0 @@ -#include "gb.h" -#include -#include -#include -#include - -static uint8_t *state_compress(const uint8_t *prev, const uint8_t *data, size_t uncompressed_size) -{ - size_t malloc_size = 0x1000; - uint8_t *compressed = malloc(malloc_size); - size_t counter_pos = 0; - size_t data_pos = sizeof(uint16_t); - bool prev_mode = true; - *(uint16_t *)compressed = 0; -#define COUNTER (*(uint16_t *)&compressed[counter_pos]) -#define DATA (compressed[data_pos]) - - while (uncompressed_size) { - if (prev_mode) { - if (*data == *prev && COUNTER != 0xffff) { - COUNTER++; - data++; - prev++; - uncompressed_size--; - } - else { - prev_mode = false; - counter_pos += sizeof(uint16_t); - data_pos = counter_pos + sizeof(uint16_t); - if (data_pos >= malloc_size) { - malloc_size *= 2; - compressed = realloc(compressed, malloc_size); - } - COUNTER = 0; - } - } - else { - if (*data != *prev && COUNTER != 0xffff) { - COUNTER++; - DATA = *data; - data_pos++; - data++; - prev++; - uncompressed_size--; - if (data_pos >= malloc_size) { - malloc_size *= 2; - compressed = realloc(compressed, malloc_size); - } - } - else { - prev_mode = true; - counter_pos = data_pos; - data_pos = counter_pos + sizeof(uint16_t); - if (counter_pos >= malloc_size - 1) { - malloc_size *= 2; - compressed = realloc(compressed, malloc_size); - } - COUNTER = 0; - } - } - } - - return realloc(compressed, data_pos); -#undef DATA -#undef COUNTER -} - - -static void state_decompress(const uint8_t *prev, uint8_t *data, uint8_t *dest, size_t uncompressed_size) -{ - size_t counter_pos = 0; - size_t data_pos = sizeof(uint16_t); - bool prev_mode = true; -#define COUNTER (*(uint16_t *)&data[counter_pos]) -#define DATA (data[data_pos]) - - while (uncompressed_size) { - if (prev_mode) { - if (COUNTER) { - COUNTER--; - *(dest++) = *(prev++); - uncompressed_size--; - } - else { - prev_mode = false; - counter_pos += sizeof(uint16_t); - data_pos = counter_pos + sizeof(uint16_t); - } - } - else { - if (COUNTER) { - COUNTER--; - *(dest++) = DATA; - data_pos++; - prev++; - uncompressed_size--; - } - else { - prev_mode = true; - counter_pos = data_pos; - data_pos += sizeof(uint16_t); - } - } - } -#undef DATA -#undef COUNTER -} - -void GB_rewind_push(GB_gameboy_t *gb) -{ - const size_t save_size = GB_get_save_state_size(gb); - if (!gb->rewind_sequences) { - if (gb->rewind_buffer_length) { - gb->rewind_sequences = malloc(sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length); - memset(gb->rewind_sequences, 0, sizeof(*gb->rewind_sequences) * gb->rewind_buffer_length); - gb->rewind_pos = 0; - } - else { - return; - } - } - - if (gb->rewind_sequences[gb->rewind_pos].pos == GB_REWIND_FRAMES_PER_KEY) { - gb->rewind_pos++; - if (gb->rewind_pos == gb->rewind_buffer_length) { - gb->rewind_pos = 0; - } - if (gb->rewind_sequences[gb->rewind_pos].key_state) { - free(gb->rewind_sequences[gb->rewind_pos].key_state); - gb->rewind_sequences[gb->rewind_pos].key_state = NULL; - } - for (unsigned i = 0; i < GB_REWIND_FRAMES_PER_KEY; i++) { - if (gb->rewind_sequences[gb->rewind_pos].compressed_states[i]) { - free(gb->rewind_sequences[gb->rewind_pos].compressed_states[i]); - gb->rewind_sequences[gb->rewind_pos].compressed_states[i] = 0; - } - } - gb->rewind_sequences[gb->rewind_pos].pos = 0; - } - - if (!gb->rewind_sequences[gb->rewind_pos].key_state) { - gb->rewind_sequences[gb->rewind_pos].key_state = malloc(save_size); - GB_save_state_to_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state); - } - else { - uint8_t *save_state = malloc(save_size); - GB_save_state_to_buffer(gb, save_state); - gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos++] = - state_compress(gb->rewind_sequences[gb->rewind_pos].key_state, save_state, save_size); - free(save_state); - } - -} - -bool GB_rewind_pop(GB_gameboy_t *gb) -{ - if (!gb->rewind_sequences || !gb->rewind_sequences[gb->rewind_pos].key_state) { - return false; - } - - const size_t save_size = GB_get_save_state_size(gb); - if (gb->rewind_sequences[gb->rewind_pos].pos == 0) { - GB_load_state_from_buffer(gb, gb->rewind_sequences[gb->rewind_pos].key_state, save_size); - free(gb->rewind_sequences[gb->rewind_pos].key_state); - gb->rewind_sequences[gb->rewind_pos].key_state = NULL; - gb->rewind_pos = gb->rewind_pos == 0? gb->rewind_buffer_length - 1 : gb->rewind_pos - 1; - return true; - } - - uint8_t *save_state = malloc(save_size); - state_decompress(gb->rewind_sequences[gb->rewind_pos].key_state, - gb->rewind_sequences[gb->rewind_pos].compressed_states[--gb->rewind_sequences[gb->rewind_pos].pos], - save_state, - save_size); - free(gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos]); - gb->rewind_sequences[gb->rewind_pos].compressed_states[gb->rewind_sequences[gb->rewind_pos].pos] = NULL; - GB_load_state_from_buffer(gb, save_state, save_size); - free(save_state); - return true; -} - -void GB_rewind_free(GB_gameboy_t *gb) -{ - if (!gb->rewind_sequences) return; - for (unsigned i = 0; i < gb->rewind_buffer_length; i++) { - if (gb->rewind_sequences[i].key_state) { - free(gb->rewind_sequences[i].key_state); - } - for (unsigned j = 0; j < GB_REWIND_FRAMES_PER_KEY; j++) { - if (gb->rewind_sequences[i].compressed_states[j]) { - free(gb->rewind_sequences[i].compressed_states[j]); - } - } - } - free(gb->rewind_sequences); - gb->rewind_sequences = NULL; -} - -void GB_set_rewind_length(GB_gameboy_t *gb, double seconds) -{ - GB_rewind_free(gb); - if (seconds == 0) { - gb->rewind_buffer_length = 0; - } - else { - gb->rewind_buffer_length = (size_t) ceil(seconds * CPU_FREQUENCY / LCDC_PERIOD / GB_REWIND_FRAMES_PER_KEY); - } -} diff --git a/bsnes/gb/Core-new/rewind.h b/bsnes/gb/Core-new/rewind.h deleted file mode 100644 index ad548410..00000000 --- a/bsnes/gb/Core-new/rewind.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef rewind_h -#define rewind_h - -#include -#include "gb_struct_def.h" - -#ifdef GB_INTERNAL -void GB_rewind_push(GB_gameboy_t *gb); -void GB_rewind_free(GB_gameboy_t *gb); -#endif -bool GB_rewind_pop(GB_gameboy_t *gb); -void GB_set_rewind_length(GB_gameboy_t *gb, double seconds); - -#endif diff --git a/bsnes/gb/Core-new/save_state.c b/bsnes/gb/Core-new/save_state.c deleted file mode 100644 index 8ef99aed..00000000 --- a/bsnes/gb/Core-new/save_state.c +++ /dev/null @@ -1,377 +0,0 @@ -#include "gb.h" -#include -#include - -static bool dump_section(FILE *f, const void *src, uint32_t size) -{ - if (fwrite(&size, 1, sizeof(size), f) != sizeof(size)) { - return false; - } - - if (fwrite(src, 1, size, f) != size) { - return false; - } - - return true; -} - -#define DUMP_SECTION(gb, f, section) dump_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section)) - -/* Todo: we need a sane and protable save state format. */ -int GB_save_state(GB_gameboy_t *gb, const char *path) -{ - FILE *f = fopen(path, "wb"); - if (!f) { - GB_log(gb, "Could not open save state: %s.\n", strerror(errno)); - return errno; - } - - if (fwrite(GB_GET_SECTION(gb, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error; - if (!DUMP_SECTION(gb, f, core_state)) goto error; - if (!DUMP_SECTION(gb, f, dma )) goto error; - if (!DUMP_SECTION(gb, f, mbc )) goto error; - if (!DUMP_SECTION(gb, f, hram )) goto error; - if (!DUMP_SECTION(gb, f, timing )) goto error; - if (!DUMP_SECTION(gb, f, apu )) goto error; - if (!DUMP_SECTION(gb, f, rtc )) goto error; - if (!DUMP_SECTION(gb, f, video )) goto error; - - if (GB_is_hle_sgb(gb)) { - if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error; - } - - - if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) { - goto error; - } - - if (fwrite(gb->ram, 1, gb->ram_size, f) != gb->ram_size) { - goto error; - } - - if (fwrite(gb->vram, 1, gb->vram_size, f) != gb->vram_size) { - goto error; - } - - errno = 0; - -error: - fclose(f); - return errno; -} - -#undef DUMP_SECTION - -size_t GB_get_save_state_size(GB_gameboy_t *gb) -{ - return GB_SECTION_SIZE(header) - + GB_SECTION_SIZE(core_state) + sizeof(uint32_t) - + GB_SECTION_SIZE(dma ) + sizeof(uint32_t) - + GB_SECTION_SIZE(mbc ) + sizeof(uint32_t) - + GB_SECTION_SIZE(hram ) + sizeof(uint32_t) - + GB_SECTION_SIZE(timing ) + sizeof(uint32_t) - + GB_SECTION_SIZE(apu ) + sizeof(uint32_t) - + GB_SECTION_SIZE(rtc ) + sizeof(uint32_t) - + GB_SECTION_SIZE(video ) + sizeof(uint32_t) - + (GB_is_hle_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0) - + gb->mbc_ram_size - + gb->ram_size - + gb->vram_size; -} - -/* A write-line function for memory copying */ -static void buffer_write(const void *src, size_t size, uint8_t **dest) -{ - memcpy(*dest, src, size); - *dest += size; -} - -static void buffer_dump_section(uint8_t **buffer, const void *src, uint32_t size) -{ - buffer_write(&size, sizeof(size), buffer); - buffer_write(src, size, buffer); -} - -#define DUMP_SECTION(gb, buffer, section) buffer_dump_section(&buffer, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section)) -void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer) -{ - buffer_write(GB_GET_SECTION(gb, header), GB_SECTION_SIZE(header), &buffer); - DUMP_SECTION(gb, buffer, core_state); - DUMP_SECTION(gb, buffer, dma ); - DUMP_SECTION(gb, buffer, mbc ); - DUMP_SECTION(gb, buffer, hram ); - DUMP_SECTION(gb, buffer, timing ); - DUMP_SECTION(gb, buffer, apu ); - DUMP_SECTION(gb, buffer, rtc ); - DUMP_SECTION(gb, buffer, video ); - - if (GB_is_hle_sgb(gb)) { - buffer_dump_section(&buffer, gb->sgb, sizeof(*gb->sgb)); - } - - - buffer_write(gb->mbc_ram, gb->mbc_ram_size, &buffer); - buffer_write(gb->ram, gb->ram_size, &buffer); - buffer_write(gb->vram, gb->vram_size, &buffer); -} - -/* Best-effort read function for maximum future compatibility. */ -static bool read_section(FILE *f, void *dest, uint32_t size) -{ - uint32_t saved_size = 0; - if (fread(&saved_size, 1, sizeof(size), f) != sizeof(size)) { - return false; - } - - if (saved_size <= size) { - if (fread(dest, 1, saved_size, f) != saved_size) { - return false; - } - } - else { - if (fread(dest, 1, size, f) != size) { - return false; - } - fseek(f, saved_size - size, SEEK_CUR); - } - - return true; -} -#undef DUMP_SECTION - -static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save) -{ - if (gb->magic != save->magic) { - GB_log(gb, "The file is not a save state, or is from an incompatible operating system.\n"); - return false; - } - - if (gb->version != save->version) { - GB_log(gb, "The save state is for a different version of SameBoy.\n"); - return false; - } - - if (gb->mbc_ram_size < save->mbc_ram_size) { - GB_log(gb, "The save state has non-matching MBC RAM size.\n"); - return false; - } - - if (gb->vram_size != save->vram_size) { - GB_log(gb, "The save state has non-matching VRAM size. Try changing the emulated model.\n"); - return false; - } - - if (GB_is_hle_sgb(gb) != GB_is_hle_sgb(save)) { - GB_log(gb, "The save state is %sfor a Super Game Boy. Try changing the emulated model.\n", GB_is_hle_sgb(save)? "" : "not "); - return false; - } - - if (gb->ram_size != save->ram_size) { - if (gb->ram_size == 0x1000 * 8 && save->ram_size == 0x2000 * 8) { - /* A bug in versions prior to 0.12 made CGB instances allocate twice the ammount of RAM. - Ignore this issue to retain compatibility with older, 0.11, save states. */ - } - else { - GB_log(gb, "The save state has non-matching RAM size. Try changing the emulated model.\n"); - return false; - } - } - - return true; -} - -#define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section)) - -int GB_load_state(GB_gameboy_t *gb, const char *path) -{ - GB_gameboy_t save; - - /* Every unread value should be kept the same. */ - memcpy(&save, gb, sizeof(save)); - /* ...Except ram size, we use it to detect old saves with incorrect ram sizes */ - save.ram_size = 0; - - FILE *f = fopen(path, "rb"); - if (!f) { - GB_log(gb, "Could not open save state: %s.\n", strerror(errno)); - return errno; - } - - if (fread(GB_GET_SECTION(&save, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error; - if (!READ_SECTION(&save, f, core_state)) goto error; - if (!READ_SECTION(&save, f, dma )) goto error; - if (!READ_SECTION(&save, f, mbc )) goto error; - if (!READ_SECTION(&save, f, hram )) goto error; - if (!READ_SECTION(&save, f, timing )) goto error; - if (!READ_SECTION(&save, f, apu )) goto error; - if (!READ_SECTION(&save, f, rtc )) goto error; - if (!READ_SECTION(&save, f, video )) goto error; - - if (save.ram_size == 0) { - /* Save doesn't have ram size specified, it's a pre 0.12 save state with potentially - incorrect RAM amount if it's a CGB instance */ - if (GB_is_cgb(&save)) { - save.ram_size = 0x2000 * 8; // Incorrect RAM size - } - else { - save.ram_size = gb->ram_size; - } - } - - if (!verify_state_compatibility(gb, &save)) { - errno = -1; - goto error; - } - - if (GB_is_hle_sgb(gb)) { - if (!read_section(f, gb->sgb, sizeof(*gb->sgb))) goto error; - } - - memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size); - if (fread(gb->mbc_ram, 1, save.mbc_ram_size, f) != save.mbc_ram_size) { - fclose(f); - return EIO; - } - - if (fread(gb->ram, 1, gb->ram_size, f) != gb->ram_size) { - fclose(f); - return EIO; - } - - /* Fix for 0.11 save states that allocate twice the amount of RAM in CGB instances */ - fseek(f, save.ram_size - gb->ram_size, SEEK_CUR); - - if (fread(gb->vram, 1, gb->vram_size, f) != gb->vram_size) { - fclose(f); - return EIO; - } - - size_t orig_ram_size = gb->ram_size; - memcpy(gb, &save, sizeof(save)); - gb->ram_size = orig_ram_size; - - errno = 0; - - if (gb->cartridge_type->has_rumble && gb->rumble_callback) { - gb->rumble_callback(gb, gb->rumble_state); - } - - for (unsigned i = 0; i < 32; i++) { - GB_palette_changed(gb, false, i * 2); - GB_palette_changed(gb, true, i * 2); - } - - gb->bg_fifo.read_end &= 0xF; - gb->bg_fifo.write_end &= 0xF; - gb->oam_fifo.read_end &= 0xF; - gb->oam_fifo.write_end &= 0xF; - -error: - fclose(f); - return errno; -} - -#undef READ_SECTION - -/* An read-like function for buffer-copying */ -static size_t buffer_read(void *dest, size_t length, const uint8_t **buffer, size_t *buffer_length) -{ - if (length > *buffer_length) { - length = *buffer_length; - } - - memcpy(dest, *buffer, length); - *buffer += length; - *buffer_length -= length; - - return length; -} - -static bool buffer_read_section(const uint8_t **buffer, size_t *buffer_length, void *dest, uint32_t size) -{ - uint32_t saved_size = 0; - if (buffer_read(&saved_size, sizeof(size), buffer, buffer_length) != sizeof(size)) { - return false; - } - - if (saved_size > *buffer_length) return false; - - if (saved_size <= size) { - if (buffer_read(dest, saved_size, buffer, buffer_length) != saved_size) { - return false; - } - } - else { - if (buffer_read(dest, size, buffer, buffer_length) != size) { - return false; - } - *buffer += saved_size - size; - *buffer_length -= saved_size - size; - } - - return true; -} - -#define READ_SECTION(gb, buffer, length, section) buffer_read_section(&buffer, &length, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section)) -int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length) -{ - GB_gameboy_t save; - - /* Every unread value should be kept the same. */ - memcpy(&save, gb, sizeof(save)); - - if (buffer_read(GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header), &buffer, &length) != GB_SECTION_SIZE(header)) return -1; - if (!READ_SECTION(&save, buffer, length, core_state)) return -1; - if (!READ_SECTION(&save, buffer, length, dma )) return -1; - if (!READ_SECTION(&save, buffer, length, mbc )) return -1; - if (!READ_SECTION(&save, buffer, length, hram )) return -1; - if (!READ_SECTION(&save, buffer, length, timing )) return -1; - if (!READ_SECTION(&save, buffer, length, apu )) return -1; - if (!READ_SECTION(&save, buffer, length, rtc )) return -1; - if (!READ_SECTION(&save, buffer, length, video )) return -1; - - if (!verify_state_compatibility(gb, &save)) { - return -1; - } - - if (GB_is_hle_sgb(gb)) { - if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb))) return -1; - } - - memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size); - if (buffer_read(gb->mbc_ram, save.mbc_ram_size, &buffer, &length) != save.mbc_ram_size) { - return -1; - } - - if (buffer_read(gb->ram, gb->ram_size, &buffer, &length) != gb->ram_size) { - return -1; - } - - if (buffer_read(gb->vram,gb->vram_size, &buffer, &length) != gb->vram_size) { - return -1; - } - - /* Fix for 0.11 save states that allocate twice the amount of RAM in CGB instances */ - buffer += save.ram_size - gb->ram_size; - length -= save.ram_size - gb->ram_size; - - memcpy(gb, &save, sizeof(save)); - - if (gb->cartridge_type->has_rumble && gb->rumble_callback) { - gb->rumble_callback(gb, gb->rumble_state); - } - - for (unsigned i = 0; i < 32; i++) { - GB_palette_changed(gb, false, i * 2); - GB_palette_changed(gb, true, i * 2); - } - - gb->bg_fifo.read_end &= 0xF; - gb->bg_fifo.write_end &= 0xF; - gb->oam_fifo.read_end &= 0xF; - gb->oam_fifo.write_end &= 0xF; - - return 0; -} - -#undef READ_SECTION diff --git a/bsnes/gb/Core-new/save_state.h b/bsnes/gb/Core-new/save_state.h deleted file mode 100644 index 546ac2d9..00000000 --- a/bsnes/gb/Core-new/save_state.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Macros to make the GB_gameboy_t struct more future compatible when state saving */ -#ifndef save_state_h -#define save_state_h -#include - -#define GB_PADDING(type, old_usage) type old_usage##__do_not_use - -#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) struct {} name##_section_start; __VA_ARGS__; struct {} name##_section_end -#define GB_SECTION_OFFSET(name) (offsetof(GB_gameboy_t, name##_section_start)) -#define GB_SECTION_SIZE(name) (offsetof(GB_gameboy_t, name##_section_end) - offsetof(GB_gameboy_t, name##_section_start)) -#define GB_GET_SECTION(gb, name) ((void*)&((gb)->name##_section_start)) - -#define GB_aligned_double __attribute__ ((aligned (8))) double - - -/* Public calls related to save states */ -int GB_save_state(GB_gameboy_t *gb, const char *path); -size_t GB_get_save_state_size(GB_gameboy_t *gb); -/* Assumes buffer is big enough to contain the save state. Use with GB_get_save_state_size(). */ -void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer); - -int GB_load_state(GB_gameboy_t *gb, const char *path); -int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length); -#endif /* save_state_h */ diff --git a/bsnes/gb/Core-new/sgb.c b/bsnes/gb/Core-new/sgb.c deleted file mode 100644 index 8539238c..00000000 --- a/bsnes/gb/Core-new/sgb.c +++ /dev/null @@ -1,846 +0,0 @@ -#include "gb.h" -#include "random.h" -#include -#include - -#ifndef M_PI - #define M_PI 3.14159265358979323846 -#endif - -#define INTRO_ANIMATION_LENGTH 200 - -enum { - PAL01 = 0x00, - PAL23 = 0x01, - PAL03 = 0x02, - PAL12 = 0x03, - ATTR_BLK = 0x04, - ATTR_LIN = 0x05, - ATTR_DIV = 0x06, - PAL_SET = 0x0A, - PAL_TRN = 0x0B, - DATA_SND = 0x0F, - MLT_REQ = 0x11, - CHR_TRN = 0x13, - PCT_TRN = 0x14, - ATTR_TRN = 0x15, - ATTR_SET = 0x16, - MASK_EN = 0x17, -}; - -typedef enum { - MASK_DISABLED, - MASK_FREEZE, - MASK_BLACK, - MASK_COLOR_0, -} mask_mode_t; - -typedef enum { - TRANSFER_LOW_TILES, - TRANSFER_HIGH_TILES, - TRANSFER_BORDER_DATA, - TRANSFER_PALETTES, - TRANSFER_ATTRIBUTES, -} transfer_dest_t; - -#define SGB_PACKET_SIZE 16 -static inline void pal_command(GB_gameboy_t *gb, unsigned first, unsigned second) -{ - gb->sgb->effective_palettes[0] = gb->sgb->effective_palettes[4] = - gb->sgb->effective_palettes[8] = gb->sgb->effective_palettes[12] = - gb->sgb->command[1] | (gb->sgb->command[2] << 8); - - for (unsigned i = 0; i < 3; i++) { - gb->sgb->effective_palettes[first * 4 + i + 1] = gb->sgb->command[3 + i * 2] | (gb->sgb->command[4 + i * 2] << 8); - } - - for (unsigned i = 0; i < 3; i++) { - gb->sgb->effective_palettes[second * 4 + i + 1] = gb->sgb->command[9 + i * 2] | (gb->sgb->command[10 + i * 2] << 8); - } -} - -static inline void load_attribute_file(GB_gameboy_t *gb, unsigned file_index) -{ - if (file_index > 0x2C) return; - uint8_t *output = gb->sgb->attribute_map; - for (unsigned i = 0; i < 90; i++) { - uint8_t byte = gb->sgb->attribute_files[file_index * 90 + i]; - for (unsigned j = 4; j--;) { - *(output++) = byte >> 6; - byte <<= 2; - } - } -} - -static const uint16_t built_in_palettes[] = -{ - 0x67BF, 0x265B, 0x10B5, 0x2866, - 0x637B, 0x3AD9, 0x0956, 0x0000, - 0x7F1F, 0x2A7D, 0x30F3, 0x4CE7, - 0x57FF, 0x2618, 0x001F, 0x006A, - 0x5B7F, 0x3F0F, 0x222D, 0x10EB, - 0x7FBB, 0x2A3C, 0x0015, 0x0900, - 0x2800, 0x7680, 0x01EF, 0x2FFF, - 0x73BF, 0x46FF, 0x0110, 0x0066, - 0x533E, 0x2638, 0x01E5, 0x0000, - 0x7FFF, 0x2BBF, 0x00DF, 0x2C0A, - 0x7F1F, 0x463D, 0x74CF, 0x4CA5, - 0x53FF, 0x03E0, 0x00DF, 0x2800, - 0x433F, 0x72D2, 0x3045, 0x0822, - 0x7FFA, 0x2A5F, 0x0014, 0x0003, - 0x1EED, 0x215C, 0x42FC, 0x0060, - 0x7FFF, 0x5EF7, 0x39CE, 0x0000, - 0x4F5F, 0x630E, 0x159F, 0x3126, - 0x637B, 0x121C, 0x0140, 0x0840, - 0x66BC, 0x3FFF, 0x7EE0, 0x2C84, - 0x5FFE, 0x3EBC, 0x0321, 0x0000, - 0x63FF, 0x36DC, 0x11F6, 0x392A, - 0x65EF, 0x7DBF, 0x035F, 0x2108, - 0x2B6C, 0x7FFF, 0x1CD9, 0x0007, - 0x53FC, 0x1F2F, 0x0E29, 0x0061, - 0x36BE, 0x7EAF, 0x681A, 0x3C00, - 0x7BBE, 0x329D, 0x1DE8, 0x0423, - 0x739F, 0x6A9B, 0x7293, 0x0001, - 0x5FFF, 0x6732, 0x3DA9, 0x2481, - 0x577F, 0x3EBC, 0x456F, 0x1880, - 0x6B57, 0x6E1B, 0x5010, 0x0007, - 0x0F96, 0x2C97, 0x0045, 0x3200, - 0x67FF, 0x2F17, 0x2230, 0x1548, -}; - -static const struct { - char name[16]; - unsigned palette_index; -} palette_assignments[] = -{ - {"ZELDA", 5}, - {"SUPER MARIOLAND", 6}, - {"MARIOLAND2", 0x14}, - {"SUPERMARIOLAND3", 2}, - {"KIRBY DREAM LAND", 0xB}, - {"HOSHINOKA-BI", 0xB}, - {"KIRBY'S PINBALL", 3}, - {"YOSSY NO TAMAGO", 0xC}, - {"MARIO & YOSHI", 0xC}, - {"YOSSY NO COOKIE", 4}, - {"YOSHI'S COOKIE", 4}, - {"DR.MARIO", 0x12}, - {"TETRIS", 0x11}, - {"YAKUMAN", 0x13}, - {"METROID2", 0x1F}, - {"KAERUNOTAMENI", 9}, - {"GOLF", 0x18}, - {"ALLEY WAY", 0x16}, - {"BASEBALL", 0xF}, - {"TENNIS", 0x17}, - {"F1RACE", 0x1E}, - {"KID ICARUS", 0xE}, - {"QIX", 0x19}, - {"SOLARSTRIKER", 7}, - {"X", 0x1C}, - {"GBWARS", 0x15}, -}; - -static void command_ready(GB_gameboy_t *gb) -{ - /* SGB header commands are used to send the contents of the header to the SNES CPU. - A header command looks like this: - Command ID: 0b1111xxx1, where xxx is the packet index. (e.g. F1 for [0x104, 0x112), F3 for [0x112, 0x120)) - Checksum: Simple one byte sum for the following content bytes - 0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */ - - if ((gb->sgb->command[0] & 0xF1) == 0xF1) { - if(gb->boot_rom_finished) return; - - uint8_t checksum = 0; - for (unsigned i = 2; i < 0x10; i++) { - checksum += gb->sgb->command[i]; - } - if (checksum != gb->sgb->command[1]) { - GB_log(gb, "Failed checksum for SGB header command, disabling SGB features\n"); - gb->sgb->disable_commands = true; - return; - } - unsigned index = (gb->sgb->command[0] >> 1) & 7; - if (index > 5) { - return; - } - memcpy(&gb->sgb->received_header[index * 14], &gb->sgb->command[2], 14); - if (gb->sgb->command[0] == 0xfb) { - if (gb->sgb->received_header[0x42] != 3 || gb->sgb->received_header[0x47] != 0x33) { - gb->sgb->disable_commands = true; - for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) { - if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) { - gb->sgb->effective_palettes[0] = built_in_palettes[palette_assignments[i].palette_index * 4 - 4]; - gb->sgb->effective_palettes[1] = built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4]; - gb->sgb->effective_palettes[2] = built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4]; - gb->sgb->effective_palettes[3] = built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4]; - break; - } - } - } - } - return; - } - - /* Ignore malformed commands (0 length)*/ - if ((gb->sgb->command[0] & 7) == 0) return; - - switch (gb->sgb->command[0] >> 3) { - case PAL01: - pal_command(gb, 0, 1); - break; - case PAL23: - pal_command(gb, 2, 3); - break; - case PAL03: - pal_command(gb, 0, 3); - break; - case PAL12: - pal_command(gb, 1, 2); - break; - case ATTR_BLK: { - struct { - uint8_t count; - struct { - uint8_t control; - uint8_t palettes; - uint8_t left, top, right, bottom; - } data[]; - } *command = (void *)(gb->sgb->command + 1); - if (command->count > 0x12) return; - - for (unsigned i = 0; i < command->count; i++) { - bool inside = command->data[i].control & 1; - bool middle = command->data[i].control & 2; - bool outside = command->data[i].control & 4; - uint8_t inside_palette = command->data[i].palettes & 0x3; - uint8_t middle_palette = (command->data[i].palettes >> 2) & 0x3; - uint8_t outside_palette = (command->data[i].palettes >> 4) & 0x3; - - if (inside && !middle && !outside) { - middle = true; - middle_palette = inside_palette; - } - else if (outside && !middle && !inside) { - middle = true; - middle_palette = outside_palette; - } - - command->data[i].left &= 0x1F; - command->data[i].top &= 0x1F; - command->data[i].right &= 0x1F; - command->data[i].bottom &= 0x1F; - - for (unsigned y = 0; y < 18; y++) { - for (unsigned x = 0; x < 20; x++) { - if (x < command->data[i].left || x > command->data[i].right || - y < command->data[i].top || y > command->data[i].bottom) { - if (outside) { - gb->sgb->attribute_map[x + 20 * y] = outside_palette; - } - } - else if (x > command->data[i].left && x < command->data[i].right && - y > command->data[i].top && y < command->data[i].bottom) { - if (inside) { - gb->sgb->attribute_map[x + 20 * y] = inside_palette; - } - } - else if(middle) { - gb->sgb->attribute_map[x + 20 * y] = middle_palette; - } - } - } - } - break; - } - case ATTR_LIN: { - struct { - uint8_t count; - uint8_t data[]; - } *command = (void *)(gb->sgb->command + 1); - if (command->count > sizeof(gb->sgb->command) - 2) return; - - for (unsigned i = 0; i < command->count; i++) { - bool horizontal = command->data[i] & 0x80; - uint8_t palette = (command->data[i] >> 5) & 0x3; - uint8_t line = (command->data[i]) & 0x1F; - - if (horizontal) { - if (line > 18) continue; - for (unsigned x = 0; x < 20; x++) { - gb->sgb->attribute_map[x + 20 * line] = palette; - } - } - else { - if (line > 20) continue; - for (unsigned y = 0; y < 18; y++) { - gb->sgb->attribute_map[line + 20 * y] = palette; - } - } - } - break; - } - case ATTR_DIV: { - uint8_t high_palette = gb->sgb->command[1] & 3; - uint8_t low_palette = (gb->sgb->command[1] >> 2) & 3; - uint8_t middle_palette = (gb->sgb->command[1] >> 4) & 3; - bool horizontal = gb->sgb->command[1] & 0x40; - uint8_t line = gb->sgb->command[2] & 0x1F; - - for (unsigned y = 0; y < 18; y++) { - for (unsigned x = 0; x < 20; x++) { - if ((horizontal? y : x) < line) { - gb->sgb->attribute_map[x + 20 * y] = low_palette; - } - else if ((horizontal? y : x) == line) { - gb->sgb->attribute_map[x + 20 * y] = middle_palette; - } - else { - gb->sgb->attribute_map[x + 20 * y] = high_palette; - } - } - } - - break; - } - case PAL_SET: - memcpy(&gb->sgb->effective_palettes[0], - &gb->sgb->ram_palettes[4 * (gb->sgb->command[1] + (gb->sgb->command[2] & 1) * 0x100)], - 8); - memcpy(&gb->sgb->effective_palettes[4], - &gb->sgb->ram_palettes[4 * (gb->sgb->command[3] + (gb->sgb->command[4] & 1) * 0x100)], - 8); - memcpy(&gb->sgb->effective_palettes[8], - &gb->sgb->ram_palettes[4 * (gb->sgb->command[5] + (gb->sgb->command[6] & 1) * 0x100)], - 8); - memcpy(&gb->sgb->effective_palettes[12], - &gb->sgb->ram_palettes[4 * (gb->sgb->command[7] + (gb->sgb->command[8] & 1) * 0x100)], - 8); - - gb->sgb->effective_palettes[12] = gb->sgb->effective_palettes[8] = - gb->sgb->effective_palettes[4] = gb->sgb->effective_palettes[0]; - - if (gb->sgb->command[9] & 0x80) { - load_attribute_file(gb, gb->sgb->command[9] & 0x3F); - } - - if (gb->sgb->command[9] & 0x40) { - gb->sgb->mask_mode = MASK_DISABLED; - } - break; - case PAL_TRN: - gb->sgb->vram_transfer_countdown = 2; - gb->sgb->transfer_dest = TRANSFER_PALETTES; - break; - case DATA_SND: - // Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this - break; - case MLT_REQ: - if (gb->sgb->player_count == 1) { - gb->sgb->current_player = 0; - } - gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility, - fix this to be 0 based. */ - if (gb->sgb->player_count == 3) { - gb->sgb->current_player++; - } - gb->sgb->mlt_lock = true; - break; - case CHR_TRN: - gb->sgb->vram_transfer_countdown = 2; - gb->sgb->transfer_dest = (gb->sgb->command[1] & 1)? TRANSFER_HIGH_TILES : TRANSFER_LOW_TILES; - break; - case PCT_TRN: - gb->sgb->vram_transfer_countdown = 2; - gb->sgb->transfer_dest = TRANSFER_BORDER_DATA; - break; - case ATTR_TRN: - gb->sgb->vram_transfer_countdown = 2; - gb->sgb->transfer_dest = TRANSFER_ATTRIBUTES; - break; - case ATTR_SET: - load_attribute_file(gb, gb->sgb->command[0] & 0x3F); - - if (gb->sgb->command[0] & 0x40) { - gb->sgb->mask_mode = MASK_DISABLED; - } - break; - case MASK_EN: - gb->sgb->mask_mode = gb->sgb->command[1] & 3; - break; - default: - if ((gb->sgb->command[0] >> 3) == 8 && - (gb->sgb->command[1] & ~0x80) == 0 && - (gb->sgb->command[2] & ~0x80) == 0) { - /* Mute/dummy sound commands, ignore this command as it's used by many games at startup */ - break; - } - GB_log(gb, "Unimplemented SGB command %x: ", gb->sgb->command[0] >> 3); - for (unsigned i = 0; i < gb->sgb->command_write_index / 8; i++) { - GB_log(gb, "%02x ", gb->sgb->command[i]); - } - GB_log(gb, "\n"); - } -} - -void GB_sgb_write(GB_gameboy_t *gb, uint8_t value) -{ - if (gb->joyp_write_callback) { - gb->joyp_write_callback(gb, value); - } - - if (!GB_is_sgb(gb)) return; - if (!GB_is_hle_sgb(gb)) { - /* Notify via callback */ - return; - } - if (gb->sgb->disable_commands) return; - if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) { - return; - } - - uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8; - if ((gb->sgb->command[0] & 0xF1) == 0xF1) { - command_size = SGB_PACKET_SIZE * 8; - } - - if ((value & 0x20) == 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) != 0) { - gb->sgb->mlt_lock ^= true; - } - - switch ((value >> 4) & 3) { - case 3: - gb->sgb->ready_for_pulse = true; - if ((gb->sgb->player_count & 1) == 0 && !gb->sgb->mlt_lock) { - gb->sgb->current_player++; - gb->sgb->current_player &= 3; - gb->sgb->mlt_lock = true; - } - break; - - case 2: // Zero - if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return; - if (gb->sgb->ready_for_stop) { - if (gb->sgb->command_write_index == command_size) { - command_ready(gb); - gb->sgb->command_write_index = 0; - memset(gb->sgb->command, 0, sizeof(gb->sgb->command)); - } - gb->sgb->ready_for_pulse = false; - gb->sgb->ready_for_write = false; - gb->sgb->ready_for_stop = false; - } - else { - gb->sgb->command_write_index++; - gb->sgb->ready_for_pulse = false; - if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) { - gb->sgb->ready_for_stop = true; - } - } - break; - case 1: // One - if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return; - if (gb->sgb->ready_for_stop) { - GB_log(gb, "Corrupt SGB command.\n"); - gb->sgb->ready_for_pulse = false; - gb->sgb->ready_for_write = false; - gb->sgb->command_write_index = 0; - memset(gb->sgb->command, 0, sizeof(gb->sgb->command)); - } - else { - gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7); - gb->sgb->command_write_index++; - gb->sgb->ready_for_pulse = false; - if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) { - gb->sgb->ready_for_stop = true; - } - } - break; - - case 0: - if (!gb->sgb->ready_for_pulse) return; - gb->sgb->ready_for_write = true; - gb->sgb->ready_for_pulse = false; - if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) != 0 || - gb->sgb->command_write_index == 0 || - gb->sgb->ready_for_stop) { - gb->sgb->command_write_index = 0; - memset(gb->sgb->command, 0, sizeof(gb->sgb->command)); - gb->sgb->ready_for_stop = false; - } - break; - - default: - break; - } -} - -static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color) -{ - return GB_convert_rgb15(gb, color); -} - -static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade) -{ - uint8_t r = ((color) & 0x1F) - fade; - uint8_t g = ((color >> 5) & 0x1F) - fade; - uint8_t b = ((color >> 10) & 0x1F) - fade; - - if (r >= 0x20) r = 0; - if (g >= 0x20) g = 0; - if (b >= 0x20) b = 0; - - color = r | (g << 5) | (b << 10); - - return GB_convert_rgb15(gb, color); -} - -#include -static void render_boot_animation (GB_gameboy_t *gb) -{ -#include "sgb_animation_logo.inc" - uint32_t *output = &gb->screen[48 + 40 * 256]; - uint8_t *input = animation_logo; - unsigned fade_blue = 0; - unsigned fade_red = 0; - if (gb->sgb->intro_animation < 80 - 32) { - fade_blue = 32; - } - else if (gb->sgb->intro_animation < 80) { - fade_blue = 80 - gb->sgb->intro_animation; - } - else if (gb->sgb->intro_animation > INTRO_ANIMATION_LENGTH - 32) { - fade_red = fade_blue = gb->sgb->intro_animation - INTRO_ANIMATION_LENGTH + 32; - } - uint32_t colors[] = { - convert_rgb15(gb, 0), - convert_rgb15_with_fade(gb, 0x14A5, fade_blue), - convert_rgb15_with_fade(gb, 0x54E0, fade_blue), - convert_rgb15_with_fade(gb, 0x0019, fade_red), - convert_rgb15(gb, 0x0011), - convert_rgb15(gb, 0x0009), - }; - unsigned y_min = (144 - animation_logo_height) / 2; - unsigned y_max = y_min + animation_logo_height; - for (unsigned y = 0; y < 144; y++) { - for (unsigned x = 0; x < 160; x++) { - if (y < y_min || y >= y_max) { - *(output++) = colors[0]; - } - else { - uint8_t color = *input; - if (color >= 3) { - if (color == gb->sgb->intro_animation / 2 - 3) { - color = 5; - } - else if (color == gb->sgb->intro_animation / 2 - 4) { - color = 4; - } - else if (color < gb->sgb->intro_animation / 2 - 4) { - color = 3; - } - else { - color = 0; - } - } - *(output++) = colors[color]; - input++; - } - } - output += 256 - 160; - } -} - -static void render_jingle(GB_gameboy_t *gb, size_t count); -void GB_sgb_render(GB_gameboy_t *gb) -{ - if (gb->apu_output.sample_rate) { - render_jingle(gb, gb->apu_output.sample_rate / GB_get_usual_frame_rate(gb)); - } - - if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++; - - if (!gb->screen || !gb->rgb_encode_callback) return; - - if (gb->sgb->mask_mode != MASK_FREEZE) { - memcpy(gb->sgb->effective_screen_buffer, - gb->sgb->screen_buffer, - sizeof(gb->sgb->effective_screen_buffer)); - } - - if (gb->sgb->vram_transfer_countdown) { - if (--gb->sgb->vram_transfer_countdown == 0) { - if (gb->sgb->transfer_dest == TRANSFER_LOW_TILES || gb->sgb->transfer_dest == TRANSFER_HIGH_TILES) { - uint8_t *base = &gb->sgb->pending_border.tiles[gb->sgb->transfer_dest == TRANSFER_HIGH_TILES ? 0x80 * 8 * 8 : 0]; - for (unsigned tile = 0; tile < 0x80; tile++) { - unsigned tile_x = (tile % 10) * 16; - unsigned tile_y = (tile / 10) * 8; - for (unsigned y = 0; y < 0x8; y++) { - for (unsigned x = 0; x < 0x8; x++) { - base[tile * 8 * 8 + y * 8 + x] = gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] + - gb->sgb->screen_buffer[(tile_x + x + 8) + (tile_y + y) * 160] * 4; - } - } - } - - } - else { - unsigned size = 0; - uint16_t *data = NULL; - - switch (gb->sgb->transfer_dest) { - case TRANSFER_PALETTES: - size = 0x100; - data = gb->sgb->ram_palettes; - break; - case TRANSFER_BORDER_DATA: - size = 0x88; - data = gb->sgb->pending_border.raw_data; - break; - case TRANSFER_ATTRIBUTES: - size = 0xFE; - data = (uint16_t *)gb->sgb->attribute_files; - break; - default: - return; // Corrupt state? - } - - for (unsigned tile = 0; tile < size; tile++) { - unsigned tile_x = (tile % 20) * 8; - unsigned tile_y = (tile / 20) * 8; - for (unsigned y = 0; y < 0x8; y++) { - static const uint16_t pixel_to_bits[4] = {0x0000, 0x0080, 0x8000, 0x8080}; - *data = 0; - for (unsigned x = 0; x < 8; x++) { - *data |= pixel_to_bits[gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] & 3] >> x; - } -#ifdef GB_BIG_ENDIAN - if (gb->sgb->transfer_dest == TRANSFER_ATTRIBUTES) { - *data = __builtin_bswap16(*data); - } -#endif - data++; - } - } - if (gb->sgb->transfer_dest == TRANSFER_BORDER_DATA) { - gb->sgb->border_animation = 64; - } - } - } - } - - uint32_t colors[4 * 4]; - for (unsigned i = 0; i < 4 * 4; i++) { - colors[i] = convert_rgb15(gb, gb->sgb->effective_palettes[i]); - } - - if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) { - render_boot_animation(gb); - } - else { - uint32_t *output = &gb->screen[48 + 40 * 256]; - uint8_t *input = gb->sgb->effective_screen_buffer; - switch ((mask_mode_t) gb->sgb->mask_mode) { - case MASK_DISABLED: - case MASK_FREEZE: { - for (unsigned y = 0; y < 144; y++) { - for (unsigned x = 0; x < 160; x++) { - uint8_t palette = gb->sgb->attribute_map[x / 8 + y / 8 * 20] & 3; - *(output++) = colors[(*(input++) & 3) + palette * 4]; - } - output += 256 - 160; - } - break; - } - case MASK_BLACK: - { - uint32_t black = convert_rgb15(gb, 0); - for (unsigned y = 0; y < 144; y++) { - for (unsigned x = 0; x < 160; x++) { - *(output++) = black; - } - output += 256 - 160; - } - break; - } - case MASK_COLOR_0: - { - for (unsigned y = 0; y < 144; y++) { - for (unsigned x = 0; x < 160; x++) { - *(output++) = colors[0]; - } - output += 256 - 160; - } - break; - } - } - } - - uint32_t border_colors[16 * 4]; - if (gb->sgb->border_animation == 0 || gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) { - for (unsigned i = 0; i < 16 * 4; i++) { - border_colors[i] = convert_rgb15(gb, gb->sgb->border.palette[i]); - } - } - else if (gb->sgb->border_animation > 32) { - gb->sgb->border_animation--; - for (unsigned i = 0; i < 16 * 4; i++) { - border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], 64 - gb->sgb->border_animation); - } - } - else { - gb->sgb->border_animation--; - for (unsigned i = 0; i < 16 * 4; i++) { - border_colors[i] = convert_rgb15_with_fade(gb, gb->sgb->border.palette[i], gb->sgb->border_animation); - } - } - - - if (gb->sgb->border_animation == 32) { - memcpy(&gb->sgb->border, &gb->sgb->pending_border, sizeof(gb->sgb->border)); - } - - for (unsigned tile_y = 0; tile_y < 28; tile_y++) { - for (unsigned tile_x = 0; tile_x < 32; tile_x++) { - bool gb_area = false; - if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) { - gb_area = true; - } - uint16_t tile = gb->sgb->border.map[tile_x + tile_y * 32]; - uint8_t flip_x = (tile & 0x4000)? 0x7 : 0; - uint8_t flip_y = (tile & 0x8000)? 0x7 : 0; - uint8_t palette = (tile >> 10) & 3; - for (unsigned y = 0; y < 8; y++) { - for (unsigned x = 0; x < 8; x++) { - uint8_t color = gb->sgb->border.tiles[(tile & 0xFF) * 64 + (x ^ flip_x) + (y ^ flip_y) * 8] & 0xF; - if (color == 0) { - if (gb_area) continue; - gb->screen[tile_x * 8 + x + (tile_y * 8 + y) * 0x100] = colors[0]; - } - else { - gb->screen[tile_x * 8 + x + (tile_y * 8 + y) * 0x100] = border_colors[color + palette * 16]; - } - } - } - } - } -} - -void GB_sgb_load_default_data(GB_gameboy_t *gb) -{ - -#include "sgb_border.inc" - - memcpy(gb->sgb->border.map, tilemap, sizeof(tilemap)); - memcpy(gb->sgb->border.palette, palette, sizeof(palette)); - - /* Expend tileset */ - for (unsigned tile = 0; tile < sizeof(tiles) / 32; tile++) { - for (unsigned y = 0; y < 8; y++) { - for (unsigned x = 0; x < 8; x++) { - gb->sgb->border.tiles[tile * 8 * 8 + y * 8 + x] = - (tiles[tile * 32 + y * 2 + 0] & (1 << (7 ^ x)) ? 1 : 0) | - (tiles[tile * 32 + y * 2 + 1] & (1 << (7 ^ x)) ? 2 : 0) | - (tiles[tile * 32 + y * 2 + 16] & (1 << (7 ^ x)) ? 4 : 0) | - (tiles[tile * 32 + y * 2 + 17] & (1 << (7 ^ x)) ? 8 : 0); - } - } - } - - if (gb->model != GB_MODEL_SGB2) { - /* Delete the "2" */ - gb->sgb->border.map[25 * 32 + 25] = gb->sgb->border.map[25 * 32 + 26] = - gb->sgb->border.map[26 * 32 + 25] = gb->sgb->border.map[26 * 32 + 26] = - gb->sgb->border.map[27 * 32 + 25] = gb->sgb->border.map[27 * 32 + 26] = - gb->sgb->border.map[0]; - - /* Re-center */ - memmove(&gb->sgb->border.map[25 * 32 + 1], &gb->sgb->border.map[25 * 32], (32 * 3 - 1) * sizeof(gb->sgb->border.map[0])); - } - gb->sgb->effective_palettes[0] = built_in_palettes[0]; - gb->sgb->effective_palettes[1] = built_in_palettes[1]; - gb->sgb->effective_palettes[2] = built_in_palettes[2]; - gb->sgb->effective_palettes[3] = built_in_palettes[3]; -} - -static double fm_synth(double phase) -{ - return (sin(phase * M_PI * 2) + - sin(phase * M_PI * 2 + sin(phase * M_PI * 2)) + - sin(phase * M_PI * 2 + sin(phase * M_PI * 3)) + - sin(phase * M_PI * 2 + sin(phase * M_PI * 4))) / 4; -} - -static double fm_sweep(double phase) -{ - double ret = 0; - for (unsigned i = 0; i < 8; i++) { - ret += sin((phase * M_PI * 2 + sin(phase * M_PI * 8) / 4) * pow(1.25, i)) * (8 - i) / 36; - } - return ret; -} -static double random_double(void) -{ - return ((signed)(GB_random32() % 0x10001) - 0x8000) / (double) 0x8000; -} - -static void render_jingle(GB_gameboy_t *gb, size_t count) -{ - const double frequencies[7] = { - 466.16, // Bb4 - 587.33, // D5 - 698.46, // F5 - 830.61, // Ab5 - 1046.50, // C6 - 1244.51, // Eb6 - 1567.98, // G6 - }; - - assert(gb->apu_output.sample_callback); - - if (gb->sgb->intro_animation < 0) { - GB_sample_t sample = {0, 0}; - for (unsigned i = 0; i < count; i++) { - gb->apu_output.sample_callback(gb, &sample); - } - return; - } - - if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return; - - signed jingle_stage = (gb->sgb->intro_animation - 64) / 3; - double sweep_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate; - double sweep_phase_shift = 1000.0 * pow(2, gb->sgb->intro_animation / 40.0) / gb->apu_output.sample_rate; - if (sweep_cutoff_ratio > 1) { - sweep_cutoff_ratio = 1; - } - - GB_sample_t stereo; - for (unsigned i = 0; i < count; i++) { - double sample = 0; - for (signed f = 0; f < 7 && f < jingle_stage; f++) { - sample += fm_synth(gb->sgb_intro_jingle_phases[f]) * - (0.75 * pow(0.5, jingle_stage - f) + 0.25) / 5.0; - gb->sgb_intro_jingle_phases[f] += frequencies[f] / gb->apu_output.sample_rate; - } - if (gb->sgb->intro_animation > 100) { - sample *= pow((INTRO_ANIMATION_LENGTH - gb->sgb->intro_animation) / (INTRO_ANIMATION_LENGTH - 100.0), 3); - } - - if (gb->sgb->intro_animation < 120) { - double next = fm_sweep(gb->sgb_intro_sweep_phase) * 0.3 + random_double() * 0.7; - gb->sgb_intro_sweep_phase += sweep_phase_shift; - - gb->sgb_intro_sweep_previous_sample = next * (sweep_cutoff_ratio) + - gb->sgb_intro_sweep_previous_sample * (1 - sweep_cutoff_ratio); - sample += gb->sgb_intro_sweep_previous_sample * pow((120 - gb->sgb->intro_animation) / 120.0, 2) * 0.8; - } - - stereo.left = stereo.right = sample * 0x7000; - gb->apu_output.sample_callback(gb, &stereo); - } - - return; -} - diff --git a/bsnes/gb/Core-new/sgb.h b/bsnes/gb/Core-new/sgb.h deleted file mode 100644 index df90253c..00000000 --- a/bsnes/gb/Core-new/sgb.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef sgb_h -#define sgb_h -#include "gb_struct_def.h" -#include -#include - -typedef struct GB_sgb_s GB_sgb_t; - -#ifdef GB_INTERNAL -struct GB_sgb_s { - uint8_t command[16 * 7]; - uint16_t command_write_index; - bool ready_for_pulse; - bool ready_for_write; - bool ready_for_stop; - bool disable_commands; - - /* Screen buffer */ - uint8_t screen_buffer[160 * 144]; // Live image from the Game Boy - uint8_t effective_screen_buffer[160 * 144]; // Image actually rendered to the screen - - /* Multiplayer Input */ - uint8_t player_count, current_player; - - /* Mask */ - uint8_t mask_mode; - - /* Data Transfer */ - uint8_t vram_transfer_countdown, transfer_dest; - - /* Border */ - struct { - uint8_t tiles[0x100 * 8 * 8]; /* High nibble not used*/ - union { - struct { - uint16_t map[32 * 32]; - uint16_t palette[16 * 4]; - }; - uint16_t raw_data[0x440]; - }; - } border, pending_border; - uint8_t border_animation; - - /* Colorization */ - uint16_t effective_palettes[4 * 4]; - uint16_t ram_palettes[4 * 512]; - uint8_t attribute_map[20 * 18]; - uint8_t attribute_files[0xFE0]; - - /* Intro */ - int16_t intro_animation; - - /* GB Header */ - uint8_t received_header[0x54]; - - /* Multiplayer (cont) */ - bool mlt_lock; -}; - -void GB_sgb_write(GB_gameboy_t *gb, uint8_t value); -void GB_sgb_render(GB_gameboy_t *gb); -void GB_sgb_load_default_data(GB_gameboy_t *gb); - -#endif - -#endif diff --git a/bsnes/gb/Core-new/sgb_animation_logo.inc b/bsnes/gb/Core-new/sgb_animation_logo.inc deleted file mode 100644 index 75075f49..00000000 --- a/bsnes/gb/Core-new/sgb_animation_logo.inc +++ /dev/null @@ -1,563 +0,0 @@ -static uint8_t animation_logo[] = { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x3, 0x3, 0x1, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x1, 0x1, 0x3, 0x3, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x3, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, - 0x4, 0x4, 0x4, 0x3, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, - 0x4, 0x4, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xE, 0xE, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, - 0x4, 0x4, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x3, 0x1, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, - 0x4, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x1, - 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x7, 0x0, 0x0, 0x1, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x1, 0xC, 0xC, 0xC, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0x1, - 0x0, 0x0, 0xE, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, - 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x3, 0x1, 0x1, 0x0, 0x0, 0x5, - 0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x7, 0x7, 0x7, 0x9, 0x9, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x0, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0x1, - 0x1, 0xE, 0xE, 0xE, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, - 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0x3, 0x1, 0x0, 0x0, 0x5, 0x5, - 0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, - 0x7, 0x7, 0x7, 0x9, 0x1, 0x1, 0x1, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC, - 0xC, 0xC, 0xC, 0xC, 0x1, 0x1, 0xC, 0xC, 0x0, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0xE, - 0xE, 0xE, 0xE, 0xE, 0xE, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1, 0x1, 0x0, 0x1, 0x5, 0x5, - 0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, - 0x7, 0x7, 0x9, 0x1, 0x1, 0x0, 0x0, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0xC, 0xC, - 0xC, 0xC, 0x1, 0x1, 0x0, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xE, 0xE, 0xE, 0xE, 0xE, - 0xE, 0xE, 0xE, 0xE, 0xE, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x5, 0x5, 0x5, - 0x5, 0x1, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, - 0x7, 0x7, 0x1, 0x1, 0x0, 0x0, 0x1, 0x9, 0x9, 0x9, 0x0, 0x0, 0x0, 0x1, 0xC, 0xC, - 0xC, 0x1, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0x1, 0xD, 0x1, 0x1, 0x1, - 0x1, 0xE, 0xE, 0xE, 0xE, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, - 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, - 0x1, 0x1, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, - 0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0x0, 0xC, 0xC, 0xC, - 0xC, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xD, 0xD, 0x1, 0x0, 0x0, - 0xE, 0xE, 0xF, 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, - 0x4, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, - 0x1, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, - 0x7, 0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, - 0x1, 0x1, 0x0, 0x1, 0xC, 0xC, 0xC, 0x1, 0x1, 0x0, 0x1, 0xD, 0x1, 0x1, 0x0, 0xF, - 0xF, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, - 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x5, 0x5, 0x5, 0x1, - 0x1, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x1, 0x7, 0x7, 0x7, - 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, - 0x1, 0x0, 0x1, 0xC, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xD, 0xD, 0x1, 0x0, 0x1, 0xF, - 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x1, 0x1, - 0x0, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x9, 0x1, 0x0, 0x1, 0xC, 0xC, 0xB, 0xB, - 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x1, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0xF, 0xF, - 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x1, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x1, 0x6, 0x6, 0x6, 0x1, 0x0, - 0x0, 0x0, 0x1, 0x6, 0x6, 0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x7, 0x7, 0x7, 0x1, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x1, 0x1, 0x0, 0xC, 0xC, 0xB, 0xB, 0x1, - 0xC, 0xC, 0xC, 0xC, 0x1, 0x1, 0x1, 0x0, 0x1, 0xD, 0x1, 0x1, 0x0, 0x1, 0xF, 0xF, - 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x6, 0x6, 0x6, 0x1, 0x1, 0x0, - 0x0, 0x0, 0x6, 0x6, 0x7, 0x7, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x8, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x9, 0x9, 0x9, 0x9, 0x1, 0x0, 0x0, 0xC, 0xB, 0xB, 0xB, 0x1, - 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x0, 0x0, 0xF, 0xF, 0xF, - 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, 0x4, 0x1, 0x0, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, - 0x0, 0x6, 0x6, 0x7, 0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x8, 0x8, 0x1, - 0x0, 0x0, 0x0, 0x1, 0x9, 0x9, 0xA, 0x1, 0x1, 0x0, 0xC, 0xB, 0xB, 0xB, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0xF, 0xF, 0xF, - 0x1, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x1, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, 0x1, 0x1, 0x6, 0x6, 0x6, 0x1, 0x0, 0x0, - 0x1, 0x6, 0x7, 0x7, 0x7, 0x1, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x8, 0x8, 0xA, 0x1, - 0x0, 0x0, 0x0, 0x9, 0x9, 0xA, 0xA, 0x1, 0x0, 0x1, 0xB, 0xB, 0xB, 0xD, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, - 0x1, 0x0, 0x0, 0x0, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x4, 0x1, 0x1, 0x6, 0x6, 0x1, 0x1, 0x0, 0x1, - 0x6, 0x1, 0x7, 0x7, 0x7, 0x1, 0x0, 0x0, 0x1, 0x7, 0x7, 0x8, 0x8, 0xA, 0xA, 0x1, - 0x0, 0x0, 0xA, 0xA, 0xA, 0xA, 0x1, 0x1, 0x0, 0xB, 0xB, 0x1, 0xD, 0xD, 0xD, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, - 0x1, 0x0, 0x0, 0x0, 0xF, 0xF, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x5, 0x5, 0x5, 0x5, - 0x5, 0x1, 0x0, 0x0, 0x1, 0x4, 0x4, 0x1, 0x1, 0x0, 0x6, 0x6, 0x1, 0x0, 0x1, 0x6, - 0x1, 0x1, 0x7, 0x7, 0x7, 0x1, 0x0, 0x1, 0x7, 0x7, 0x8, 0x8, 0x1, 0x1, 0xA, 0xA, - 0x1, 0xA, 0xA, 0xA, 0xA, 0x1, 0x1, 0xB, 0xB, 0xB, 0x1, 0x1, 0x1, 0xD, 0xD, 0x1, - 0x0, 0x0, 0x0, 0x1, 0xD, 0xD, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, - 0x1, 0x0, 0x1, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, - 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x1, 0x1, 0x0, 0x0, 0x6, 0x6, 0x6, 0x6, 0x6, 0x1, - 0x1, 0x0, 0x1, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x8, 0x8, 0x8, 0x1, 0x0, 0x1, 0xA, - 0xA, 0xA, 0xA, 0xB, 0xB, 0xB, 0xB, 0xB, 0xB, 0x1, 0x1, 0x0, 0x0, 0xD, 0xD, 0xD, - 0xD, 0xD, 0xD, 0xD, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, - 0xF, 0xF, 0xF, 0xF, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x5, - 0x5, 0x5, 0x5, 0x5, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6, 0x1, 0x1, 0x1, - 0x0, 0x0, 0x0, 0x1, 0x7, 0x7, 0x7, 0x1, 0x8, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, - 0xB, 0xB, 0xB, 0xB, 0xB, 0xB, 0xB, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0xD, - 0xD, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, - 0xF, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x8, 0x8, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x8, 0x8, 0x8, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x8, 0x8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, - 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x8, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, - 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, - 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, - 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, - 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, - 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x1, 0x0, 0x1, 0x2, 0x2, - 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, - 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, - 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, - 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, - 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, - 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, - 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x1, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, - 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, - 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, - 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, - 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, - 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, - 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, - 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, - 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, - 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, - 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, - 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, - 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, - 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, - 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 -}; -static const unsigned animation_logo_height = sizeof(animation_logo) / 160; diff --git a/bsnes/gb/Core-new/sgb_border.inc b/bsnes/gb/Core-new/sgb_border.inc deleted file mode 100644 index d7d0a5c9..00000000 --- a/bsnes/gb/Core-new/sgb_border.inc +++ /dev/null @@ -1,658 +0,0 @@ -static const uint16_t palette[] = { - 0x0000, 0x0011, 0x18C6, 0x001A, 0x318C, 0x39CE, 0x5294, 0x5AD6, - 0x739C, 0x45A8, 0x4520, 0x18A5, 0x4631, 0x2033, 0x20EC, 0x18B7 -}; - -static const uint16_t tilemap[] = { - 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, - 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, - 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, - 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, - 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, - 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, - 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, - 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, 0x1002, - 0x1001, 0x1003, 0x1004, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, - 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, - 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, - 0x1005, 0x1005, 0x1005, 0x1005, 0x1005, 0x5004, 0x5003, 0x1001, - 0x1001, 0x1006, 0x1007, 0x1007, 0x1007, 0x1008, 0x1009, 0x100A, - 0x100B, 0x100C, 0x100D, 0x100E, 0x100F, 0x1010, 0x1011, 0x1012, - 0x1013, 0x1014, 0x1015, 0x100E, 0x1016, 0x1017, 0x1018, 0x1019, - 0x101A, 0x101B, 0x101C, 0x1007, 0x1007, 0x1007, 0x5006, 0x1001, - 0x1001, 0x101D, 0x101E, 0x101E, 0x101E, 0x101F, 0x1020, 0x1021, - 0x1022, 0x1023, 0x1024, 0x1025, 0x5024, 0x1026, 0x1025, 0x1025, - 0x1027, 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, - 0x102F, 0x1030, 0x1031, 0x101E, 0x101E, 0x101E, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1034, 0x1035, 0x5034, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x8034, 0x1036, 0xC034, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0xC01D, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0x1037, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1032, 0x1038, 0x1001, - 0x1001, 0x101D, 0x1032, 0x1032, 0x1032, 0x1033, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, - 0x1000, 0x1000, 0xC033, 0x1032, 0x1032, 0x1039, 0x103A, 0x1001, - 0x1001, 0x103B, 0x103C, 0x1032, 0x1032, 0xC03C, 0x103D, 0x103D, - 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, - 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, 0x103D, - 0x103D, 0x103D, 0x103E, 0x103F, 0x1040, 0x1041, 0x1001, 0x1001, - 0x1001, 0x1042, 0x1043, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, - 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, - 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, 0x1044, - 0x1044, 0x1044, 0x1045, 0x1046, 0x1001, 0x1001, 0x1001, 0x1001, - 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1047, 0x1048, 0x1049, - 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, 0x1050, 0x1051, - 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, 0x1058, 0x1059, - 0x105A, 0x105B, 0x105C, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, - 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x105D, 0x105E, 0x105F, - 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, - 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F, - 0x1070, 0x1071, 0x1072, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, - 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, 0x1073, 0x1074, 0x1075, - 0x1076, 0x1077, 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, - 0x107E, 0x107F, 0x1080, 0x1081, 0x1082, 0x1083, 0x507A, 0x1084, - 0x1001, 0x1085, 0x507A, 0x1001, 0x1001, 0x1001, 0x1001, 0x1001, -}; - -const uint8_t tiles[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x00, 0xFF, 0x01, 0xFE, 0x06, 0xF9, 0x08, 0xF7, - 0x11, 0xEF, 0x22, 0xDB, 0x20, 0xDB, 0x40, 0xB7, - 0xFF, 0x00, 0xFF, 0x00, 0xFE, 0x00, 0xF8, 0x00, - 0xF1, 0x00, 0xE6, 0x04, 0xE4, 0x00, 0xC8, 0x00, - 0x7F, 0x80, 0x80, 0x7F, 0x00, 0xFF, 0x7F, 0xFF, - 0x80, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0x80, 0x00, 0x00, 0x00, 0x7F, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0xB7, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, - 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, - 0xC8, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFF, 0x02, 0xFF, - 0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0xC1, 0xDD, 0x00, 0xC9, - 0x14, 0xFF, 0x14, 0xFF, 0x14, 0xFF, 0x00, 0xC9, - 0x00, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x36, 0x22, - 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x36, 0x22, - 0x00, 0xFF, 0x00, 0xFF, 0xC7, 0xDF, 0x01, 0xCF, - 0x11, 0xFF, 0x11, 0xFF, 0x11, 0xFF, 0x01, 0xCF, - 0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x31, 0x20, - 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x31, 0x20, - 0x00, 0xFF, 0x00, 0xFF, 0xC2, 0xFF, 0x03, 0xFF, - 0x02, 0xFE, 0x02, 0xFE, 0x02, 0xFF, 0x02, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x03, 0x00, - 0x03, 0x01, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x08, 0xFF, 0x18, 0xFF, - 0x08, 0x4E, 0x08, 0x4E, 0x09, 0x1F, 0x08, 0x1C, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, - 0xB9, 0x10, 0xB9, 0xA1, 0xE9, 0xA0, 0xEB, 0x41, - 0x00, 0xFF, 0x00, 0xFF, 0x4F, 0xFF, 0x02, 0x1F, - 0x02, 0x4F, 0x02, 0x4F, 0xF2, 0xFF, 0x02, 0xE7, - 0x00, 0x00, 0x00, 0x00, 0x4F, 0x00, 0xE2, 0xA0, - 0xB2, 0xA0, 0xB2, 0x10, 0xF2, 0x00, 0x1A, 0x10, - 0x00, 0xFF, 0x00, 0xFF, 0xBC, 0xFD, 0x22, 0xFB, - 0x22, 0xFB, 0x3C, 0xFD, 0x24, 0xFF, 0x26, 0xF9, - 0x00, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x26, 0x00, - 0x26, 0x00, 0x3E, 0x00, 0x24, 0x00, 0x26, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x50, 0xFF, 0x49, 0xEF, - 0x49, 0xF0, 0x46, 0xFF, 0x49, 0xF0, 0x49, 0xEF, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x59, 0x00, - 0x4F, 0x06, 0x46, 0x00, 0x4F, 0x06, 0x59, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x88, 0xFF, 0x00, 0x72, - 0x00, 0xF2, 0x05, 0xFF, 0x00, 0xF8, 0x00, 0x78, - 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x8D, 0x08, - 0x0D, 0x05, 0x05, 0x00, 0x07, 0x05, 0x87, 0x02, - 0x00, 0xFF, 0x00, 0xFF, 0x8A, 0xFF, 0x02, 0x27, - 0x02, 0x27, 0x52, 0xFF, 0x02, 0x8F, 0x02, 0x8F, - 0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0xDA, 0x88, - 0xDA, 0x50, 0x52, 0x00, 0x72, 0x50, 0x72, 0x20, - 0x00, 0xFF, 0x00, 0xFF, 0xFA, 0xFF, 0x22, 0xFF, - 0x22, 0xFF, 0x23, 0xFF, 0x22, 0xFF, 0x22, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x22, 0x00, - 0x22, 0x00, 0x23, 0x00, 0x22, 0x00, 0x22, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x20, 0xFF, 0x20, 0xFF, - 0x20, 0xFF, 0xE0, 0xFF, 0x20, 0xFF, 0x20, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, - 0x20, 0x00, 0xE0, 0x00, 0x20, 0x00, 0x20, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x33, 0x37, 0x00, 0x77, - 0x80, 0xFF, 0x20, 0x27, 0x08, 0xFF, 0x00, 0x77, - 0x00, 0x00, 0x00, 0x00, 0xFB, 0x40, 0x88, 0x88, - 0x80, 0x00, 0xF8, 0x50, 0x08, 0x00, 0x88, 0x88, - 0x00, 0xFF, 0x00, 0xFF, 0xEF, 0xFF, 0x88, 0xFF, - 0x88, 0xFF, 0x8F, 0xFF, 0x88, 0xFF, 0x88, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x88, 0x00, - 0x88, 0x00, 0x8F, 0x00, 0x88, 0x00, 0x88, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0xF9, 0xFD, 0x80, 0xF9, - 0x84, 0xFF, 0xF4, 0xFF, 0x84, 0xFF, 0x80, 0xF9, - 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x86, 0x02, - 0x84, 0x00, 0xF4, 0x00, 0x84, 0x00, 0x86, 0x02, - 0x00, 0xFF, 0x00, 0xFF, 0xC0, 0xDF, 0x00, 0xCF, - 0x10, 0xFF, 0x10, 0xFF, 0x10, 0xFF, 0x00, 0xCF, - 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x30, 0x20, - 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x30, 0x20, - 0x00, 0xFF, 0x00, 0xFF, 0x30, 0x36, 0x00, 0x74, - 0x82, 0xFF, 0x22, 0x27, 0x0A, 0xFF, 0x00, 0x74, - 0x00, 0x00, 0x00, 0x00, 0xF9, 0x40, 0x8B, 0x89, - 0x82, 0x00, 0xFA, 0x50, 0x0A, 0x00, 0x8B, 0x89, - 0x00, 0xFF, 0x00, 0xFF, 0xE2, 0xEF, 0x02, 0xE7, - 0x0A, 0xFF, 0x0A, 0xFF, 0x0A, 0xFF, 0x00, 0xE4, - 0x00, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x1A, 0x10, - 0x0A, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x1B, 0x12, - 0x00, 0xFF, 0x00, 0xFF, 0x14, 0xFF, 0x16, 0xFF, - 0x14, 0xFC, 0x15, 0xFE, 0x14, 0xFF, 0x04, 0xCF, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x16, 0x00, - 0x17, 0x01, 0x15, 0x00, 0x14, 0x00, 0x34, 0x10, - 0x00, 0xFF, 0x00, 0xFF, 0x2F, 0xFF, 0x28, 0xFF, - 0x28, 0xFF, 0xA8, 0x7F, 0x28, 0x3F, 0x68, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x28, 0x00, - 0x28, 0x00, 0xA8, 0x00, 0xE8, 0x80, 0x68, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x3F, - 0x40, 0xFF, 0x40, 0xFF, 0x40, 0xFF, 0x00, 0x3F, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x80, - 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xC0, 0x80, - 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, - 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFF, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0xC1, 0xDD, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xC1, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x02, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x4A, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x0A, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x22, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x82, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x20, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x60, 0x67, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xF8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x8F, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xA2, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xF9, 0xFD, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xC0, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x60, 0x66, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xF9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xE0, 0xEE, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xF1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xC4, 0xDF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0xE4, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x2F, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, - 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x3C, 0xFF, - 0x7E, 0xFF, 0xE7, 0xE7, 0xFF, 0x7E, 0xFF, 0x7E, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x7E, - 0x81, 0xC3, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0x7E, 0xFF, 0x3C, 0xFF, 0x00, 0x7E, 0x81, - 0x3C, 0xC3, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0xC3, 0x81, - 0x7E, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xF7, 0x00, 0xF7, 0x00, 0xF7, 0x00, 0xF7, - 0x00, 0xF7, 0x00, 0xED, 0x00, 0xED, 0x00, 0xED, - 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, - 0x09, 0x00, 0x11, 0x02, 0x11, 0x02, 0x11, 0x02, - 0x00, 0xED, 0x00, 0xDB, 0x00, 0xDB, 0x00, 0xDB, - 0x00, 0xB7, 0x00, 0xB7, 0x00, 0x6F, 0x00, 0x6F, - 0x11, 0x02, 0x23, 0x04, 0x23, 0x04, 0x23, 0x04, - 0x47, 0x08, 0x47, 0x08, 0x8F, 0x10, 0x8F, 0x10, - 0x00, 0xFE, 0x00, 0xFD, 0x00, 0xFB, 0x00, 0xF7, - 0x00, 0xEE, 0x00, 0xDD, 0x00, 0xBB, 0x00, 0x77, - 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, - 0x10, 0x01, 0x21, 0x02, 0x43, 0x04, 0x87, 0x08, - 0x00, 0xDF, 0x00, 0xBF, 0x00, 0xBF, 0x00, 0x7F, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x1F, 0x20, 0x3F, 0x40, 0x3F, 0x40, 0x7F, 0x80, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xEF, 0x00, 0xB7, - 0x00, 0xB7, 0x00, 0xDB, 0x00, 0xDD, 0x00, 0xEE, - 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x88, 0x40, - 0x88, 0x40, 0xC4, 0x20, 0xC2, 0x20, 0xE1, 0x10, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFC, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFC, 0x00, 0xE3, 0x00, 0x1F, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x1C, 0x00, 0xE0, 0x00, - 0x00, 0xFE, 0x00, 0xFD, 0x00, 0xF3, 0x00, 0xEF, - 0x00, 0x1C, 0x00, 0xF3, 0x00, 0xEF, 0x00, 0x1F, - 0x01, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x10, 0x00, - 0xE0, 0x03, 0x03, 0x0C, 0x0F, 0x10, 0x1F, 0xE0, - 0x00, 0xEF, 0x00, 0xDF, 0x00, 0xBF, 0x00, 0x7F, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x0F, 0x10, 0x1F, 0x20, 0x3F, 0x40, 0x7F, 0x80, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x00, 0xF7, 0x00, 0xF9, 0x00, 0xFE, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xF0, 0x08, 0xF8, 0x06, 0xFE, 0x01, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x00, 0x80, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x80, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x7F, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x7F, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x00, 0x03, 0x00, 0xFF, 0x00, 0xFC, 0x00, 0x03, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFC, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xFC, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x00, 0xFC, 0x00, 0xE3, 0x00, 0x1F, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0x03, 0x03, 0x1C, 0x1F, 0xE0, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x01, 0xFF, 0x01, 0xFD, 0x03, 0xFF, 0x03, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x01, 0xFE, 0x02, 0xFE, 0x02, 0xFC, 0x00, - 0x0E, 0xEE, 0x3F, 0xFF, 0x75, 0x71, 0xFB, 0xE7, - 0xE3, 0xCB, 0xC7, 0x9F, 0x07, 0x3E, 0x84, 0x7C, - 0xFB, 0x1B, 0xE6, 0x26, 0x8E, 0x82, 0x3E, 0x22, - 0x7C, 0x54, 0x7D, 0x25, 0xF9, 0x40, 0xFB, 0x01, - 0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x80, 0xFF, - 0x00, 0x7F, 0x80, 0x4F, 0x31, 0x7F, 0x71, 0xFD, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x80, - 0xFF, 0x00, 0xFF, 0x30, 0xFF, 0xB1, 0xDE, 0x52, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0x7B, 0x87, 0xFF, 0x8E, 0xFE, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x84, 0xFA, 0x82, 0xF9, 0x88, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0xC1, 0xFD, 0xE3, 0x7B, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xC3, 0xBC, 0x24, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x01, 0xFF, 0x03, 0xFF, 0xE3, 0xFB, 0xF7, 0xBF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x01, 0xFE, 0x02, 0x7C, 0x64, 0xFC, 0xB4, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0x7F, 0x80, 0xFF, 0xA0, 0x2F, 0xF0, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x80, 0xFF, 0x80, 0xFF, 0x70, 0x8F, 0x80, - 0x00, 0xFF, 0x00, 0xFF, 0x02, 0xFD, 0x00, 0xF7, - 0x00, 0xFF, 0x11, 0xEE, 0x11, 0xEE, 0x10, 0xEF, - 0xFF, 0x00, 0xFF, 0x00, 0xFC, 0x03, 0xF8, 0x0F, - 0xF0, 0x0F, 0xE0, 0x1F, 0xE1, 0x1E, 0xE0, 0x1F, - 0x00, 0xFF, 0x00, 0xFF, 0x10, 0xE7, 0x00, 0xFB, - 0xC4, 0x3B, 0x98, 0x03, 0x00, 0xEF, 0x80, 0x7F, - 0xFF, 0x00, 0xFF, 0x00, 0x0F, 0xF8, 0x07, 0xFC, - 0x07, 0xF8, 0xEF, 0x74, 0xFF, 0x10, 0x7F, 0x80, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xEF, 0x00, 0xFF, 0x22, 0xDD, 0x06, 0xB9, - 0xFF, 0x00, 0xFF, 0x00, 0xF8, 0x07, 0xF0, 0x0F, - 0xF0, 0x1F, 0xE0, 0x1F, 0xC0, 0x3F, 0xC4, 0x7B, - 0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7D, 0x02, 0xFD, - 0x02, 0xBD, 0x40, 0xBF, 0x40, 0xBF, 0x40, 0xBF, - 0xFF, 0x00, 0xFF, 0x00, 0x7E, 0x83, 0x7C, 0x83, - 0x7C, 0xC3, 0x7C, 0x83, 0x3C, 0xC3, 0x3C, 0xC3, - 0x00, 0xFF, 0x00, 0xFF, 0x10, 0xEF, 0x00, 0xFF, - 0x00, 0xF7, 0x00, 0xF7, 0x48, 0xB6, 0x48, 0xB7, - 0xFF, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x0F, 0xF0, - 0x0F, 0xF8, 0x0F, 0xF8, 0x07, 0xF9, 0x06, 0xF9, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xBF, 0x02, 0xFC, - 0x02, 0x7D, 0x02, 0xFD, 0x02, 0xFD, 0x20, 0xDD, - 0xFF, 0x00, 0xFF, 0x00, 0xC1, 0x7E, 0x81, 0x7F, - 0x81, 0xFE, 0x01, 0xFE, 0x03, 0xFC, 0x03, 0xFE, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xBF, 0x40, 0xBF, - 0x47, 0xB8, 0x08, 0xF0, 0x08, 0xF7, 0x0F, 0xF0, - 0xFF, 0x00, 0xFF, 0x00, 0xC0, 0x7F, 0x80, 0x7F, - 0x80, 0x7F, 0x87, 0x7F, 0x87, 0x78, 0x80, 0x7F, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFB, 0x24, 0xCB, - 0xE4, 0x1B, 0x00, 0x1F, 0x00, 0xFF, 0x80, 0x3F, - 0xFF, 0x00, 0xFF, 0x00, 0x1C, 0xE7, 0x18, 0xF7, - 0x18, 0xE7, 0xF8, 0xE7, 0xF8, 0x07, 0x78, 0xC7, - 0x00, 0xFF, 0x00, 0xFF, 0x04, 0xF9, 0x00, 0xFF, - 0x71, 0x8E, 0x89, 0x06, 0x81, 0x7E, 0xE1, 0x1E, - 0xFF, 0x00, 0xFF, 0x00, 0x03, 0xFE, 0x01, 0xFE, - 0x00, 0xFF, 0x70, 0xFF, 0x70, 0x8F, 0x01, 0xFE, - 0x00, 0xFF, 0x00, 0xFF, 0x02, 0xF9, 0x00, 0xFF, - 0x00, 0xFF, 0x03, 0xFC, 0x06, 0xB9, 0x44, 0xBB, - 0xFF, 0x00, 0xFF, 0x00, 0xFC, 0x07, 0xF0, 0x0F, - 0xE0, 0x1F, 0xC1, 0x3E, 0xC3, 0x7C, 0x87, 0x78, - 0x00, 0xFF, 0x00, 0xFF, 0x08, 0xF7, 0x00, 0xFD, - 0xC0, 0x3F, 0x11, 0x0E, 0x00, 0xFF, 0x08, 0xF7, - 0xFF, 0x00, 0xFF, 0x00, 0x07, 0xF8, 0x03, 0xFE, - 0x01, 0xFE, 0xE0, 0xFF, 0xF0, 0x0F, 0xF0, 0x0F, - 0x00, 0xFF, 0x00, 0xFF, 0x08, 0x77, 0x40, 0xBF, - 0x04, 0xBB, 0x00, 0xFE, 0x00, 0xDD, 0x00, 0x7F, - 0xFF, 0x00, 0xFF, 0x00, 0x87, 0xF8, 0x87, 0x78, - 0xC3, 0x7C, 0xC3, 0x3D, 0xE2, 0x3F, 0xE0, 0x9F, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFD, 0x06, 0xF9, - 0x0C, 0x73, 0x08, 0xF7, 0x10, 0xE7, 0x20, 0xCF, - 0xFF, 0x00, 0xFF, 0x00, 0xC3, 0x3E, 0x83, 0x7C, - 0x87, 0xF8, 0x0F, 0xF0, 0x1F, 0xE8, 0x3F, 0xD0, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xF7, 0x00, 0xFF, - 0x01, 0xDE, 0x06, 0xF8, 0x1C, 0xC3, 0x00, 0xF3, - 0xFF, 0x00, 0xFF, 0x00, 0xF8, 0x0F, 0xE0, 0x1F, - 0xE0, 0x3F, 0xC3, 0x3D, 0xE7, 0x38, 0xFF, 0x0C, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xDF, 0x00, 0xFF, - 0x00, 0xF7, 0x08, 0x77, 0x08, 0xF7, 0x08, 0xF7, - 0xFF, 0x00, 0xFF, 0x00, 0x3F, 0xE0, 0x0F, 0xF0, - 0x0F, 0xF8, 0x87, 0xF8, 0x87, 0x78, 0x07, 0xF8, - 0x03, 0xFF, 0x03, 0xFF, 0x01, 0xFF, 0x00, 0xFE, - 0x18, 0xDF, 0x1C, 0xFD, 0x0F, 0xEF, 0x07, 0xF7, - 0xFC, 0x00, 0xFE, 0x02, 0xFF, 0x01, 0xFF, 0x01, - 0xFF, 0x38, 0xF3, 0x12, 0xF9, 0x19, 0xFC, 0x0C, - 0x02, 0x79, 0x80, 0xFD, 0xC0, 0xDF, 0xF0, 0xFE, - 0x79, 0x3F, 0x19, 0xDB, 0x19, 0xFB, 0xF9, 0xF7, - 0xFF, 0x84, 0xFF, 0x82, 0x7F, 0x60, 0x9F, 0x91, - 0xEF, 0xA9, 0xF6, 0x34, 0xFE, 0x1C, 0x1F, 0x11, - 0x63, 0xEF, 0xF3, 0xEB, 0xC6, 0xCE, 0xEF, 0xDE, - 0x8C, 0x9C, 0xDE, 0xBD, 0x9C, 0x9D, 0xFF, 0xEF, - 0x9E, 0x02, 0xBC, 0xA4, 0x3D, 0x14, 0x7B, 0x4A, - 0x73, 0x21, 0xF7, 0x94, 0xF7, 0xF6, 0xFE, 0xEE, - 0x8D, 0xEC, 0x9E, 0x7D, 0x1C, 0x5B, 0x38, 0xFA, - 0x79, 0xF7, 0x71, 0x75, 0xF3, 0xF3, 0xEF, 0xCF, - 0xF3, 0x90, 0xF7, 0x14, 0xEF, 0xA8, 0xEF, 0x2D, - 0xCF, 0x41, 0x8E, 0x8A, 0x3C, 0x3C, 0x39, 0x19, - 0x67, 0xFF, 0xEF, 0xFE, 0xEC, 0xDC, 0xCF, 0xCF, - 0xDD, 0xDC, 0xDC, 0x9F, 0x2C, 0x2F, 0xD7, 0xC7, - 0xB9, 0x21, 0xBB, 0xAA, 0xB3, 0x81, 0x76, 0x76, - 0x77, 0x76, 0xE7, 0xA4, 0xD7, 0x44, 0xFB, 0xCB, - 0xB3, 0x37, 0x73, 0x72, 0xF4, 0xEC, 0xEF, 0xCD, - 0xCD, 0x09, 0x11, 0xF3, 0x29, 0xA7, 0xF1, 0xCF, - 0xCD, 0x49, 0xDF, 0xDE, 0xBF, 0xA5, 0x7F, 0x5D, - 0xF6, 0x32, 0xFE, 0x14, 0xFE, 0x70, 0xFF, 0xC1, - 0xF0, 0x77, 0xF0, 0x67, 0xE0, 0xCF, 0x80, 0x97, - 0xC8, 0xBB, 0x98, 0xBB, 0x90, 0xD3, 0xE8, 0xE7, - 0xDF, 0x58, 0xBF, 0x28, 0x7F, 0x50, 0x7F, 0x28, - 0xF7, 0x84, 0xFF, 0xDC, 0xEF, 0xA4, 0xDF, 0xC0, - 0x00, 0xFF, 0x04, 0xF3, 0x03, 0xF8, 0x00, 0xFF, - 0x08, 0xF7, 0x03, 0xFC, 0x00, 0xBF, 0x18, 0xC7, - 0xF0, 0x0F, 0xF8, 0x0F, 0xFE, 0x05, 0xFF, 0x00, - 0xE7, 0x18, 0xC0, 0x3F, 0xC0, 0x7F, 0xE0, 0x3F, - 0x00, 0xFF, 0x00, 0xFF, 0x08, 0xF6, 0x08, 0x77, - 0x08, 0xF5, 0x08, 0xF7, 0x10, 0xE7, 0x70, 0x87, - 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF9, 0x86, 0xF9, - 0x86, 0x7B, 0x0C, 0xF3, 0x08, 0xFF, 0x38, 0xCF, - 0x0A, 0xF1, 0x88, 0x77, 0x0E, 0xF1, 0x00, 0xFF, - 0x00, 0xFF, 0x7F, 0x80, 0x41, 0xBE, 0x81, 0x3E, - 0x84, 0x7F, 0x0E, 0xF1, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x3E, 0xC1, 0x7E, 0x81, 0x7E, 0xC1, - 0x04, 0xFB, 0x04, 0xDB, 0x24, 0xDB, 0x20, 0xDF, - 0x20, 0xDF, 0x00, 0xFF, 0x08, 0xE7, 0x19, 0xE6, - 0x38, 0xC7, 0x38, 0xE7, 0x38, 0xC7, 0x18, 0xE7, - 0x18, 0xE7, 0x18, 0xE7, 0x10, 0xFF, 0x10, 0xEF, - 0x48, 0xB5, 0x80, 0x3F, 0x84, 0x7B, 0x80, 0x7F, - 0xA1, 0x5E, 0x21, 0x5E, 0x02, 0x7C, 0x02, 0x7D, - 0x46, 0xBB, 0x44, 0xFB, 0x40, 0xBF, 0x40, 0xBF, - 0xC0, 0x3F, 0xC1, 0xBE, 0xE1, 0x9F, 0xE3, 0x9C, - 0x60, 0x9D, 0x64, 0x99, 0x84, 0x3B, 0x84, 0x7B, - 0x04, 0x7B, 0x40, 0xBB, 0x41, 0xBA, 0x09, 0xF2, - 0x03, 0xFE, 0x43, 0xBE, 0x43, 0xFC, 0xC3, 0x3C, - 0xC7, 0xB8, 0x87, 0x7C, 0x86, 0x7D, 0x86, 0x7D, - 0x80, 0x7F, 0x80, 0x7F, 0x8F, 0x70, 0x10, 0xEF, - 0x10, 0xEF, 0x1F, 0xE0, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x0F, 0xF0, 0x0F, 0xF0, - 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x48, 0xB7, 0x48, 0xB7, 0xC0, 0x3F, 0x01, 0xFE, - 0x01, 0xFE, 0x81, 0x2E, 0x50, 0xAF, 0x50, 0xAF, - 0x30, 0xCF, 0x30, 0xCF, 0xF0, 0x0F, 0xF0, 0x0F, - 0xF0, 0x0F, 0x70, 0xDF, 0x20, 0xDF, 0x60, 0x9F, - 0x06, 0xF8, 0x00, 0xFD, 0xF0, 0x0F, 0x00, 0x7E, - 0x00, 0xEE, 0xE2, 0x1C, 0x02, 0xFD, 0x0C, 0xF1, - 0x03, 0xFD, 0x03, 0xFE, 0xE1, 0x1E, 0xF1, 0x8F, - 0xF1, 0x1F, 0x01, 0xFF, 0x03, 0xFC, 0x07, 0xFA, - 0x08, 0xF3, 0x08, 0xF7, 0x08, 0xF7, 0x00, 0xFF, - 0x40, 0xBB, 0x01, 0xFE, 0x20, 0xDF, 0x18, 0xE7, - 0x87, 0x7C, 0x87, 0x78, 0x87, 0x78, 0x87, 0x78, - 0x87, 0x7C, 0xC0, 0x3F, 0xE0, 0x1F, 0xF0, 0x0F, - 0x08, 0xF7, 0x08, 0xF7, 0x01, 0xFE, 0x11, 0xEE, - 0x01, 0xDE, 0x82, 0x7C, 0x04, 0xF9, 0x38, 0xC3, - 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xE0, 0x1F, - 0xE1, 0x3E, 0x03, 0xFD, 0x07, 0xFA, 0x0F, 0xF4, - 0x10, 0x6F, 0x00, 0x7F, 0x01, 0x7E, 0x01, 0xFE, - 0x01, 0xFE, 0x11, 0xEE, 0x10, 0xEE, 0x12, 0xEC, - 0xE0, 0x9F, 0xF0, 0x8F, 0xF0, 0x8F, 0xF0, 0x0F, - 0xF0, 0x0F, 0xE1, 0x1E, 0xE1, 0x1F, 0xE1, 0x1F, - 0x40, 0x9F, 0x80, 0x3F, 0x80, 0x7F, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x7F, 0xA0, 0x7F, 0xC0, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x00, 0xFF, 0x00, 0xFB, 0x08, 0xF7, 0x00, 0xFF, - 0x01, 0xBE, 0x03, 0xFC, 0x00, 0x7F, 0x80, 0x7F, - 0xFE, 0x01, 0xFC, 0x07, 0xF0, 0x0F, 0xE0, 0x1F, - 0xC1, 0x7E, 0x80, 0x7F, 0x80, 0xFF, 0x00, 0xFF, - 0x08, 0xF7, 0x10, 0xE7, 0x60, 0x8F, 0xC0, 0x3F, - 0x80, 0x7F, 0xE0, 0x0F, 0x00, 0xEF, 0x00, 0xEF, - 0x0F, 0xF0, 0x1F, 0xE8, 0x3F, 0xD0, 0x7F, 0x80, - 0xFF, 0x00, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0, - 0x02, 0xF8, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x04, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xD0, 0xC6, 0x00, 0x1F, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0xC9, 0xFF, 0xE0, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xE7, 0x86, 0x01, 0x39, 0x01, 0xFF, 0x03, 0xFF, - 0x03, 0xFF, 0x00, 0xFC, 0x00, 0xFE, 0x00, 0xFF, - 0xFF, 0x9E, 0xFF, 0xC7, 0xFE, 0x00, 0xFE, 0x02, - 0xFF, 0x03, 0xFF, 0x02, 0xFF, 0x01, 0xFF, 0x00, - 0xC3, 0xD3, 0xC0, 0xBC, 0x80, 0xBF, 0x00, 0x7F, - 0x80, 0x7F, 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, - 0x7F, 0x6B, 0x7F, 0x03, 0xFF, 0xC0, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x00, - 0xC7, 0x1B, 0x00, 0x7C, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x23, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xC0, 0x1F, 0x00, 0x7F, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x20, 0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x50, 0x4F, 0x00, 0x9F, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x40, 0xFF, 0x60, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x07, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xC7, 0x18, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x20, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x80, 0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xF7, 0x08, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x0C, 0xE1, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x12, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x38, 0x87, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x40, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x8F, 0x30, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x40, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xF0, 0x07, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x03, 0xF0, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x0C, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x0E, 0xF1, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0x7F, 0x80, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, - 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00 -}; diff --git a/bsnes/gb/Core-new/sm83_cpu.c b/bsnes/gb/Core-new/sm83_cpu.c deleted file mode 100644 index 77248b51..00000000 --- a/bsnes/gb/Core-new/sm83_cpu.c +++ /dev/null @@ -1,1521 +0,0 @@ -#include -#include -#include -#include "gb.h" - - -typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode); - -typedef enum { - /* Default behavior. If the CPU writes while another component reads, it reads the old value */ - GB_CONFLICT_READ_OLD, - /* If the CPU writes while another component reads, it reads the new value */ - GB_CONFLICT_READ_NEW, - /* If the CPU and another component write at the same time, the CPU's value "wins" */ - GB_CONFLICT_WRITE_CPU, - /* Register specific values */ - GB_CONFLICT_STAT_CGB, - GB_CONFLICT_STAT_DMG, - GB_CONFLICT_PALETTE_DMG, - GB_CONFLICT_PALETTE_CGB, -} GB_conflict_t; - -/* Todo: How does double speed mode affect these? */ -static const GB_conflict_t cgb_conflict_map[0x80] = { - [GB_IO_IF] = GB_CONFLICT_WRITE_CPU, - [GB_IO_LYC] = GB_CONFLICT_WRITE_CPU, - [GB_IO_STAT] = GB_CONFLICT_STAT_CGB, - [GB_IO_BGP] = GB_CONFLICT_PALETTE_CGB, - [GB_IO_OBP0] = GB_CONFLICT_PALETTE_CGB, - [GB_IO_OBP1] = GB_CONFLICT_PALETTE_CGB, - - - /* Todo: most values not verified, and probably differ between revisions */ -}; - -/* Todo: verify on an MGB */ -static const GB_conflict_t dmg_conflict_map[0x80] = { - [GB_IO_IF] = GB_CONFLICT_WRITE_CPU, - [GB_IO_LYC] = GB_CONFLICT_READ_OLD, - [GB_IO_LCDC] = GB_CONFLICT_READ_NEW, - [GB_IO_SCY] = GB_CONFLICT_READ_NEW, - [GB_IO_STAT] = GB_CONFLICT_STAT_DMG, - - [GB_IO_BGP] = GB_CONFLICT_PALETTE_DMG, - [GB_IO_OBP0] = GB_CONFLICT_PALETTE_DMG, - [GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG, - - /* Todo: these were not verified at all */ - [GB_IO_WY] = GB_CONFLICT_READ_NEW, - [GB_IO_WX] = GB_CONFLICT_READ_NEW, - [GB_IO_SCX] = GB_CONFLICT_READ_NEW, -}; - -/* Todo: Verify on an SGB1 */ -static const GB_conflict_t sgb_conflict_map[0x80] = { - [GB_IO_IF] = GB_CONFLICT_WRITE_CPU, - [GB_IO_LYC] = GB_CONFLICT_READ_OLD, - [GB_IO_LCDC] = GB_CONFLICT_READ_NEW, - [GB_IO_SCY] = GB_CONFLICT_READ_NEW, - [GB_IO_STAT] = GB_CONFLICT_STAT_DMG, - - [GB_IO_BGP] = GB_CONFLICT_READ_NEW, - [GB_IO_OBP0] = GB_CONFLICT_READ_NEW, - [GB_IO_OBP1] = GB_CONFLICT_READ_NEW, - - /* Todo: these were not verified at all */ - [GB_IO_WY] = GB_CONFLICT_READ_NEW, - [GB_IO_WX] = GB_CONFLICT_READ_NEW, - [GB_IO_SCX] = GB_CONFLICT_READ_NEW, -}; - -static uint8_t cycle_read(GB_gameboy_t *gb, uint16_t addr) -{ - if (gb->pending_cycles) { - GB_advance_cycles(gb, gb->pending_cycles); - } - uint8_t ret = GB_read_memory(gb, addr); - gb->pending_cycles = 4; - return ret; -} - -static uint8_t cycle_read_inc_oam_bug(GB_gameboy_t *gb, uint16_t addr) -{ - if (gb->pending_cycles) { - GB_advance_cycles(gb, gb->pending_cycles); - } - GB_trigger_oam_bug_read_increase(gb, addr); /* Todo: test T-cycle timing */ - uint8_t ret = GB_read_memory(gb, addr); - gb->pending_cycles = 4; - return ret; -} - -/* A special case for IF during ISR, returns the old value of IF. */ -/* TODO: Verify the timing, it might be wrong in cases where, in the same M cycle, IF - is both read be the CPU, modified by the ISR, and modified by an actual interrupt. - If this timing proves incorrect, the ISR emulation must be updated so IF reads are - timed correctly. */ -static uint8_t cycle_write_if(GB_gameboy_t *gb, uint8_t value) -{ - assert(gb->pending_cycles); - GB_advance_cycles(gb, gb->pending_cycles); - uint8_t old = (gb->io_registers[GB_IO_IF]) & 0x1F; - GB_write_memory(gb, 0xFF00 + GB_IO_IF, value); - gb->pending_cycles = 4; - return old; -} - -static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - assert(gb->pending_cycles); - GB_conflict_t conflict = GB_CONFLICT_READ_OLD; - if ((addr & 0xFF80) == 0xFF00) { - const GB_conflict_t *map = NULL; - if (GB_is_cgb(gb)) { - map = cgb_conflict_map; - } - else if (GB_is_sgb(gb)) { - map = sgb_conflict_map; - } - else { - map = dmg_conflict_map; - } - conflict = map[addr & 0x7F]; - } - switch (conflict) { - case GB_CONFLICT_READ_OLD: - GB_advance_cycles(gb, gb->pending_cycles); - GB_write_memory(gb, addr, value); - gb->pending_cycles = 4; - return; - - case GB_CONFLICT_READ_NEW: - GB_advance_cycles(gb, gb->pending_cycles - 1); - GB_write_memory(gb, addr, value); - gb->pending_cycles = 5; - return; - - case GB_CONFLICT_WRITE_CPU: - GB_advance_cycles(gb, gb->pending_cycles + 1); - GB_write_memory(gb, addr, value); - gb->pending_cycles = 3; - return; - - /* The DMG STAT-write bug is basically the STAT register being read as FF for a single T-cycle */ - case GB_CONFLICT_STAT_DMG: - GB_advance_cycles(gb, gb->pending_cycles); - /* State 7 is the edge between HBlank and OAM mode, and it behaves a bit weird. - The OAM interrupt seems to be blocked by HBlank interrupts in that case, despite - the timing not making much sense for that. - This is a hack to simulate this effect */ - if (gb->display_state == 7 && (gb->io_registers[GB_IO_STAT] & 0x28) == 0x08) { - GB_write_memory(gb, addr, ~0x20); - } - else { - GB_write_memory(gb, addr, 0xFF); - } - GB_advance_cycles(gb, 1); - GB_write_memory(gb, addr, value); - gb->pending_cycles = 3; - return; - - case GB_CONFLICT_STAT_CGB: { - /* Todo: Verify this with SCX adjustments */ - /* The LYC bit behaves differently */ - uint8_t old_value = GB_read_memory(gb, addr); - GB_advance_cycles(gb, gb->pending_cycles); - GB_write_memory(gb, addr, (old_value & 0x40) | (value & ~0x40)); - GB_advance_cycles(gb, 1); - GB_write_memory(gb, addr, value); - gb->pending_cycles = 3; - return; - } - - /* There is some "time travel" going on with these two values, as it appears - that there's some off-by-1-T-cycle timing issue in the PPU implementation. - - This is should be accurate for every measureable scenario, though. */ - - case GB_CONFLICT_PALETTE_DMG: { - GB_advance_cycles(gb, gb->pending_cycles - 2); - uint8_t old_value = GB_read_memory(gb, addr); - GB_write_memory(gb, addr, value | old_value); - GB_advance_cycles(gb, 1); - GB_write_memory(gb, addr, value); - gb->pending_cycles = 5; - return; - } - - case GB_CONFLICT_PALETTE_CGB: { - GB_advance_cycles(gb, gb->pending_cycles - 2); - GB_write_memory(gb, addr, value); - gb->pending_cycles = 6; - return; - } - } -} - -static void cycle_no_access(GB_gameboy_t *gb) -{ - gb->pending_cycles += 4; -} - -static void cycle_oam_bug(GB_gameboy_t *gb, uint8_t register_id) -{ - if (GB_is_cgb(gb)) { - /* Slight optimization */ - gb->pending_cycles += 4; - return; - } - if (gb->pending_cycles) { - GB_advance_cycles(gb, gb->pending_cycles); - } - GB_trigger_oam_bug(gb, gb->registers[register_id]); /* Todo: test T-cycle timing */ - gb->pending_cycles = 4; - -} - -static void flush_pending_cycles(GB_gameboy_t *gb) -{ - if (gb->pending_cycles) { - GB_advance_cycles(gb, gb->pending_cycles); - } - gb->pending_cycles = 0; -} - -/* Todo: test if multi-byte opcodes trigger the OAM bug correctly */ - -static void ill(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_log(gb, "Illegal Opcode. Halting.\n"); - gb->interrupt_enable = 0; - gb->halted = true; -} - -static void nop(GB_gameboy_t *gb, uint8_t opcode) -{ -} - -static void stop(GB_gameboy_t *gb, uint8_t opcode) -{ - if (gb->io_registers[GB_IO_KEY1] & 0x1) { - flush_pending_cycles(gb); - bool needs_alignment = false; - - GB_advance_cycles(gb, 0x4); - /* Make sure we keep the CPU ticks aligned correctly when returning from double speed mode */ - if (gb->double_speed_alignment & 7) { - GB_advance_cycles(gb, 0x4); - needs_alignment = true; - } - - gb->cgb_double_speed ^= true; - gb->io_registers[GB_IO_KEY1] = 0; - - for (unsigned i = 0x800; i--;) { - GB_advance_cycles(gb, 0x40); - } - - if (!needs_alignment) { - GB_advance_cycles(gb, 0x4); - } - - } - else { - GB_timing_sync(gb); - if ((gb->io_registers[GB_IO_JOYP] & 0xF) != 0xF) { - /* HW Bug? When STOP is executed while a button is down, the CPU halts forever - yet the other hardware keeps running. */ - gb->interrupt_enable = 0; - gb->halted = true; - } - else { - gb->stopped = true; - } - } - - /* Todo: is PC being actually read? */ - gb->pc++; -} - -/* Operand naming conventions for functions: - r = 8-bit register - lr = low 8-bit register - hr = high 8-bit register - rr = 16-bit register - d8 = 8-bit imm - d16 = 16-bit imm - d.. = [..] - cc = condition code (z, nz, c, nc) - */ - -static void ld_rr_d16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - uint16_t value; - register_id = (opcode >> 4) + 1; - value = cycle_read_inc_oam_bug(gb, gb->pc++); - value |= cycle_read_inc_oam_bug(gb, gb->pc++) << 8; - gb->registers[register_id] = value; -} - -static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - register_id = (opcode >> 4) + 1; - cycle_write(gb, gb->registers[register_id], gb->registers[GB_REGISTER_AF] >> 8); -} - -static void inc_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id = (opcode >> 4) + 1; - cycle_oam_bug(gb, register_id); - gb->registers[register_id]++; -} - -static void inc_hr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - register_id = ((opcode >> 4) + 1) & 0x03; - gb->registers[register_id] += 0x100; - gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - - if ((gb->registers[register_id] & 0x0F00) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[register_id] & 0xFF00) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} -static void dec_hr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - register_id = ((opcode >> 4) + 1) & 0x03; - gb->registers[register_id] -= 0x100; - gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - - if ((gb->registers[register_id] & 0x0F00) == 0xF00) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[register_id] & 0xFF00) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void ld_hr_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - register_id = ((opcode >> 4) + 1) & 0x03; - gb->registers[register_id] &= 0xFF; - gb->registers[register_id] |= cycle_read_inc_oam_bug(gb, gb->pc++) << 8; -} - -static void rlca(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry = (gb->registers[GB_REGISTER_AF] & 0x8000) != 0; - - gb->registers[GB_REGISTER_AF] = (gb->registers[GB_REGISTER_AF] & 0xFF00) << 1; - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG | 0x0100; - } -} - -static void rla(GB_gameboy_t *gb, uint8_t opcode) -{ - bool bit7 = (gb->registers[GB_REGISTER_AF] & 0x8000) != 0; - bool carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - - gb->registers[GB_REGISTER_AF] = (gb->registers[GB_REGISTER_AF] & 0xFF00) << 1; - if (carry) { - gb->registers[GB_REGISTER_AF] |= 0x0100; - } - if (bit7) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode) -{ - /* Todo: Verify order is correct */ - uint16_t addr; - addr = cycle_read_inc_oam_bug(gb, gb->pc++); - addr |= cycle_read_inc_oam_bug(gb, gb->pc++) << 8; - cycle_write(gb, addr, gb->registers[GB_REGISTER_SP] & 0xFF); - cycle_write(gb, addr+1, gb->registers[GB_REGISTER_SP] >> 8); -} - -static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t hl = gb->registers[GB_REGISTER_HL]; - uint16_t rr; - uint8_t register_id; - cycle_no_access(gb); - register_id = (opcode >> 4) + 1; - rr = gb->registers[register_id]; - gb->registers[GB_REGISTER_HL] = hl + rr; - gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_CARRY_FLAG | GB_HALF_CARRY_FLAG); - - /* The meaning of the Half Carry flag is really hard to track -_- */ - if (((hl & 0xFFF) + (rr & 0xFFF)) & 0x1000) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ( ((unsigned long) hl + (unsigned long) rr) & 0x10000) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void ld_a_drr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - register_id = (opcode >> 4) + 1; - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= cycle_read(gb, gb->registers[register_id]) << 8; -} - -static void dec_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id = (opcode >> 4) + 1; - cycle_oam_bug(gb, register_id); - gb->registers[register_id]--; -} - -static void inc_lr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - uint8_t value; - register_id = (opcode >> 4) + 1; - - value = (gb->registers[register_id] & 0xFF) + 1; - gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value; - - gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - - if ((gb->registers[register_id] & 0x0F) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[register_id] & 0xFF) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} -static void dec_lr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - uint8_t value; - register_id = (opcode >> 4) + 1; - - value = (gb->registers[register_id] & 0xFF) - 1; - gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value; - - gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - - if ((gb->registers[register_id] & 0x0F) == 0xF) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[register_id] & 0xFF) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void ld_lr_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - register_id = (opcode >> 4) + 1; - gb->registers[register_id] &= 0xFF00; - gb->registers[register_id] |= cycle_read_inc_oam_bug(gb, gb->pc++); -} - -static void rrca(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry = (gb->registers[GB_REGISTER_AF] & 0x100) != 0; - - gb->registers[GB_REGISTER_AF] = (gb->registers[GB_REGISTER_AF] >> 1) & 0xFF00; - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG | 0x8000; - } -} - -static void rra(GB_gameboy_t *gb, uint8_t opcode) -{ - bool bit1 = (gb->registers[GB_REGISTER_AF] & 0x0100) != 0; - bool carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - - gb->registers[GB_REGISTER_AF] = (gb->registers[GB_REGISTER_AF] >> 1) & 0xFF00; - if (carry) { - gb->registers[GB_REGISTER_AF] |= 0x8000; - } - if (bit1) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void jr_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - /* Todo: Verify timing */ - gb->pc += (int8_t)cycle_read_inc_oam_bug(gb, gb->pc) + 1; - cycle_no_access(gb); -} - -static bool condition_code(GB_gameboy_t *gb, uint8_t opcode) -{ - switch ((opcode >> 3) & 0x3) { - case 0: - return !(gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG); - case 1: - return (gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG); - case 2: - return !(gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG); - case 3: - return (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG); - } - - return false; -} - -static void jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - int8_t offset = cycle_read_inc_oam_bug(gb, gb->pc++); - if (condition_code(gb, opcode)) { - gb->pc += offset; - cycle_no_access(gb); - } -} - -static void daa(GB_gameboy_t *gb, uint8_t opcode) -{ - int16_t result = gb->registers[GB_REGISTER_AF] >> 8; - - gb->registers[GB_REGISTER_AF] &= ~(0xFF00 | GB_ZERO_FLAG); - - if (gb->registers[GB_REGISTER_AF] & GB_SUBSTRACT_FLAG) { - if (gb->registers[GB_REGISTER_AF] & GB_HALF_CARRY_FLAG) { - result = (result - 0x06) & 0xFF; - } - - if (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) { - result -= 0x60; - } - } - else { - if ((gb->registers[GB_REGISTER_AF] & GB_HALF_CARRY_FLAG) || (result & 0x0F) > 0x09) { - result += 0x06; - } - - if ((gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) || result > 0x9F) { - result += 0x60; - } - } - - if ((result & 0xFF) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - - if ((result & 0x100) == 0x100) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - - gb->registers[GB_REGISTER_AF] &= ~GB_HALF_CARRY_FLAG; - gb->registers[GB_REGISTER_AF] |= result << 8; -} - -static void cpl(GB_gameboy_t *gb, uint8_t opcode) -{ - gb->registers[GB_REGISTER_AF] ^= 0xFF00; - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG; -} - -static void scf(GB_gameboy_t *gb, uint8_t opcode) -{ - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG); -} - -static void ccf(GB_gameboy_t *gb, uint8_t opcode) -{ - gb->registers[GB_REGISTER_AF] ^= GB_CARRY_FLAG; - gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG); -} - -static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode) -{ - cycle_write(gb, gb->registers[GB_REGISTER_HL]++, gb->registers[GB_REGISTER_AF] >> 8); -} - -static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode) -{ - cycle_write(gb, gb->registers[GB_REGISTER_HL]--, gb->registers[GB_REGISTER_AF] >> 8); -} - -static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode) -{ - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= cycle_read_inc_oam_bug(gb, gb->registers[GB_REGISTER_HL]++) << 8; -} - -static void ld_a_dhld(GB_gameboy_t *gb, uint8_t opcode) -{ - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= cycle_read_inc_oam_bug(gb, gb->registers[GB_REGISTER_HL]--) << 8; -} - -static void inc_dhl(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - value = cycle_read(gb, gb->registers[GB_REGISTER_HL]) + 1; - cycle_write(gb, gb->registers[GB_REGISTER_HL], value); - - gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - if ((value & 0x0F) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((value & 0xFF) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void dec_dhl(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - value = cycle_read(gb, gb->registers[GB_REGISTER_HL]) - 1; - cycle_write(gb, gb->registers[GB_REGISTER_HL], value); - - gb->registers[GB_REGISTER_AF] &= ~( GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - if ((value & 0x0F) == 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((value & 0xFF) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void ld_dhl_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t data = cycle_read_inc_oam_bug(gb, gb->pc++); - cycle_write(gb, gb->registers[GB_REGISTER_HL], data); -} - -static uint8_t get_src_value(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t src_register_id; - uint8_t src_low; - src_register_id = ((opcode >> 1) + 1) & 3; - src_low = opcode & 1; - if (src_register_id == GB_REGISTER_AF) { - if (src_low) { - return gb->registers[GB_REGISTER_AF] >> 8; - } - return cycle_read(gb, gb->registers[GB_REGISTER_HL]); - } - if (src_low) { - return gb->registers[src_register_id] & 0xFF; - } - return gb->registers[src_register_id] >> 8; -} - -static void set_src_value(GB_gameboy_t *gb, uint8_t opcode, uint8_t value) -{ - uint8_t src_register_id; - uint8_t src_low; - src_register_id = ((opcode >> 1) + 1) & 3; - src_low = opcode & 1; - - if (src_register_id == GB_REGISTER_AF) { - if (src_low) { - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= value << 8; - } - else { - cycle_write(gb, gb->registers[GB_REGISTER_HL], value); - } - } - else { - if (src_low) { - gb->registers[src_register_id] &= 0xFF00; - gb->registers[src_register_id] |= value; - } - else { - gb->registers[src_register_id] &= 0xFF; - gb->registers[src_register_id] |= value << 8; - } - } -} - -/* The LD r,r instruction is extremely common and extremely simple. Decoding this opcode at runtime is a significent - performance hit, so we generate functions for every ld x,y couple (including [hl]) at compile time using macros. */ - -/* Todo: It's probably wise to do the same to all opcodes. */ - -#define LD_X_Y(x, y) \ -static void ld_##x##_##y(GB_gameboy_t *gb, uint8_t opcode) \ -{ \ - gb->x = gb->y;\ -} - -#define LD_X_DHL(x) \ -static void ld_##x##_##dhl(GB_gameboy_t *gb, uint8_t opcode) \ -{ \ -gb->x = cycle_read(gb, gb->registers[GB_REGISTER_HL]); \ -} - -#define LD_DHL_Y(y) \ -static void ld_##dhl##_##y(GB_gameboy_t *gb, uint8_t opcode) \ -{ \ -cycle_write(gb, gb->registers[GB_REGISTER_HL], gb->y); \ -} - -LD_X_Y(b,c) LD_X_Y(b,d) LD_X_Y(b,e) LD_X_Y(b,h) LD_X_Y(b,l) LD_X_DHL(b) LD_X_Y(b,a) -LD_X_Y(c,b) LD_X_Y(c,d) LD_X_Y(c,e) LD_X_Y(c,h) LD_X_Y(c,l) LD_X_DHL(c) LD_X_Y(c,a) -LD_X_Y(d,b) LD_X_Y(d,c) LD_X_Y(d,e) LD_X_Y(d,h) LD_X_Y(d,l) LD_X_DHL(d) LD_X_Y(d,a) -LD_X_Y(e,b) LD_X_Y(e,c) LD_X_Y(e,d) LD_X_Y(e,h) LD_X_Y(e,l) LD_X_DHL(e) LD_X_Y(e,a) -LD_X_Y(h,b) LD_X_Y(h,c) LD_X_Y(h,d) LD_X_Y(h,e) LD_X_Y(h,l) LD_X_DHL(h) LD_X_Y(h,a) -LD_X_Y(l,b) LD_X_Y(l,c) LD_X_Y(l,d) LD_X_Y(l,e) LD_X_Y(l,h) LD_X_DHL(l) LD_X_Y(l,a) -LD_DHL_Y(b) LD_DHL_Y(c) LD_DHL_Y(d) LD_DHL_Y(e) LD_DHL_Y(h) LD_DHL_Y(l) LD_DHL_Y(a) -LD_X_Y(a,b) LD_X_Y(a,c) LD_X_Y(a,d) LD_X_Y(a,e) LD_X_Y(a,h) LD_X_Y(a,l) LD_X_DHL(a) - - -static void add_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a + value) << 8; - if ((uint8_t)(a + value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) + (value & 0xF) > 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) + ((unsigned long) value) > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void adc_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a, carry; - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - gb->registers[GB_REGISTER_AF] = (a + value + carry) << 8; - - if ((uint8_t)(a + value + carry) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) + (value & 0xF) + carry > 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) + ((unsigned long) value) + carry > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void sub_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBSTRACT_FLAG; - if (a == value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF)) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (a < value) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void sbc_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a, carry; - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBSTRACT_FLAG; - - if ((uint8_t) (a - value - carry) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF) + carry) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) - ((unsigned long) value) - carry > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void and_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = ((a & value) << 8) | GB_HALF_CARRY_FLAG; - if ((a & value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void xor_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a ^ value) << 8; - if ((a ^ value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void or_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a | value) << 8; - if ((a | value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void cp_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - if (a == value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF)) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (a < value) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void halt(GB_gameboy_t *gb, uint8_t opcode) -{ - assert(gb->pending_cycles == 4); - gb->pending_cycles = 0; - GB_advance_cycles(gb, 1); - GB_advance_cycles(gb, 1); - GB_advance_cycles(gb, 1); - GB_advance_cycles(gb, 1); - - gb->halted = true; - /* Despite what some online documentations say, the HALT bug also happens on a CGB, in both CGB and DMG modes. */ - if (((gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F) != 0)) { - if (gb->ime) { - gb->halted = false; - gb->pc--; - } - else { - gb->halted = false; - gb->halt_bug = true; - } - } - gb->just_halted = true; -} - -static void pop_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - register_id = ((opcode >> 4) + 1) & 3; - gb->registers[register_id] = cycle_read_inc_oam_bug(gb, gb->registers[GB_REGISTER_SP]++); - gb->registers[register_id] |= cycle_read(gb, gb->registers[GB_REGISTER_SP]++) << 8; - gb->registers[GB_REGISTER_AF] &= 0xFFF0; // Make sure we don't set impossible flags on F! See Blargg's PUSH AF test. -} - -static void jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t addr = cycle_read_inc_oam_bug(gb, gb->pc++); - addr |= (cycle_read_inc_oam_bug(gb, gb->pc++) << 8); - if (condition_code(gb, opcode)) { - cycle_no_access(gb); - gb->pc = addr; - } -} - -static void jp_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t addr = cycle_read_inc_oam_bug(gb, gb->pc); - addr |= (cycle_read_inc_oam_bug(gb, gb->pc + 1) << 8); - cycle_no_access(gb); - gb->pc = addr; - -} - -static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t call_addr = gb->pc - 1; - uint16_t addr = cycle_read_inc_oam_bug(gb, gb->pc++); - addr |= (cycle_read_inc_oam_bug(gb, gb->pc++) << 8); - if (condition_code(gb, opcode)) { - cycle_oam_bug(gb, GB_REGISTER_SP); - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) >> 8); - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); - gb->pc = addr; - - GB_debugger_call_hook(gb, call_addr); - } -} - -static void push_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - cycle_oam_bug(gb, GB_REGISTER_SP); - register_id = ((opcode >> 4) + 1) & 3; - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) >> 8); - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) & 0xFF); -} - -static void add_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = cycle_read_inc_oam_bug(gb, gb->pc++); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a + value) << 8; - if ((uint8_t) (a + value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) + (value & 0xF) > 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) + ((unsigned long) value) > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void adc_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a, carry; - value = cycle_read_inc_oam_bug(gb, gb->pc++); - a = gb->registers[GB_REGISTER_AF] >> 8; - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - gb->registers[GB_REGISTER_AF] = (a + value + carry) << 8; - - if (gb->registers[GB_REGISTER_AF] == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) + (value & 0xF) + carry > 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) + ((unsigned long) value) + carry > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void sub_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = cycle_read_inc_oam_bug(gb, gb->pc++); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBSTRACT_FLAG; - if (a == value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF)) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (a < value) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void sbc_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a, carry; - value = cycle_read_inc_oam_bug(gb, gb->pc++); - a = gb->registers[GB_REGISTER_AF] >> 8; - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBSTRACT_FLAG; - - if ((uint8_t) (a - value - carry) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF) + carry) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) - ((unsigned long) value) - carry > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void and_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = cycle_read_inc_oam_bug(gb, gb->pc++); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = ((a & value) << 8) | GB_HALF_CARRY_FLAG; - if ((a & value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void xor_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = cycle_read_inc_oam_bug(gb, gb->pc++); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a ^ value) << 8; - if ((a ^ value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void or_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = cycle_read_inc_oam_bug(gb, gb->pc++); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a | value) << 8; - if ((a | value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - value = cycle_read_inc_oam_bug(gb, gb->pc++); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - if (a == value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF)) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (a < value) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void rst(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t call_addr = gb->pc - 1; - cycle_oam_bug(gb, GB_REGISTER_SP); - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) >> 8); - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); - gb->pc = opcode ^ 0xC7; - GB_debugger_call_hook(gb, call_addr); -} - -static void ret(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_debugger_ret_hook(gb); - gb->pc = cycle_read_inc_oam_bug(gb, gb->registers[GB_REGISTER_SP]++); - gb->pc |= cycle_read(gb, gb->registers[GB_REGISTER_SP]++) << 8; - cycle_no_access(gb); -} - -static void reti(GB_gameboy_t *gb, uint8_t opcode) -{ - ret(gb, opcode); - gb->ime = true; -} - -static void ret_cc(GB_gameboy_t *gb, uint8_t opcode) -{ - if (condition_code(gb, opcode)) { - cycle_no_access(gb); - ret(gb, opcode); - } - else { - cycle_no_access(gb); - } -} - -static void call_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t call_addr = gb->pc - 1; - uint16_t addr = cycle_read_inc_oam_bug(gb, gb->pc++); - addr |= (cycle_read_inc_oam_bug(gb, gb->pc++) << 8); - cycle_oam_bug(gb, GB_REGISTER_SP); - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) >> 8); - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); - gb->pc = addr; - GB_debugger_call_hook(gb, call_addr); -} - -static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t temp = cycle_read_inc_oam_bug(gb, gb->pc++); - cycle_write(gb, 0xFF00 + temp, gb->registers[GB_REGISTER_AF] >> 8); -} - -static void ld_a_da8(GB_gameboy_t *gb, uint8_t opcode) -{ - gb->registers[GB_REGISTER_AF] &= 0xFF; - uint8_t temp = cycle_read_inc_oam_bug(gb, gb->pc++); - gb->registers[GB_REGISTER_AF] |= cycle_read(gb, 0xFF00 + temp) << 8; -} - -static void ld_dc_a(GB_gameboy_t *gb, uint8_t opcode) -{ - cycle_write(gb, 0xFF00 + (gb->registers[GB_REGISTER_BC] & 0xFF), gb->registers[GB_REGISTER_AF] >> 8); -} - -static void ld_a_dc(GB_gameboy_t *gb, uint8_t opcode) -{ - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= cycle_read(gb, 0xFF00 + (gb->registers[GB_REGISTER_BC] & 0xFF)) << 8; -} - -static void add_sp_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - int16_t offset; - uint16_t sp = gb->registers[GB_REGISTER_SP]; - offset = (int8_t) cycle_read_inc_oam_bug(gb, gb->pc++); - cycle_no_access(gb); - cycle_no_access(gb); - gb->registers[GB_REGISTER_SP] += offset; - - gb->registers[GB_REGISTER_AF] &= 0xFF00; - - /* A new instruction, a new meaning for Half Carry! */ - if ((sp & 0xF) + (offset & 0xF) > 0xF) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((sp & 0xFF) + (offset & 0xFF) > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void jp_hl(GB_gameboy_t *gb, uint8_t opcode) -{ - gb->pc = gb->registers[GB_REGISTER_HL]; -} - -static void ld_da16_a(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t addr; - addr = cycle_read_inc_oam_bug(gb, gb->pc++); - addr |= cycle_read_inc_oam_bug(gb, gb->pc++) << 8; - cycle_write(gb, addr, gb->registers[GB_REGISTER_AF] >> 8); -} - -static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t addr; - gb->registers[GB_REGISTER_AF] &= 0xFF; - addr = cycle_read_inc_oam_bug(gb, gb->pc++); - addr |= cycle_read_inc_oam_bug(gb, gb->pc++) << 8 ; - gb->registers[GB_REGISTER_AF] |= cycle_read(gb, addr) << 8; -} - -static void di(GB_gameboy_t *gb, uint8_t opcode) -{ - /* DI is NOT delayed, not even on a CGB. Mooneye's di_timing-GS test fails on a CGB - for different reasons. */ - gb->ime = false; -} - -static void ei(GB_gameboy_t *gb, uint8_t opcode) -{ - /* ei is actually "disable interrupts for one instruction, then enable them". */ - if (!gb->ime && !gb->ime_toggle) { - gb->ime_toggle = true; - } -} - -static void ld_hl_sp_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - int16_t offset; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - offset = (int8_t) cycle_read_inc_oam_bug(gb, gb->pc++); - cycle_no_access(gb); - gb->registers[GB_REGISTER_HL] = gb->registers[GB_REGISTER_SP] + offset; - - if ((gb->registers[GB_REGISTER_SP] & 0xF) + (offset & 0xF) > 0xF) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[GB_REGISTER_SP] & 0xFF) + (offset & 0xFF) > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void ld_sp_hl(GB_gameboy_t *gb, uint8_t opcode) -{ - gb->registers[GB_REGISTER_SP] = gb->registers[GB_REGISTER_HL]; - cycle_no_access(gb); -} - -static void rlc_r(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry; - uint8_t value; - value = get_src_value(gb, opcode); - carry = (value & 0x80) != 0; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - set_src_value(gb, opcode, (value << 1) | carry); - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (!(value << 1)) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void rrc_r(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry; - uint8_t value; - value = get_src_value(gb, opcode); - carry = (value & 0x01) != 0; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - value = (value >> 1) | (carry << 7); - set_src_value(gb, opcode, value); - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (value == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void rl_r(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry; - uint8_t value; - bool bit7; - value = get_src_value(gb, opcode); - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - bit7 = (value & 0x80) != 0; - - gb->registers[GB_REGISTER_AF] &= 0xFF00; - value = (value << 1) | carry; - set_src_value(gb, opcode, value); - if (bit7) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (value == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void rr_r(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry; - uint8_t value; - bool bit1; - - value = get_src_value(gb, opcode); - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - bit1 = (value & 0x1) != 0; - - gb->registers[GB_REGISTER_AF] &= 0xFF00; - value = (value >> 1) | (carry << 7); - set_src_value(gb, opcode, value); - if (bit1) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (value == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void sla_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - bool carry; - value = get_src_value(gb, opcode); - carry = (value & 0x80) != 0; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - set_src_value(gb, opcode, (value << 1)); - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if ((value & 0x7F) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void sra_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t bit7; - uint8_t value; - value = get_src_value(gb, opcode); - bit7 = value & 0x80; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - if (value & 1) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - value = (value >> 1) | bit7; - set_src_value(gb, opcode, value); - if (value == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void srl_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - value = get_src_value(gb, opcode); - gb->registers[GB_REGISTER_AF] &= 0xFF00; - set_src_value(gb, opcode, (value >> 1)); - if (value & 1) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (!(value >> 1)) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void swap_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - value = get_src_value(gb, opcode); - gb->registers[GB_REGISTER_AF] &= 0xFF00; - set_src_value(gb, opcode, (value >> 4) | (value << 4)); - if (!value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void bit_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - uint8_t bit; - value = get_src_value(gb, opcode); - bit = 1 << ((opcode >> 3) & 7); - if ((opcode & 0xC0) == 0x40) { /* Bit */ - gb->registers[GB_REGISTER_AF] &= 0xFF00 | GB_CARRY_FLAG; - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - if (!(bit & value)) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - } - else if ((opcode & 0xC0) == 0x80) { /* res */ - set_src_value(gb, opcode, value & ~bit) ; - } - else if ((opcode & 0xC0) == 0xC0) { /* set */ - set_src_value(gb, opcode, value | bit) ; - } -} - -static void cb_prefix(GB_gameboy_t *gb, uint8_t opcode) -{ - opcode = cycle_read_inc_oam_bug(gb, gb->pc++); - switch (opcode >> 3) { - case 0: - rlc_r(gb, opcode); - break; - case 1: - rrc_r(gb, opcode); - break; - case 2: - rl_r(gb, opcode); - break; - case 3: - rr_r(gb, opcode); - break; - case 4: - sla_r(gb, opcode); - break; - case 5: - sra_r(gb, opcode); - break; - case 6: - swap_r(gb, opcode); - break; - case 7: - srl_r(gb, opcode); - break; - default: - bit_r(gb, opcode); - break; - } -} - -static GB_opcode_t *opcodes[256] = { - /* X0 X1 X2 X3 X4 X5 X6 X7 */ - /* X8 X9 Xa Xb Xc Xd Xe Xf */ - nop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rlca, /* 0X */ - ld_da16_sp, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rrca, - stop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rla, /* 1X */ - jr_r8, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rra, - jr_cc_r8, ld_rr_d16, ld_dhli_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, daa, /* 2X */ - jr_cc_r8, add_hl_rr, ld_a_dhli, dec_rr, inc_lr, dec_lr, ld_lr_d8, cpl, - jr_cc_r8, ld_rr_d16, ld_dhld_a, inc_rr, inc_dhl, dec_dhl, ld_dhl_d8, scf, /* 3X */ - jr_cc_r8, add_hl_rr, ld_a_dhld, dec_rr, inc_hr, dec_hr, ld_hr_d8, ccf, - nop, ld_b_c, ld_b_d, ld_b_e, ld_b_h, ld_b_l, ld_b_dhl, ld_b_a, /* 4X */ - ld_c_b, nop, ld_c_d, ld_c_e, ld_c_h, ld_c_l, ld_c_dhl, ld_c_a, - ld_d_b, ld_d_c, nop, ld_d_e, ld_d_h, ld_d_l, ld_d_dhl, ld_d_a, /* 5X */ - ld_e_b, ld_e_c, ld_e_d, nop, ld_e_h, ld_e_l, ld_e_dhl, ld_e_a, - ld_h_b, ld_h_c, ld_h_d, ld_h_e, nop, ld_h_l, ld_h_dhl, ld_h_a, /* 6X */ - ld_l_b, ld_l_c, ld_l_d, ld_l_e, ld_l_h, nop, ld_l_dhl, ld_l_a, - ld_dhl_b, ld_dhl_c, ld_dhl_d, ld_dhl_e, ld_dhl_h, ld_dhl_l, halt, ld_dhl_a, /* 7X */ - ld_a_b, ld_a_c, ld_a_d, ld_a_e, ld_a_h, ld_a_l, ld_a_dhl, nop, - add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, /* 8X */ - adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, - sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, /* 9X */ - sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, - and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, /* aX */ - xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, - or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, /* bX */ - cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, - ret_cc, pop_rr, jp_cc_a16, jp_a16, call_cc_a16,push_rr, add_a_d8, rst, /* cX */ - ret_cc, ret, jp_cc_a16, cb_prefix, call_cc_a16,call_a16, adc_a_d8, rst, - ret_cc, pop_rr, jp_cc_a16, ill, call_cc_a16,push_rr, sub_a_d8, rst, /* dX */ - ret_cc, reti, jp_cc_a16, ill, call_cc_a16,ill, sbc_a_d8, rst, - ld_da8_a, pop_rr, ld_dc_a, ill, ill, push_rr, and_a_d8, rst, /* eX */ - add_sp_r8, jp_hl, ld_da16_a, ill, ill, ill, xor_a_d8, rst, - ld_a_da8, pop_rr, ld_a_dc, di, ill, push_rr, or_a_d8, rst, /* fX */ - ld_hl_sp_r8,ld_sp_hl, ld_a_da16, ei, ill, ill, cp_a_d8, rst, -}; -void GB_cpu_run(GB_gameboy_t *gb) -{ - if (gb->hdma_on) { - GB_advance_cycles(gb, 4); - return; - } - if (gb->stopped) { - GB_timing_sync(gb); - GB_advance_cycles(gb, 4); - if ((gb->io_registers[GB_IO_JOYP] & 0xF) != 0xF) { - gb->stopped = false; - /* The CPU takes more time to wake up then the other components */ - for (unsigned i = 0x800; i--;) { - GB_advance_cycles(gb, 0x40); - } - GB_advance_cycles(gb, 8); - } - return; - } - - if ((gb->interrupt_enable & 0x10) && (gb->ime || gb->halted)) { - GB_timing_sync(gb); - } - - if (gb->halted && !GB_is_cgb(gb) && !gb->just_halted) { - GB_advance_cycles(gb, 2); - } - - uint8_t interrupt_queue = gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F; - - if (gb->halted) { - GB_advance_cycles(gb, (GB_is_cgb(gb) || gb->just_halted) ? 4 : 2); - } - gb->just_halted = false; - - bool effecitve_ime = gb->ime; - if (gb->ime_toggle) { - gb->ime = !gb->ime; - gb->ime_toggle = false; - } - - /* Wake up from HALT mode without calling interrupt code. */ - if (gb->halted && !effecitve_ime && interrupt_queue) { - gb->halted = false; - } - - /* Call interrupt */ - else if (effecitve_ime && interrupt_queue) { - gb->halted = false; - uint16_t call_addr = gb->pc; - - cycle_no_access(gb); - cycle_no_access(gb); - GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ - cycle_no_access(gb); - - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) >> 8); - interrupt_queue = gb->interrupt_enable; - - if (gb->registers[GB_REGISTER_SP] == GB_IO_IF + 0xFF00 + 1) { - gb->registers[GB_REGISTER_SP]--; - interrupt_queue &= cycle_write_if(gb, (gb->pc) & 0xFF); - } - else { - cycle_write(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); - interrupt_queue &= (gb->io_registers[GB_IO_IF]) & 0x1F; - } - - if (interrupt_queue) { - uint8_t interrupt_bit = 0; - while (!(interrupt_queue & 1)) { - interrupt_queue >>= 1; - interrupt_bit++; - } - gb->io_registers[GB_IO_IF] &= ~(1 << interrupt_bit); - gb->pc = interrupt_bit * 8 + 0x40; - } - else { - gb->pc = 0; - } - gb->ime = false; - GB_debugger_call_hook(gb, call_addr); - } - /* Run mode */ - else if(!gb->halted) { - gb->last_opcode_read = cycle_read_inc_oam_bug(gb, gb->pc++); - if (gb->halt_bug) { - gb->pc--; - gb->halt_bug = false; - } - opcodes[gb->last_opcode_read](gb, gb->last_opcode_read); - } - - if (gb->hdma_starting) { - gb->hdma_starting = false; - gb->hdma_on = true; - gb->hdma_cycles = -8; - } - flush_pending_cycles(gb); -} diff --git a/bsnes/gb/Core-new/sm83_cpu.h b/bsnes/gb/Core-new/sm83_cpu.h deleted file mode 100644 index 49fa80b5..00000000 --- a/bsnes/gb/Core-new/sm83_cpu.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef sm83_cpu_h -#define sm83_cpu_h -#include "gb_struct_def.h" -#include - -void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count); -#ifdef GB_INTERNAL -void GB_cpu_run(GB_gameboy_t *gb); -#endif - -#endif /* sm83_cpu_h */ diff --git a/bsnes/gb/Core-new/sm83_disassembler.c b/bsnes/gb/Core-new/sm83_disassembler.c deleted file mode 100644 index 96aec000..00000000 --- a/bsnes/gb/Core-new/sm83_disassembler.c +++ /dev/null @@ -1,788 +0,0 @@ -#include -#include -#include "gb.h" - - -typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc); - -static void ill(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, ".BYTE $%02x\n", opcode); - (*pc)++; -} - -static void nop(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "NOP\n"); - (*pc)++; -} - -static void stop(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint8_t next = GB_read_memory(gb, (*pc)++); - if (next) { - GB_log(gb, "CORRUPTED STOP (%02x)\n", next); - } - else { - GB_log(gb, "STOP\n"); - } -} - -static char *register_names[] = {"af", "bc", "de", "hl", "sp"}; - -static void ld_rr_d16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - uint16_t value; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - value = GB_read_memory(gb, (*pc)++); - value |= GB_read_memory(gb, (*pc)++) << 8; - const char *symbol = GB_debugger_name_for_address(gb, value); - if (symbol) { - GB_log(gb, "LD %s, %s ; =$%04x\n", register_names[register_id], symbol, value); - } - else { - GB_log(gb, "LD %s, $%04x\n", register_names[register_id], value); - } -} - -static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - GB_log(gb, "LD [%s], a\n", register_names[register_id]); -} - -static void inc_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - GB_log(gb, "INC %s\n", register_names[register_id]); -} - -static void inc_hr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - (*pc)++; - register_id = ((opcode >> 4) + 1) & 0x03; - GB_log(gb, "INC %c\n", register_names[register_id][0]); - -} -static void dec_hr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - (*pc)++; - register_id = ((opcode >> 4) + 1) & 0x03; - GB_log(gb, "DEC %c\n", register_names[register_id][0]); -} - -static void ld_hr_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - (*pc)++; - register_id = ((opcode >> 4) + 1) & 0x03; - GB_log(gb, "LD %c, $%02x\n", register_names[register_id][0], GB_read_memory(gb, (*pc)++)); -} - -static void rlca(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RLCA\n"); -} - -static void rla(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RLA\n"); -} - -static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc){ - uint16_t addr; - (*pc)++; - addr = GB_read_memory(gb, (*pc)++); - addr |= GB_read_memory(gb, (*pc)++) << 8; - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "LD [%s], sp ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "LD [$%04x], sp\n", addr); - } -} - -static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - (*pc)++; - register_id = (opcode >> 4) + 1; - GB_log(gb, "ADD hl, %s\n", register_names[register_id]); -} - -static void ld_a_drr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - GB_log(gb, "LD a, [%s]\n", register_names[register_id]); -} - -static void dec_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - GB_log(gb, "DEC %s\n", register_names[register_id]); -} - -static void inc_lr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - - GB_log(gb, "INC %c\n", register_names[register_id][1]); -} -static void dec_lr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - - GB_log(gb, "DEC %c\n", register_names[register_id][1]); -} - -static void ld_lr_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - - GB_log(gb, "LD %c, $%02x\n", register_names[register_id][1], GB_read_memory(gb, (*pc)++)); -} - -static void rrca(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "RRCA\n"); - (*pc)++; -} - -static void rra(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "RRA\n"); - (*pc)++; -} - -static void jr_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1; - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR %s ; =$%04x\n", symbol, addr); - } - else { - GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR $%04x\n", addr); - } - (*pc)++; -} - -static const char *condition_code(uint8_t opcode) -{ - switch ((opcode >> 3) & 0x3) { - case 0: - return "nz"; - case 1: - return "z"; - case 2: - return "nc"; - case 3: - return "c"; - } - - return NULL; -} - -static void jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1; - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr); - } - else { - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, $%04x\n", condition_code(opcode), addr); - } - (*pc)++; -} - -static void daa(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "DAA\n"); - (*pc)++; -} - -static void cpl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "CPL\n"); - (*pc)++; -} - -static void scf(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "SCF\n"); - (*pc)++; -} - -static void ccf(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "CCF\n"); - (*pc)++; -} - -static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "LD [hli], a\n"); - (*pc)++; -} - -static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "LD [hld], a\n"); - (*pc)++; -} - -static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "LD a, [hli]\n"); - (*pc)++; -} - -static void ld_a_dhld(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "LD a, [hld]\n"); - (*pc)++; -} - -static void inc_dhl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "INC [hl]\n"); - (*pc)++; -} - -static void dec_dhl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "DEC [hl]\n"); - (*pc)++; -} - -static void ld_dhl_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LD [hl], $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static const char *get_src_name(uint8_t opcode) -{ - uint8_t src_register_id; - uint8_t src_low; - src_register_id = ((opcode >> 1) + 1) & 3; - src_low = (opcode & 1); - if (src_register_id == GB_REGISTER_AF) { - return src_low? "a": "[hl]"; - } - if (src_low) { - return register_names[src_register_id] + 1; - } - static const char *high_register_names[] = {"a", "b", "d", "h"}; - return high_register_names[src_register_id]; -} - -static const char *get_dst_name(uint8_t opcode) -{ - uint8_t dst_register_id; - uint8_t dst_low; - dst_register_id = ((opcode >> 4) + 1) & 3; - dst_low = opcode & 8; - if (dst_register_id == GB_REGISTER_AF) { - return dst_low? "a": "[hl]"; - } - if (dst_low) { - return register_names[dst_register_id] + 1; - } - static const char *high_register_names[] = {"a", "b", "d", "h"}; - return high_register_names[dst_register_id]; -} - -static void ld_r_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LD %s, %s\n", get_dst_name(opcode), get_src_name(opcode)); -} - -static void add_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "ADD %s\n", get_src_name(opcode)); -} - -static void adc_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "ADC %s\n", get_src_name(opcode)); -} - -static void sub_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SUB %s\n", get_src_name(opcode)); -} - -static void sbc_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SBC %s\n", get_src_name(opcode)); -} - -static void and_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "AND %s\n", get_src_name(opcode)); -} - -static void xor_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "XOR %s\n", get_src_name(opcode)); -} - -static void or_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "OR %s\n", get_src_name(opcode)); -} - -static void cp_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "CP %s\n", get_src_name(opcode)); -} - -static void halt(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "HALT\n"); -} - -static void ret_cc(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "RET %s\n", condition_code(opcode)); -} - -static void pop_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = ((GB_read_memory(gb, (*pc)++) >> 4) + 1) & 3; - GB_log(gb, "POP %s\n", register_names[register_id]); -} - -static void jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr); - } - else { - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, $%04x\n", condition_code(opcode), addr); - } - (*pc) += 2; -} - -static void jp_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "JP %s ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "JP $%04x\n", addr); - } - (*pc) += 2; -} - -static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "CALL %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr); - } - else { - GB_log(gb, "CALL %s, $%04x\n", condition_code(opcode), addr); - } - (*pc) += 2; -} - -static void push_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = ((GB_read_memory(gb, (*pc)++) >> 4) + 1) & 3; - GB_log(gb, "PUSH %s\n", register_names[register_id]); -} - -static void add_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "ADD $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void adc_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "ADC $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void sub_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SUB $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void sbc_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SBC $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void and_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "AND $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void xor_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "XOR $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void or_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "OR $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "CP $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void rst(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RST $%02x\n", opcode ^ 0xC7); - -} - -static void ret(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_attributed_log(gb, GB_LOG_UNDERLINE, "RET\n"); -} - -static void reti(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_attributed_log(gb, GB_LOG_UNDERLINE, "RETI\n"); -} - -static void call_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "CALL %s ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "CALL $%04x\n", addr); - } - (*pc) += 2; -} - -static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint8_t addr = GB_read_memory(gb, (*pc)++); - const char *symbol = GB_debugger_name_for_address(gb, 0xff00 + addr); - if (symbol) { - GB_log(gb, "LDH [%s & $FF], a ; =$%02x\n", symbol, addr); - } - else { - GB_log(gb, "LDH [$%02x], a\n", addr); - } -} - -static void ld_a_da8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint8_t addr = GB_read_memory(gb, (*pc)++); - const char *symbol = GB_debugger_name_for_address(gb, 0xff00 + addr); - if (symbol) { - GB_log(gb, "LDH a, [%s & $FF] ; =$%02x\n", symbol, addr); - } - else { - GB_log(gb, "LDH a, [$%02x]\n", addr); - } -} - -static void ld_dc_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LDH [c], a\n"); -} - -static void ld_a_dc(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LDH a, [c]\n"); -} - -static void add_sp_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - int8_t temp = GB_read_memory(gb, (*pc)++); - GB_log(gb, "ADD SP, %s$%02x\n", temp < 0? "-" : "", temp < 0? -temp : temp); -} - -static void jp_hl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "JP hl\n"); -} - -static void ld_da16_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "LD [%s], a ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "LD [$%04x], a\n", addr); - } - (*pc) += 2; -} - -static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "LD a, [%s] ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "LD a, [$%04x]\n", addr); - } - (*pc) += 2; -} - -static void di(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "DI\n"); -} - -static void ei(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "EI\n"); -} - -static void ld_hl_sp_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - int8_t temp = GB_read_memory(gb, (*pc)++); - GB_log(gb, "LD hl, sp, %s$%02x\n", temp < 0? "-" : "", temp < 0? -temp : temp); -} - -static void ld_sp_hl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LD sp, hl\n"); -} - -static void rlc_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RLC %s\n", get_src_name(opcode)); -} - -static void rrc_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RRC %s\n", get_src_name(opcode)); -} - -static void rl_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RL %s\n", get_src_name(opcode)); -} - -static void rr_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RR %s\n", get_src_name(opcode)); -} - -static void sla_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SLA %s\n", get_src_name(opcode)); -} - -static void sra_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SRA %s\n", get_src_name(opcode)); -} - -static void srl_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SRL %s\n", get_src_name(opcode)); -} - -static void swap_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SWAP %s\n", get_src_name(opcode)); -} - -static void bit_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t bit; - (*pc)++; - bit = ((opcode >> 3) & 7); - if ((opcode & 0xC0) == 0x40) { /* Bit */ - GB_log(gb, "BIT %s, %d\n", get_src_name(opcode), bit); - } - else if ((opcode & 0xC0) == 0x80) { /* res */ - GB_log(gb, "RES %s, %d\n", get_src_name(opcode), bit); - } - else if ((opcode & 0xC0) == 0xC0) { /* set */ - GB_log(gb, "SET %s, %d\n", get_src_name(opcode), bit); - } -} - -static void cb_prefix(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - opcode = GB_read_memory(gb, ++*pc); - switch (opcode >> 3) { - case 0: - rlc_r(gb, opcode, pc); - break; - case 1: - rrc_r(gb, opcode, pc); - break; - case 2: - rl_r(gb, opcode, pc); - break; - case 3: - rr_r(gb, opcode, pc); - break; - case 4: - sla_r(gb, opcode, pc); - break; - case 5: - sra_r(gb, opcode, pc); - break; - case 6: - swap_r(gb, opcode, pc); - break; - case 7: - srl_r(gb, opcode, pc); - break; - default: - bit_r(gb, opcode, pc); - break; - } -} - -static GB_opcode_t *opcodes[256] = { - /* X0 X1 X2 X3 X4 X5 X6 X7 */ - /* X8 X9 Xa Xb Xc Xd Xe Xf */ - nop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rlca, /* 0X */ - ld_da16_sp, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rrca, - stop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rla, /* 1X */ - jr_r8, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rra, - jr_cc_r8, ld_rr_d16, ld_dhli_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, daa, /* 2X */ - jr_cc_r8, add_hl_rr, ld_a_dhli, dec_rr, inc_lr, dec_lr, ld_lr_d8, cpl, - jr_cc_r8, ld_rr_d16, ld_dhld_a, inc_rr, inc_dhl, dec_dhl, ld_dhl_d8, scf, /* 3X */ - jr_cc_r8, add_hl_rr, ld_a_dhld, dec_rr, inc_hr, dec_hr, ld_hr_d8, ccf, - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 4X */ - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 5X */ - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 6X */ - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, halt, ld_r_r, /* 7X */ - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, - add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, /* 8X */ - adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, - sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, /* 9X */ - sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, - and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, /* aX */ - xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, - or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, /* bX */ - cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, - ret_cc, pop_rr, jp_cc_a16, jp_a16, call_cc_a16,push_rr, add_a_d8, rst, /* cX */ - ret_cc, ret, jp_cc_a16, cb_prefix, call_cc_a16,call_a16, adc_a_d8, rst, - ret_cc, pop_rr, jp_cc_a16, ill, call_cc_a16,push_rr, sub_a_d8, rst, /* dX */ - ret_cc, reti, jp_cc_a16, ill, call_cc_a16,ill, sbc_a_d8, rst, - ld_da8_a, pop_rr, ld_dc_a, ill, ill, push_rr, and_a_d8, rst, /* eX */ - add_sp_r8, jp_hl, ld_da16_a, ill, ill, ill, xor_a_d8, rst, - ld_a_da8, pop_rr, ld_a_dc, di, ill, push_rr, or_a_d8, rst, /* fX */ - ld_hl_sp_r8,ld_sp_hl, ld_a_da16, ei, ill, ill, cp_a_d8, rst, -}; - - - -void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count) -{ - const GB_bank_symbol_t *function_symbol = GB_debugger_find_symbol(gb, pc); - - if (function_symbol && pc - function_symbol->addr > 0x1000) { - function_symbol = NULL; - } - - if (function_symbol && pc != function_symbol->addr) { - GB_log(gb, "%s:\n", function_symbol->name); - } - - uint16_t current_function = function_symbol? function_symbol->addr : 0; - - while (count--) { - function_symbol = GB_debugger_find_symbol(gb, pc); - if (function_symbol && function_symbol->addr == pc) { - if (current_function != function_symbol->addr) { - GB_log(gb, "\n"); - } - GB_log(gb, "%s:\n", function_symbol->name); - } - if (function_symbol) { - GB_log(gb, "%s%04x <+%03x>: ", pc == gb->pc? " ->": " ", pc, pc - function_symbol->addr); - } - else { - GB_log(gb, "%s%04x: ", pc == gb->pc? " ->": " ", pc); - } - uint8_t opcode = GB_read_memory(gb, pc); - opcodes[opcode](gb, opcode, &pc); - } -} diff --git a/bsnes/gb/Core-new/symbol_hash.c b/bsnes/gb/Core-new/symbol_hash.c deleted file mode 100644 index 208e72d6..00000000 --- a/bsnes/gb/Core-new/symbol_hash.c +++ /dev/null @@ -1,110 +0,0 @@ -#include "gb.h" -#include -#include -#include -#include - -static size_t GB_map_find_symbol_index(GB_symbol_map_t *map, uint16_t addr) -{ - if (!map->symbols) { - return 0; - } - ssize_t min = 0; - ssize_t max = map->n_symbols; - while (min < max) { - size_t pivot = (min + max) / 2; - if (map->symbols[pivot].addr == addr) return pivot; - if (map->symbols[pivot].addr > addr) { - max = pivot; - } - else { - min = pivot + 1; - } - } - return (size_t) min; -} - -GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name) -{ - size_t index = GB_map_find_symbol_index(map, addr); - - if (index < map->n_symbols && map->symbols[index].addr == addr) return NULL; - - map->symbols = realloc(map->symbols, (map->n_symbols + 1) * sizeof(map->symbols[0])); - memmove(&map->symbols[index + 1], &map->symbols[index], (map->n_symbols - index) * sizeof(map->symbols[0])); - map->symbols[index].addr = addr; - map->symbols[index].name = strdup(name); - map->n_symbols++; - return &map->symbols[index]; -} - -const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr) -{ - if (!map) return NULL; - size_t index = GB_map_find_symbol_index(map, addr); - if (index < map->n_symbols && map->symbols[index].addr != addr) { - index--; - } - if (index < map->n_symbols) { - return &map->symbols[index]; - } - return NULL; -} - -GB_symbol_map_t *GB_map_alloc(void) -{ - GB_symbol_map_t *map = malloc(sizeof(*map)); - memset(map, 0, sizeof(*map)); - return map; -} - -void GB_map_free(GB_symbol_map_t *map) -{ - for (unsigned i = 0; i < map->n_symbols; i++) { - free(map->symbols[i].name); - } - - if (map->symbols) { - free(map->symbols); - } - - free(map); -} - -static int hash_name(const char *name) -{ - int r = 0; - while (*name) { - r <<= 1; - if (r & 0x400) { - r ^= 0x401; - } - r += (unsigned char)*(name++); - } - - return r & 0x3FF; -} - -void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *bank_symbol) -{ - int hash = hash_name(bank_symbol->name); - GB_symbol_t *symbol = malloc(sizeof(*symbol)); - symbol->name = bank_symbol->name; - symbol->addr = bank_symbol->addr; - symbol->bank = bank; - symbol->next = map->buckets[hash]; - map->buckets[hash] = symbol; -} - -const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name) -{ - int hash = hash_name(name); - GB_symbol_t *symbol = map->buckets[hash]; - - while (symbol) { - if (strcmp(symbol->name, name) == 0) return symbol; - symbol = symbol->next; - } - - return NULL; -} diff --git a/bsnes/gb/Core-new/symbol_hash.h b/bsnes/gb/Core-new/symbol_hash.h deleted file mode 100644 index 2a03c96b..00000000 --- a/bsnes/gb/Core-new/symbol_hash.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef symbol_hash_h -#define symbol_hash_h - -#include -#include -#include - -typedef struct { - char *name; - uint16_t addr; -} GB_bank_symbol_t; - -typedef struct GB_symbol_s { - struct GB_symbol_s *next; - const char *name; - uint16_t bank; - uint16_t addr; -} GB_symbol_t; - -typedef struct { - GB_bank_symbol_t *symbols; - size_t n_symbols; -} GB_symbol_map_t; - -typedef struct { - GB_symbol_t *buckets[0x400]; -} GB_reversed_symbol_map_t; - -#ifdef GB_INTERNAL -void GB_reversed_map_add_symbol(GB_reversed_symbol_map_t *map, uint16_t bank, GB_bank_symbol_t *symbol); -const GB_symbol_t *GB_reversed_map_find_symbol(GB_reversed_symbol_map_t *map, const char *name); -GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name); -const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr); -GB_symbol_map_t *GB_map_alloc(void); -void GB_map_free(GB_symbol_map_t *map); -#endif - -#endif /* symbol_hash_h */ diff --git a/bsnes/gb/Core-new/timing.c b/bsnes/gb/Core-new/timing.c deleted file mode 100644 index 283558c0..00000000 --- a/bsnes/gb/Core-new/timing.c +++ /dev/null @@ -1,294 +0,0 @@ -#include "gb.h" -#ifdef _WIN32 -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0500 -#endif -#include -#else -#include -#endif - -static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128}; - -#ifndef DISABLE_TIMEKEEPING -static int64_t get_nanoseconds(void) -{ -#ifndef _WIN32 - struct timeval now; - gettimeofday(&now, NULL); - return (now.tv_usec) * 1000 + now.tv_sec * 1000000000L; -#else - FILETIME time; - GetSystemTimeAsFileTime(&time); - return (((int64_t)time.dwHighDateTime << 32) | time.dwLowDateTime) * 100L; -#endif -} - -static void nsleep(uint64_t nanoseconds) -{ -#ifndef _WIN32 - struct timespec sleep = {0, nanoseconds}; - nanosleep(&sleep, NULL); -#else - HANDLE timer; - LARGE_INTEGER time; - timer = CreateWaitableTimer(NULL, true, NULL); - time.QuadPart = -(nanoseconds / 100L); - SetWaitableTimer(timer, &time, 0, NULL, NULL, false); - WaitForSingleObject(timer, INFINITE); - CloseHandle(timer); -#endif -} - -bool GB_timing_sync_turbo(GB_gameboy_t *gb) -{ - if (!gb->turbo_dont_skip) { - int64_t nanoseconds = get_nanoseconds(); - if (nanoseconds <= gb->last_sync + (1000000000LL * LCDC_PERIOD / GB_get_clock_rate(gb))) { - return true; - } - gb->last_sync = nanoseconds; - } - return false; -} - -void GB_timing_sync(GB_gameboy_t *gb) -{ - if (gb->turbo) { - gb->cycles_since_last_sync = 0; - return; - } - /* Prevent syncing if not enough time has passed.*/ - if (gb->cycles_since_last_sync < LCDC_PERIOD / 3) return; - - uint64_t target_nanoseconds = gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */ - int64_t nanoseconds = get_nanoseconds(); - int64_t time_to_sleep = target_nanoseconds + gb->last_sync - nanoseconds; - if (time_to_sleep > 0 && time_to_sleep < LCDC_PERIOD * 1000000000LL / GB_get_clock_rate(gb)) { - nsleep(time_to_sleep); - gb->last_sync += target_nanoseconds; - } - else { - gb->last_sync = nanoseconds; - } - - gb->cycles_since_last_sync = 0; - if (gb->update_input_hint_callback) { - gb->update_input_hint_callback(gb); - } -} -#else - -bool GB_timing_sync_turbo(GB_gameboy_t *gb) -{ - return false; -} - -void GB_timing_sync(GB_gameboy_t *gb) -{ -} - -#endif -static void GB_ir_run(GB_gameboy_t *gb) -{ - 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)); - } -} - -static void advance_tima_state_machine(GB_gameboy_t *gb) -{ - if (gb->tima_reload_state == GB_TIMA_RELOADED) { - gb->tima_reload_state = GB_TIMA_RUNNING; - } - else if (gb->tima_reload_state == GB_TIMA_RELOADING) { - gb->io_registers[GB_IO_IF] |= 4; - gb->tima_reload_state = GB_TIMA_RELOADED; - } -} - -static void increase_tima(GB_gameboy_t *gb) -{ - gb->io_registers[GB_IO_TIMA]++; - if (gb->io_registers[GB_IO_TIMA] == 0) { - gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA]; - gb->tima_reload_state = GB_TIMA_RELOADING; - } -} - -static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value) -{ - /* TIMA increases when a specific high-bit becomes a low-bit. */ - value &= INTERNAL_DIV_CYCLES - 1; - uint32_t triggers = gb->div_counter & ~value; - if ((gb->io_registers[GB_IO_TAC] & 4) && (triggers & GB_TAC_TRIGGER_BITS[gb->io_registers[GB_IO_TAC] & 3])) { - increase_tima(gb); - } - - /* TODO: Can switching to double speed mode trigger an event? */ - if (triggers & (gb->cgb_double_speed? 0x2000 : 0x1000)) { - GB_apu_run(gb); - GB_apu_div_event(gb); - } - gb->div_counter = value; -} - -static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles) -{ - GB_STATE_MACHINE(gb, div, cycles, 1) { - GB_STATE(gb, div, 1); - GB_STATE(gb, div, 2); - GB_STATE(gb, div, 3); - } - - GB_set_internal_div_counter(gb, 0); -main: - GB_SLEEP(gb, div, 1, 3); - while (true) { - advance_tima_state_machine(gb); - GB_set_internal_div_counter(gb, gb->div_counter + 4); - gb->apu.apu_cycles += 4 << !gb->cgb_double_speed; - GB_SLEEP(gb, div, 2, 4); - } - - /* Todo: This is ugly to allow compatibility with 0.11 save states. Fix me when breaking save compatibility */ - { - div3: - /* Compensate for lack of prefetch emulation, as well as DIV's internal initial value */ - GB_set_internal_div_counter(gb, 8); - goto 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; - - if (!gb->stopped) { - GB_timers_run(gb, cycles); - advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode - } - - gb->debugger_ticks += cycles; - - if (!gb->cgb_double_speed) { - cycles <<= 1; - } - - // Not affected by speed boost - 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; - if (!gb->stopped) { // TODO: Verify what happens in STOP mode - GB_dma_run(gb); - GB_hdma_run(gb); - } - GB_apu_run(gb); - GB_display_run(gb, cycles); - GB_ir_run(gb); -} - -/* - This glitch is based on the expected results of mooneye-gb rapid_toggle test. - This glitch happens because how TIMA is increased, see GB_set_internal_div_counter. - According to GiiBiiAdvance, GBC's behavior is different, but this was not tested or implemented. -*/ -void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac) -{ - /* Glitch only happens when old_tac is enabled. */ - if (!(old_tac & 4)) return; - - unsigned old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3]; - unsigned new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3]; - - /* The bit used for overflow testing must have been 1 */ - if (gb->div_counter & old_clocks) { - /* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */ - if (!(new_tac & 4) || gb->div_counter & new_clocks) { - increase_tima(gb); - } - } -} - -void GB_rtc_run(GB_gameboy_t *gb) -{ - if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */ - time_t current_time = time(NULL); - while (gb->last_rtc_second < current_time) { - gb->last_rtc_second++; - if (++gb->rtc_real.seconds == 60) - { - gb->rtc_real.seconds = 0; - if (++gb->rtc_real.minutes == 60) - { - gb->rtc_real.minutes = 0; - if (++gb->rtc_real.hours == 24) - { - gb->rtc_real.hours = 0; - if (++gb->rtc_real.days == 0) - { - if (gb->rtc_real.high & 1) /* Bit 8 of days*/ - { - gb->rtc_real.high |= 0x80; /* Overflow bit */ - } - gb->rtc_real.high ^= 1; - } - } - } - } - } - } -} diff --git a/bsnes/gb/Core-new/timing.h b/bsnes/gb/Core-new/timing.h deleted file mode 100644 index 02ca54ce..00000000 --- a/bsnes/gb/Core-new/timing.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef timing_h -#define timing_h -#include "gb_struct_def.h" - -#ifdef GB_INTERNAL -void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles); -void GB_rtc_run(GB_gameboy_t *gb); -void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac); -bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */ -void GB_timing_sync(GB_gameboy_t *gb); - -enum { - GB_TIMA_RUNNING = 0, - GB_TIMA_RELOADING = 1, - GB_TIMA_RELOADED = 2 -}; - -#define GB_HALT_VALUE (0xFFFF) - -#define GB_SLEEP(gb, unit, state, cycles) do {\ - (gb)->unit##_cycles -= (cycles) * __state_machine_divisor; \ - if ((gb)->unit##_cycles <= 0) {\ - (gb)->unit##_state = state;\ - return;\ - unit##state:; \ - }\ -} while (0) - -#define GB_HALT(gb, unit) (gb)->unit##_cycles = GB_HALT_VALUE - -#define GB_STATE_MACHINE(gb, unit, cycles, divisor) \ -static const int __state_machine_divisor = divisor;\ -(gb)->unit##_cycles += cycles; \ -if ((gb)->unit##_cycles <= 0 || (gb)->unit##_cycles == GB_HALT_VALUE) {\ - return;\ -}\ -switch ((gb)->unit##_state) -#endif - -#define GB_STATE(gb, unit, state) case state: goto unit##state - -#define GB_UNIT(unit) int32_t unit##_cycles, unit##_state - -#endif /* timing_h */ diff --git a/bsnes/gb/Core/apu.c b/bsnes/gb/Core/apu.c index ee581385..9afa5c93 100644 --- a/bsnes/gb/Core/apu.c +++ b/bsnes/gb/Core/apu.c @@ -48,6 +48,23 @@ bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index) return false; } +static uint8_t agb_bias_for_channel(GB_gameboy_t *gb, unsigned index) +{ + if (!gb->apu.is_active[index]) return 0; + + switch (index) { + case GB_SQUARE_1: + return gb->apu.square_channels[GB_SQUARE_1].current_volume; + case GB_SQUARE_2: + return gb->apu.square_channels[GB_SQUARE_2].current_volume; + case GB_WAVE: + return 0; + case GB_NOISE: + return gb->apu.noise_channel.current_volume; + } + return 0; +} + static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset) { if (gb->model >= GB_MODEL_AGB) { @@ -66,15 +83,17 @@ static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsign } GB_sample_t output; + uint8_t bias = agb_bias_for_channel(gb, index); + if (gb->io_registers[GB_IO_NR51] & (1 << index)) { - output.right = (0xf - value * 2) * right_volume; + output.right = (0xf - value * 2 + bias) * right_volume; } else { output.right = 0xf * right_volume; } if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) { - output.left = (0xf - value * 2) * left_volume; + output.left = (0xf - value * 2 + bias) * left_volume; } else { output.left = 0xf * left_volume; @@ -681,6 +700,21 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) case GB_IO_NR14: case GB_IO_NR24: { unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1; + + /* TODO: When the sample length changes right before being updated, the countdown should change to the + old length, but the current sample should not change. Because our write timing isn't accurate to + the T-cycle, we hack around it by stepping the sample index backwards. */ + if ((value & 0x80) == 0 && gb->apu.is_active[index]) { + /* On an AGB, as well as on CGB C and earlier (TODO: Tested: 0, B and C), it behaves slightly different on + double speed. */ + if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */ || gb->apu.square_channels[index].sample_countdown & 1) { + if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) { + gb->apu.square_channels[index].current_sample_index--; + gb->apu.square_channels[index].current_sample_index &= 7; + } + } + } + gb->apu.square_channels[index].sample_length &= 0xFF; gb->apu.square_channels[index].sample_length |= (value & 7) << 8; if (index == GB_SQUARE_1) { @@ -970,9 +1004,23 @@ void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate) if (sample_rate) { gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate); } + gb->apu_output.rate_set_in_clocks = false; GB_apu_update_cycles_per_sample(gb); } +void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample) +{ + + if (cycles_per_sample == 0) { + GB_set_sample_rate(gb, 0); + return; + } + gb->apu_output.cycles_per_sample = cycles_per_sample; + gb->apu_output.sample_rate = GB_get_clock_rate(gb) / cycles_per_sample * 2; + gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample); + gb->apu_output.rate_set_in_clocks = true; +} + void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback) { gb->apu_output.sample_callback = callback; @@ -985,6 +1033,7 @@ void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode) void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb) { + if (gb->apu_output.rate_set_in_clocks) return; if (gb->apu_output.sample_rate) { gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */ } diff --git a/bsnes/gb/Core/apu.h b/bsnes/gb/Core/apu.h index 7f8acfcc..885e0ce3 100644 --- a/bsnes/gb/Core/apu.h +++ b/bsnes/gb/Core/apu.h @@ -143,9 +143,12 @@ typedef struct { GB_double_sample_t highpass_diff; GB_sample_callback_t sample_callback; + + bool rate_set_in_clocks; } GB_apu_output_t; void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate); +void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */ void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode); void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback); #ifdef GB_INTERNAL diff --git a/bsnes/gb/Core/display.c b/bsnes/gb/Core/display.c index bc251206..5c1935c6 100644 --- a/bsnes/gb/Core/display.c +++ b/bsnes/gb/Core/display.c @@ -127,13 +127,13 @@ static void display_vblank(GB_gameboy_t *gb) if (GB_is_hle_sgb(gb)) { GB_sgb_render(gb); } - + if (gb->turbo) { if (GB_timing_sync_turbo(gb)) { return; } } - + if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) { /* LCD is off, set screen to white or black (if LCD is on in stop mode) */ if (gb->sgb) { @@ -162,9 +162,20 @@ static inline uint8_t scale_channel(uint8_t x) static inline uint8_t scale_channel_with_curve(uint8_t x) { - return (uint8_t[]){0,2,4,7,12,18,25,34,42,52,62,73,85,97,109,121,134,146,158,170,182,193,203,213,221,230,237,243,248,251,253,255,}[x]; + return (uint8_t[]){0,2,4,7,12,18,25,34,42,52,62,73,85,97,109,121,134,146,158,170,182,193,203,213,221,230,237,243,248,251,253,255}[x]; } +static inline uint8_t scale_channel_with_curve_agb(uint8_t x) +{ + return (uint8_t[]){0,2,5,10,15,20,26,32,38,45,52,60,68,76,84,92,101,110,119,128,138,148,158,168,178,189,199,210,221,232,244,255}[x]; +} + +static inline uint8_t scale_channel_with_curve_sgb(uint8_t x) +{ + return (uint8_t[]){0,2,5,9,15,20,27,34,42,50,58,67,76,85,94,104,114,123,133,143,153,163,173,182,192,202,211,220,229,238,247,255}[x]; +} + + uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color) { uint8_t r = (color) & 0x1F; @@ -177,13 +188,29 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color) b = scale_channel(b); } else { - r = scale_channel_with_curve(r); - g = scale_channel_with_curve(g); - b = scale_channel_with_curve(b); + if (GB_is_sgb(gb)) { + return gb->rgb_encode_callback(gb, + scale_channel_with_curve_sgb(r), + scale_channel_with_curve_sgb(g), + scale_channel_with_curve_sgb(b)); + } + bool agb = gb->model == GB_MODEL_AGB; + r = agb? scale_channel_with_curve_agb(r) : scale_channel_with_curve(r); + g = agb? scale_channel_with_curve_agb(g) : scale_channel_with_curve(g); + b = agb? scale_channel_with_curve_agb(b) : scale_channel_with_curve(b); if (gb->color_correction_mode != GB_COLOR_CORRECTION_CORRECT_CURVES) { - uint8_t new_g = (g * 3 + b) / 4; - uint8_t new_r = r, new_b = b; + uint8_t new_r, new_g, new_b; + if (agb) { + new_r = (r * 7 + g * 1) / 8; + new_g = (g * 3 + b * 1) / 4; + new_b = (b * 7 + r * 1) / 8; + } + else { + new_g = (g * 3 + b) / 4; + new_r = r; + new_b = b; + } if (gb->color_correction_mode == GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS) { uint8_t old_max = MAX(r, MAX(g, b)); uint8_t new_max = MAX(new_r, MAX(new_g, new_b)); @@ -200,7 +227,7 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color) if (new_min != 0xff) { new_r = 0xff - (0xff - new_r) * (0xff - old_min) / (0xff - new_min); new_g = 0xff - (0xff - new_g) * (0xff - old_min) / (0xff - new_min); - new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min);; + new_b = 0xff - (0xff - new_b) * (0xff - old_min) / (0xff - new_min); } } r = new_r; @@ -377,7 +404,6 @@ static void render_pixel_if_possible(GB_gameboy_t *gb) } uint8_t icd_pixel = 0; - { uint8_t pixel = bg_enabled? fifo_item->pixel : 0; if (pixel && bg_priority) { @@ -394,7 +420,6 @@ static void render_pixel_if_possible(GB_gameboy_t *gb) else if (gb->model & GB_MODEL_NO_SFC_BIT) { if (gb->icd_pixel_callback) { icd_pixel = pixel; - //gb->icd_pixel_callback(gb, pixel); } } else { @@ -423,7 +448,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb) gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel]; } } - + if (gb->model & GB_MODEL_NO_SFC_BIT) { if (gb->icd_pixel_callback) { gb->icd_pixel_callback(gb, icd_pixel); @@ -769,10 +794,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false); /* Todo: find out actual access time of SCX */ gb->position_in_line = - (gb->io_registers[GB_IO_SCX] & 7) - 8; - gb->current_lcd_line++; // Todo: unverified timing - if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) { - display_vblank(gb); - } + gb->fetcher_x = ((gb->io_registers[GB_IO_SCX]) / 8) & 0x1f; gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7); @@ -909,6 +931,12 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) } GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line); gb->mode_for_interrupt = 2; + + // Todo: unverified timing + gb->current_lcd_line++; + if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) { + display_vblank(gb); + } if (gb->icd_hreset_callback) { gb->icd_hreset_callback(gb); @@ -985,7 +1013,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) gb->window_disabled_while_active = false; gb->current_line = 0; // TODO: not the correct timing - gb->current_lcd_line = -1; + gb->current_lcd_line = 0; if (gb->icd_vreset_callback) { gb->icd_vreset_callback(gb); } diff --git a/bsnes/gb/Core/gb.c b/bsnes/gb/Core/gb.c index b1f4ea41..f29d400a 100644 --- a/bsnes/gb/Core/gb.c +++ b/bsnes/gb/Core/gb.c @@ -12,11 +12,19 @@ #include "random.h" #include "gb.h" + #ifdef DISABLE_REWIND #define GB_rewind_free(...) #define GB_rewind_push(...) #endif + +static inline uint32_t state_magic(void) +{ + if (sizeof(bool) == 1) return 'SAME'; + return 'S4ME'; +} + void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args) { char *string = NULL; @@ -659,7 +667,7 @@ void GB_disconnect_serial(GB_gameboy_t *gb) bool GB_is_inited(GB_gameboy_t *gb) { - return gb->magic == 'SAME'; + return gb->magic == state_magic(); } bool GB_is_cgb(GB_gameboy_t *gb) @@ -711,7 +719,8 @@ static void reset_ram(GB_gameboy_t *gb) case GB_MODEL_DMG_B: case GB_MODEL_SGB_NTSC: /* Unverified*/ case GB_MODEL_SGB_PAL: /* Unverified */ - case GB_MODEL_SGB_NO_SFC: + case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */ + case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */ for (unsigned i = 0; i < gb->ram_size; i++) { gb->ram[i] = GB_random(); if (i & 0x100) { @@ -757,7 +766,8 @@ static void reset_ram(GB_gameboy_t *gb) case GB_MODEL_DMG_B: case GB_MODEL_SGB_NTSC: /* Unverified*/ case GB_MODEL_SGB_PAL: /* Unverified */ - case GB_MODEL_SGB_NO_SFC: + case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */ + case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */ case GB_MODEL_SGB2: case GB_MODEL_SGB2_NO_SFC: for (unsigned i = 0; i < sizeof(gb->hram); i++) { @@ -782,7 +792,8 @@ static void reset_ram(GB_gameboy_t *gb) case GB_MODEL_DMG_B: case GB_MODEL_SGB_NTSC: /* Unverified */ case GB_MODEL_SGB_PAL: /* Unverified */ - case GB_MODEL_SGB_NO_SFC: /* Unverified */ + case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */ + case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */ case GB_MODEL_SGB2: case GB_MODEL_SGB2_NO_SFC: for (unsigned i = 0; i < 8; i++) { @@ -810,7 +821,8 @@ static void reset_ram(GB_gameboy_t *gb) case GB_MODEL_DMG_B: case GB_MODEL_SGB_NTSC: /* Unverified*/ case GB_MODEL_SGB_PAL: /* Unverified */ - case GB_MODEL_SGB_NO_SFC: /* Unverified */ + case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */ + case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */ case GB_MODEL_SGB2: case GB_MODEL_SGB2_NO_SFC: { uint8_t temp; @@ -857,7 +869,7 @@ void GB_reset(GB_gameboy_t *gb) gb->mbc_rom_bank = 1; gb->last_rtc_second = time(NULL); gb->cgb_ram_bank = 1; - gb->io_registers[GB_IO_JOYP] = 0xF; + gb->io_registers[GB_IO_JOYP] = 0xCF; gb->mbc_ram_size = mbc_ram_size; if (GB_is_cgb(gb)) { gb->ram_size = 0x1000 * 8; @@ -924,7 +936,7 @@ void GB_reset(GB_gameboy_t *gb) gb->nontrivial_jump_state = NULL; } - gb->magic = (uintptr_t)'SAME'; + gb->magic = state_magic(); } void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model) @@ -1016,12 +1028,12 @@ void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier) uint32_t GB_get_clock_rate(GB_gameboy_t *gb) { - if (gb->model == GB_MODEL_SGB_NTSC) { - return SGB_NTSC_FREQUENCY * gb->clock_multiplier; - } - if (gb->model == GB_MODEL_SGB_PAL) { + if (gb->model & GB_MODEL_PAL_BIT) { return SGB_PAL_FREQUENCY * gb->clock_multiplier; } + if ((gb->model & ~GB_MODEL_NO_SFC_BIT) == GB_MODEL_SGB) { + return SGB_NTSC_FREQUENCY * gb->clock_multiplier; + } return CPU_FREQUENCY * gb->clock_multiplier; } diff --git a/bsnes/gb/Core/gb.h b/bsnes/gb/Core/gb.h index c3e877c2..a5611116 100644 --- a/bsnes/gb/Core/gb.h +++ b/bsnes/gb/Core/gb.h @@ -1,7 +1,6 @@ #ifndef GB_h #define GB_h #define typeof __typeof__ -#define _XOPEN_SOURCE 500 #include #include #include @@ -71,7 +70,9 @@ typedef enum { GB_MODEL_SGB = 0x004, GB_MODEL_SGB_NTSC = GB_MODEL_SGB, GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT, - GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT, + GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT, + GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC, + GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT, // GB_MODEL_MGB = 0x100, GB_MODEL_SGB2 = 0x101, GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT, @@ -277,10 +278,6 @@ typedef struct { This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64 bit platforms. */ -/* We make sure bool is 1 for cross-platform save state compatibility. */ -/* Todo: We might want to typedef our own bool if this prevents SameBoy from working on specific platforms. */ -//_Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1"); - #ifdef GB_INTERNAL struct GB_gameboy_s { #else @@ -544,6 +541,7 @@ struct GB_gameboy_internal_s { GB_icd_pixel_callback_t icd_pixel_callback; GB_icd_vreset_callback_t icd_hreset_callback; GB_icd_vreset_callback_t icd_vreset_callback; + GB_read_memory_callback_t read_memory_callback; /* IR */ long cycles_since_ir_change; // In 8MHz units diff --git a/bsnes/gb/Core/joypad.c b/bsnes/gb/Core/joypad.c index 6c0eaff7..b8d4fdb4 100644 --- a/bsnes/gb/Core/joypad.c +++ b/bsnes/gb/Core/joypad.c @@ -3,7 +3,7 @@ void GB_update_joyp(GB_gameboy_t *gb) { - if (gb->model & GB_MODEL_SGB_NO_SFC) return; + if (gb->model & GB_MODEL_NO_SFC_BIT) return; uint8_t key_selection = 0; uint8_t previous_state = 0; @@ -12,7 +12,7 @@ void GB_update_joyp(GB_gameboy_t *gb) previous_state = gb->io_registers[GB_IO_JOYP] & 0xF; key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3; gb->io_registers[GB_IO_JOYP] &= 0xF0; - uint8_t current_player = gb->sgb? gb->sgb->current_player : 0; + uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0; switch (key_selection) { case 3: if (gb->sgb && gb->sgb->player_count > 1) { @@ -73,7 +73,7 @@ void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value) if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) { gb->io_registers[GB_IO_IF] |= 0x10; } - + gb->io_registers[GB_IO_JOYP] |= 0xC0; } void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed) diff --git a/bsnes/gb/Core/memory.c b/bsnes/gb/Core/memory.c index 73459ddd..003bb77f 100644 --- a/bsnes/gb/Core/memory.c +++ b/bsnes/gb/Core/memory.c @@ -273,7 +273,8 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) case GB_MODEL_DMG_B: case GB_MODEL_SGB_NTSC: case GB_MODEL_SGB_PAL: - case GB_MODEL_SGB_NO_SFC: + case GB_MODEL_SGB_NTSC_NO_SFC: + case GB_MODEL_SGB_PAL_NO_SFC: case GB_MODEL_SGB2: case GB_MODEL_SGB2_NO_SFC: ; @@ -310,7 +311,6 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) (gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0); case GB_IO_JOYP: GB_timing_sync(gb); - return gb->io_registers[addr & 0xFF] | 0xC0; case GB_IO_TMA: case GB_IO_LCDC: case GB_IO_SCY: @@ -422,11 +422,9 @@ static GB_read_function_t * const read_map[] = read_ram, read_high_memory, /* EXXX FXXX */ }; -static GB_read_memory_callback_t GB_read_memory_callback_v = 0; - void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback) { - GB_read_memory_callback_v = callback; + gb->read_memory_callback = callback; } uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr) @@ -437,9 +435,9 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr) if (is_addr_in_dma_use(gb, addr)) { addr = gb->dma_current_src; } - if (GB_read_memory_callback_v) { + if (gb->read_memory_callback) { uint8_t data = read_map[addr >> 12](gb, addr); - data = GB_read_memory_callback_v(gb, addr, data); + data = gb->read_memory_callback(gb, addr, data); return data; } return read_map[addr >> 12](gb, addr); @@ -599,7 +597,8 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case GB_MODEL_DMG_B: case GB_MODEL_SGB_NTSC: case GB_MODEL_SGB_PAL: - case GB_MODEL_SGB_NO_SFC: + case GB_MODEL_SGB_NTSC_NO_SFC: + case GB_MODEL_SGB_PAL_NO_SFC: case GB_MODEL_SGB2: case GB_MODEL_SGB2_NO_SFC: case GB_MODEL_CGB_E: @@ -754,9 +753,15 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) return; case GB_IO_JOYP: - gb->io_registers[GB_IO_JOYP] = value & 0xF0; - GB_sgb_write(gb, value); - GB_update_joyp(gb); + if (gb->joyp_write_callback) { + gb->joyp_write_callback(gb, value); + GB_update_joyp(gb); + } + else if ((gb->io_registers[GB_IO_JOYP] & 0x30) != (value & 0x30)) { + GB_sgb_write(gb, value); + gb->io_registers[GB_IO_JOYP] = value & 0xF0; + GB_update_joyp(gb); + } return; case GB_IO_BIOS: diff --git a/bsnes/gb/Core/sgb.c b/bsnes/gb/Core/sgb.c index de918e82..7ebeae07 100644 --- a/bsnes/gb/Core/sgb.c +++ b/bsnes/gb/Core/sgb.c @@ -72,6 +72,75 @@ static inline void load_attribute_file(GB_gameboy_t *gb, unsigned file_index) } } +static const uint16_t built_in_palettes[] = +{ + 0x67BF, 0x265B, 0x10B5, 0x2866, + 0x637B, 0x3AD9, 0x0956, 0x0000, + 0x7F1F, 0x2A7D, 0x30F3, 0x4CE7, + 0x57FF, 0x2618, 0x001F, 0x006A, + 0x5B7F, 0x3F0F, 0x222D, 0x10EB, + 0x7FBB, 0x2A3C, 0x0015, 0x0900, + 0x2800, 0x7680, 0x01EF, 0x2FFF, + 0x73BF, 0x46FF, 0x0110, 0x0066, + 0x533E, 0x2638, 0x01E5, 0x0000, + 0x7FFF, 0x2BBF, 0x00DF, 0x2C0A, + 0x7F1F, 0x463D, 0x74CF, 0x4CA5, + 0x53FF, 0x03E0, 0x00DF, 0x2800, + 0x433F, 0x72D2, 0x3045, 0x0822, + 0x7FFA, 0x2A5F, 0x0014, 0x0003, + 0x1EED, 0x215C, 0x42FC, 0x0060, + 0x7FFF, 0x5EF7, 0x39CE, 0x0000, + 0x4F5F, 0x630E, 0x159F, 0x3126, + 0x637B, 0x121C, 0x0140, 0x0840, + 0x66BC, 0x3FFF, 0x7EE0, 0x2C84, + 0x5FFE, 0x3EBC, 0x0321, 0x0000, + 0x63FF, 0x36DC, 0x11F6, 0x392A, + 0x65EF, 0x7DBF, 0x035F, 0x2108, + 0x2B6C, 0x7FFF, 0x1CD9, 0x0007, + 0x53FC, 0x1F2F, 0x0E29, 0x0061, + 0x36BE, 0x7EAF, 0x681A, 0x3C00, + 0x7BBE, 0x329D, 0x1DE8, 0x0423, + 0x739F, 0x6A9B, 0x7293, 0x0001, + 0x5FFF, 0x6732, 0x3DA9, 0x2481, + 0x577F, 0x3EBC, 0x456F, 0x1880, + 0x6B57, 0x6E1B, 0x5010, 0x0007, + 0x0F96, 0x2C97, 0x0045, 0x3200, + 0x67FF, 0x2F17, 0x2230, 0x1548, +}; + +static const struct { + char name[16]; + unsigned palette_index; +} palette_assignments[] = +{ + {"ZELDA", 5}, + {"SUPER MARIOLAND", 6}, + {"MARIOLAND2", 0x14}, + {"SUPERMARIOLAND3", 2}, + {"KIRBY DREAM LAND", 0xB}, + {"HOSHINOKA-BI", 0xB}, + {"KIRBY'S PINBALL", 3}, + {"YOSSY NO TAMAGO", 0xC}, + {"MARIO & YOSHI", 0xC}, + {"YOSSY NO COOKIE", 4}, + {"YOSHI'S COOKIE", 4}, + {"DR.MARIO", 0x12}, + {"TETRIS", 0x11}, + {"YAKUMAN", 0x13}, + {"METROID2", 0x1F}, + {"KAERUNOTAMENI", 9}, + {"GOLF", 0x18}, + {"ALLEY WAY", 0x16}, + {"BASEBALL", 0xF}, + {"TENNIS", 0x17}, + {"F1RACE", 0x1E}, + {"KID ICARUS", 0xE}, + {"QIX", 0x19}, + {"SOLARSTRIKER", 7}, + {"X", 0x1C}, + {"GBWARS", 0x15}, +}; + static void command_ready(GB_gameboy_t *gb) { /* SGB header commands are used to send the contents of the header to the SNES CPU. @@ -81,6 +150,8 @@ static void command_ready(GB_gameboy_t *gb) 0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */ if ((gb->sgb->command[0] & 0xF1) == 0xF1) { + if(gb->boot_rom_finished) return; + uint8_t checksum = 0; for (unsigned i = 2; i < 0x10; i++) { checksum += gb->sgb->command[i]; @@ -90,14 +161,23 @@ static void command_ready(GB_gameboy_t *gb) gb->sgb->disable_commands = true; return; } - if (gb->sgb->command[0] == 0xf9) { - if (gb->sgb->command[0xc] != 3) { // SGB Flag - gb->sgb->disable_commands = true; - } + unsigned index = (gb->sgb->command[0] >> 1) & 7; + if (index > 5) { + return; } - else if (gb->sgb->command[0] == 0xfb) { - if (gb->sgb->command[0x3] != 0x33) { // Old licensee code + memcpy(&gb->sgb->received_header[index * 14], &gb->sgb->command[2], 14); + if (gb->sgb->command[0] == 0xfb) { + if (gb->sgb->received_header[0x42] != 3 || gb->sgb->received_header[0x47] != 0x33) { gb->sgb->disable_commands = true; + for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) { + if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) { + gb->sgb->effective_palettes[0] = built_in_palettes[palette_assignments[i].palette_index * 4 - 4]; + gb->sgb->effective_palettes[1] = built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4]; + gb->sgb->effective_palettes[2] = built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4]; + gb->sgb->effective_palettes[3] = built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4]; + break; + } + } } } return; @@ -257,8 +337,15 @@ static void command_ready(GB_gameboy_t *gb) // Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this break; case MLT_REQ: - gb->sgb->player_count = (uint8_t[]){1, 2, 1, 4}[gb->sgb->command[1] & 3]; - gb->sgb->current_player = gb->sgb->player_count - 1; + if (gb->sgb->player_count == 1) { + gb->sgb->current_player = 0; + } + gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility, + fix this to be 0 based. */ + if (gb->sgb->player_count == 3) { + gb->sgb->current_player++; + } + gb->sgb->mlt_lock = true; break; case CHR_TRN: gb->sgb->vram_transfer_countdown = 2; @@ -298,30 +385,33 @@ static void command_ready(GB_gameboy_t *gb) } void GB_sgb_write(GB_gameboy_t *gb, uint8_t value) -{ - if (gb->joyp_write_callback) { - gb->joyp_write_callback(gb, value); - } +{ if (!GB_is_sgb(gb)) return; if (!GB_is_hle_sgb(gb)) { /* Notify via callback */ return; } if (gb->sgb->disable_commands) return; - if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) return; + if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) { + return; + } uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8; if ((gb->sgb->command[0] & 0xF1) == 0xF1) { command_size = SGB_PACKET_SIZE * 8; } + if ((value & 0x20) == 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) != 0) { + gb->sgb->mlt_lock ^= true; + } + switch ((value >> 4) & 3) { case 3: gb->sgb->ready_for_pulse = true; - /* TODO: This is the logic used by BGB which *should* work for most/all games, but a proper test ROM is needed */ - if (gb->sgb->player_count > 1 && (gb->io_registers[GB_IO_JOYP] & 0x30) == 0x10) { + if ((gb->sgb->player_count & 1) == 0 && !gb->sgb->mlt_lock) { gb->sgb->current_player++; - gb->sgb->current_player &= gb->sgb->player_count - 1; + gb->sgb->current_player &= 3; + gb->sgb->mlt_lock = true; } break; @@ -382,22 +472,9 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value) } } -static inline uint8_t scale_channel(uint8_t x) -{ - return (x << 3) | (x >> 2); -} - static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color) { - uint8_t r = (color) & 0x1F; - uint8_t g = (color >> 5) & 0x1F; - uint8_t b = (color >> 10) & 0x1F; - - r = scale_channel(r); - g = scale_channel(g); - b = scale_channel(b); - - return gb->rgb_encode_callback(gb, r, g, b); + return GB_convert_rgb15(gb, color); } static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade) @@ -410,11 +487,9 @@ static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_ if (g >= 0x20) g = 0; if (b >= 0x20) b = 0; - r = scale_channel(r); - g = scale_channel(g); - b = scale_channel(b); + color = r | (g << 5) | (b << 10); - return gb->rgb_encode_callback(gb, r, g, b); + return GB_convert_rgb15(gb, color); } #include @@ -679,10 +754,10 @@ void GB_sgb_load_default_data(GB_gameboy_t *gb) /* Re-center */ memmove(&gb->sgb->border.map[25 * 32 + 1], &gb->sgb->border.map[25 * 32], (32 * 3 - 1) * sizeof(gb->sgb->border.map[0])); } - gb->sgb->effective_palettes[0] = 0x639E; - gb->sgb->effective_palettes[1] = 0x263A; - gb->sgb->effective_palettes[2] = 0x10D4; - gb->sgb->effective_palettes[3] = 0x2866; + gb->sgb->effective_palettes[0] = built_in_palettes[0]; + gb->sgb->effective_palettes[1] = built_in_palettes[1]; + gb->sgb->effective_palettes[2] = built_in_palettes[2]; + gb->sgb->effective_palettes[3] = built_in_palettes[3]; } static double fm_synth(double phase) diff --git a/bsnes/gb/Core/sgb.h b/bsnes/gb/Core/sgb.h index 2c6e8ee9..df90253c 100644 --- a/bsnes/gb/Core/sgb.h +++ b/bsnes/gb/Core/sgb.h @@ -49,6 +49,12 @@ struct GB_sgb_s { /* Intro */ int16_t intro_animation; + + /* GB Header */ + uint8_t received_header[0x54]; + + /* Multiplayer (cont) */ + bool mlt_lock; }; void GB_sgb_write(GB_gameboy_t *gb, uint8_t value); diff --git a/bsnes/gb/Core/sm83_cpu.c b/bsnes/gb/Core/sm83_cpu.c index 12ca6704..77248b51 100644 --- a/bsnes/gb/Core/sm83_cpu.c +++ b/bsnes/gb/Core/sm83_cpu.c @@ -33,6 +33,7 @@ static const GB_conflict_t cgb_conflict_map[0x80] = { /* Todo: most values not verified, and probably differ between revisions */ }; +/* Todo: verify on an MGB */ static const GB_conflict_t dmg_conflict_map[0x80] = { [GB_IO_IF] = GB_CONFLICT_WRITE_CPU, [GB_IO_LYC] = GB_CONFLICT_READ_OLD, @@ -40,7 +41,6 @@ static const GB_conflict_t dmg_conflict_map[0x80] = { [GB_IO_SCY] = GB_CONFLICT_READ_NEW, [GB_IO_STAT] = GB_CONFLICT_STAT_DMG, - /* Todo: these are GB_CONFLICT_READ_NEW on MGB/SGB2 */ [GB_IO_BGP] = GB_CONFLICT_PALETTE_DMG, [GB_IO_OBP0] = GB_CONFLICT_PALETTE_DMG, [GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG, @@ -51,6 +51,24 @@ static const GB_conflict_t dmg_conflict_map[0x80] = { [GB_IO_SCX] = GB_CONFLICT_READ_NEW, }; +/* Todo: Verify on an SGB1 */ +static const GB_conflict_t sgb_conflict_map[0x80] = { + [GB_IO_IF] = GB_CONFLICT_WRITE_CPU, + [GB_IO_LYC] = GB_CONFLICT_READ_OLD, + [GB_IO_LCDC] = GB_CONFLICT_READ_NEW, + [GB_IO_SCY] = GB_CONFLICT_READ_NEW, + [GB_IO_STAT] = GB_CONFLICT_STAT_DMG, + + [GB_IO_BGP] = GB_CONFLICT_READ_NEW, + [GB_IO_OBP0] = GB_CONFLICT_READ_NEW, + [GB_IO_OBP1] = GB_CONFLICT_READ_NEW, + + /* Todo: these were not verified at all */ + [GB_IO_WY] = GB_CONFLICT_READ_NEW, + [GB_IO_WX] = GB_CONFLICT_READ_NEW, + [GB_IO_SCX] = GB_CONFLICT_READ_NEW, +}; + static uint8_t cycle_read(GB_gameboy_t *gb, uint16_t addr) { if (gb->pending_cycles) { @@ -92,7 +110,17 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) assert(gb->pending_cycles); GB_conflict_t conflict = GB_CONFLICT_READ_OLD; if ((addr & 0xFF80) == 0xFF00) { - conflict = (GB_is_cgb(gb)? cgb_conflict_map : dmg_conflict_map)[addr & 0x7F]; + const GB_conflict_t *map = NULL; + if (GB_is_cgb(gb)) { + map = cgb_conflict_map; + } + else if (GB_is_sgb(gb)) { + map = sgb_conflict_map; + } + else { + map = dmg_conflict_map; + } + conflict = map[addr & 0x7F]; } switch (conflict) { case GB_CONFLICT_READ_OLD: