Save state compatibility breaking cleanup

This commit is contained in:
Lior Halphon 2021-11-07 01:10:58 +02:00
parent 5565c2540b
commit fbf1bb7f98
17 changed files with 280 additions and 386 deletions

View File

@ -12,6 +12,8 @@
#include "BigSurToolbar.h" #include "BigSurToolbar.h"
#import "GBPaletteEditorController.h" #import "GBPaletteEditorController.h"
#define GB_MODEL_PAL_BIT_OLD 0x1000
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */ /* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
/* Todo: Split into category files! This is so messy!!! */ /* Todo: Split into category files! This is so messy!!! */

View File

@ -280,7 +280,7 @@ static void render(GB_gameboy_t *gb)
static void update_square_sample(GB_gameboy_t *gb, unsigned index) static void update_square_sample(GB_gameboy_t *gb, unsigned index)
{ {
if (gb->apu.square_channels[index].current_sample_index & 0x80) return; if (gb->apu.square_channels[index].sample_surpressed) return;
uint8_t duty = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6; uint8_t duty = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6;
update_sample(gb, index, update_sample(gb, index,
@ -377,7 +377,7 @@ 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]; uint8_t nrx2 = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22];
if (gb->apu.square_envelope_clock[index].locked) return; if (gb->apu.square_channels[index].envelope_clock.locked) return;
if (!(nrx2 & 7)) return; if (!(nrx2 & 7)) return;
if (gb->cgb_double_speed) { if (gb->cgb_double_speed) {
if (index == GB_SQUARE_1) { if (index == GB_SQUARE_1) {
@ -393,7 +393,7 @@ static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index)
gb->apu.square_channels[index].current_volume++; gb->apu.square_channels[index].current_volume++;
} }
else { else {
gb->apu.square_envelope_clock[index].locked = true; gb->apu.square_channels[index].envelope_clock.locked = true;
} }
} }
else { else {
@ -401,7 +401,7 @@ static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index)
gb->apu.square_channels[index].current_volume--; gb->apu.square_channels[index].current_volume--;
} }
else { else {
gb->apu.square_envelope_clock[index].locked = true; gb->apu.square_channels[index].envelope_clock.locked = true;
} }
} }
@ -414,7 +414,7 @@ static void tick_noise_envelope(GB_gameboy_t *gb)
{ {
uint8_t nr42 = gb->io_registers[GB_IO_NR42]; uint8_t nr42 = gb->io_registers[GB_IO_NR42];
if (gb->apu.noise_envelope_clock.locked) return; if (gb->apu.noise_channel.envelope_clock.locked) return;
if (!(nr42 & 7)) return; if (!(nr42 & 7)) return;
if (gb->cgb_double_speed) { if (gb->cgb_double_speed) {
@ -426,7 +426,7 @@ static void tick_noise_envelope(GB_gameboy_t *gb)
gb->apu.noise_channel.current_volume++; gb->apu.noise_channel.current_volume++;
} }
else { else {
gb->apu.noise_envelope_clock.locked = true; gb->apu.noise_channel.envelope_clock.locked = true;
} }
} }
else { else {
@ -434,7 +434,7 @@ static void tick_noise_envelope(GB_gameboy_t *gb)
gb->apu.noise_channel.current_volume--; gb->apu.noise_channel.current_volume--;
} }
else { else {
gb->apu.noise_envelope_clock.locked = true; gb->apu.noise_channel.envelope_clock.locked = true;
} }
} }
@ -486,27 +486,27 @@ void GB_apu_div_event(GB_gameboy_t *gb)
if ((gb->apu.div_divider & 7) == 7) { if ((gb->apu.div_divider & 7) == 7) {
unrolled for (unsigned i = GB_SQUARE_2 + 1; i--;) { unrolled for (unsigned i = GB_SQUARE_2 + 1; i--;) {
if (!gb->apu.square_envelope_clock[i].clock) { if (!gb->apu.square_channels[i].envelope_clock.clock) {
gb->apu.square_channels[i].volume_countdown--; gb->apu.square_channels[i].volume_countdown--;
gb->apu.square_channels[i].volume_countdown &= 7; gb->apu.square_channels[i].volume_countdown &= 7;
} }
} }
if (!gb->apu.noise_envelope_clock.clock) { if (!gb->apu.noise_channel.envelope_clock.clock) {
gb->apu.noise_channel.volume_countdown--; gb->apu.noise_channel.volume_countdown--;
gb->apu.noise_channel.volume_countdown &= 7; gb->apu.noise_channel.volume_countdown &= 7;
} }
} }
unrolled for (unsigned i = GB_SQUARE_2 + 1; i--;) { unrolled for (unsigned i = GB_SQUARE_2 + 1; i--;) {
if (gb->apu.square_envelope_clock[i].clock) { if (gb->apu.square_channels[i].envelope_clock.clock) {
tick_square_envelope(gb, i); tick_square_envelope(gb, i);
gb->apu.square_envelope_clock[i].clock = false; gb->apu.square_channels[i].envelope_clock.clock = false;
} }
} }
if (gb->apu.noise_envelope_clock.clock) { if (gb->apu.noise_channel.envelope_clock.clock) {
tick_noise_envelope(gb); tick_noise_envelope(gb);
gb->apu.noise_envelope_clock.clock = false; gb->apu.noise_channel.envelope_clock.clock = false;
} }
if ((gb->apu.div_divider & 1) == 1) { if ((gb->apu.div_divider & 1) == 1) {
@ -562,12 +562,12 @@ void GB_apu_div_secondary_event(GB_gameboy_t *gb)
unrolled for (unsigned i = GB_SQUARE_2 + 1; i--;) { unrolled for (unsigned i = GB_SQUARE_2 + 1; i--;) {
uint8_t nrx2 = gb->io_registers[i == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22]; 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) { if (gb->apu.is_active[i] && gb->apu.square_channels[i].volume_countdown == 0) {
gb->apu.square_envelope_clock[i].clock = (gb->apu.square_channels[i].volume_countdown = nrx2 & 7); gb->apu.square_channels[i].envelope_clock.clock = (gb->apu.square_channels[i].volume_countdown = nrx2 & 7);
} }
} }
if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0) { if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0) {
gb->apu.noise_envelope_clock.clock = (gb->apu.noise_channel.volume_countdown = gb->io_registers[GB_IO_NR42] & 7); gb->apu.noise_channel.envelope_clock.clock = (gb->apu.noise_channel.volume_countdown = gb->io_registers[GB_IO_NR42] & 7);
} }
} }
@ -585,10 +585,10 @@ static void step_lfsr(GB_gameboy_t *gb, unsigned cycles_offset)
gb->apu.noise_channel.lfsr &= ~high_bit_mask; gb->apu.noise_channel.lfsr &= ~high_bit_mask;
} }
gb->apu.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1; gb->apu.noise_channel.current_lfsr_sample = gb->apu.noise_channel.lfsr & 1;
if (gb->apu.is_active[GB_NOISE]) { if (gb->apu.is_active[GB_NOISE]) {
update_sample(gb, GB_NOISE, update_sample(gb, GB_NOISE,
gb->apu.current_lfsr_sample ? gb->apu.noise_channel.current_lfsr_sample ?
gb->apu.noise_channel.current_volume : 0, gb->apu.noise_channel.current_volume : 0,
cycles_offset); cycles_offset);
} }
@ -601,26 +601,26 @@ void GB_apu_run(GB_gameboy_t *gb)
gb->apu.apu_cycles = 0; gb->apu.apu_cycles = 0;
if (!cycles) return; if (!cycles) return;
if (unlikely(gb->apu.channel_3_delayed_bugged_read)) { if (unlikely(gb->apu.wave_channel.delayed_bugged_read)) {
gb->apu.channel_3_delayed_bugged_read = false; gb->apu.wave_channel.delayed_bugged_read = false;
gb->apu.wave_channel.current_sample_byte = gb->apu.wave_channel.current_sample_byte =
gb->io_registers[GB_IO_WAV_START + (gb->address_bus & 0xF)]; gb->io_registers[GB_IO_WAV_START + (gb->address_bus & 0xF)];
} }
bool start_ch4 = false; bool start_ch4 = false;
if (likely(!gb->stopped || GB_is_cgb(gb))) { if (likely(!gb->stopped || GB_is_cgb(gb))) {
if (gb->apu.channel_4_dmg_delayed_start) { if (gb->apu.noise_channel.dmg_delayed_start) {
if (gb->apu.channel_4_dmg_delayed_start == cycles) { if (gb->apu.noise_channel.dmg_delayed_start == cycles) {
gb->apu.channel_4_dmg_delayed_start = 0; gb->apu.noise_channel.dmg_delayed_start = 0;
start_ch4 = true; start_ch4 = true;
} }
else if (gb->apu.channel_4_dmg_delayed_start > cycles) { else if (gb->apu.noise_channel.dmg_delayed_start > cycles) {
gb->apu.channel_4_dmg_delayed_start -= cycles; gb->apu.noise_channel.dmg_delayed_start -= cycles;
} }
else { else {
/* Split it into two */ /* Split it into two */
cycles -= gb->apu.channel_4_dmg_delayed_start; cycles -= gb->apu.noise_channel.dmg_delayed_start;
gb->apu.apu_cycles = gb->apu.channel_4_dmg_delayed_start * 4; gb->apu.apu_cycles = gb->apu.noise_channel.dmg_delayed_start * 4;
GB_apu_run(gb); GB_apu_run(gb);
} }
} }
@ -669,6 +669,7 @@ void GB_apu_run(GB_gameboy_t *gb)
gb->apu.square_channels[i].sample_countdown = (gb->apu.square_channels[i].sample_length ^ 0x7FF) * 2 + 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++;
gb->apu.square_channels[i].current_sample_index &= 0x7; gb->apu.square_channels[i].current_sample_index &= 0x7;
gb->apu.square_channels[i].sample_surpressed = false;
if (cycles_left == 0 && gb->apu.samples[i] == 0) { if (cycles_left == 0 && gb->apu.samples[i] == 0) {
gb->apu.pcm_mask[0] &= i == GB_SQUARE_1? 0xF0 : 0x0F; gb->apu.pcm_mask[0] &= i == GB_SQUARE_1? 0xF0 : 0x0F;
} }
@ -699,7 +700,7 @@ void GB_apu_run(GB_gameboy_t *gb)
gb->apu.wave_channel.wave_form_just_read = false; gb->apu.wave_channel.wave_form_just_read = false;
} }
} }
else if (gb->apu.wave_channel.enable && gb->apu.channel_3_pulsed && gb->model < GB_MODEL_AGB) { else if (gb->apu.wave_channel.enable && gb->apu.wave_channel.pulsed && gb->model < GB_MODEL_AGB) {
uint8_t cycles_left = cycles; uint8_t cycles_left = cycles;
while (unlikely(cycles_left > gb->apu.wave_channel.sample_countdown)) { while (unlikely(cycles_left > gb->apu.wave_channel.sample_countdown)) {
cycles_left -= gb->apu.wave_channel.sample_countdown + 1; cycles_left -= gb->apu.wave_channel.sample_countdown + 1;
@ -709,7 +710,7 @@ void GB_apu_run(GB_gameboy_t *gb)
gb->io_registers[GB_IO_WAV_START + (gb->address_bus & 0xF)]; gb->io_registers[GB_IO_WAV_START + (gb->address_bus & 0xF)];
} }
else { else {
gb->apu.channel_3_delayed_bugged_read = true; gb->apu.wave_channel.delayed_bugged_read = true;
} }
} }
if (cycles_left) { if (cycles_left) {
@ -727,8 +728,8 @@ void GB_apu_run(GB_gameboy_t *gb)
} }
while (unlikely(cycles_left >= gb->apu.noise_channel.counter_countdown)) { while (unlikely(cycles_left >= gb->apu.noise_channel.counter_countdown)) {
cycles_left -= gb->apu.noise_channel.counter_countdown; cycles_left -= gb->apu.noise_channel.counter_countdown;
gb->apu.noise_channel.counter_countdown = divisor + gb->apu.channel_4_delta; gb->apu.noise_channel.counter_countdown = divisor + gb->apu.noise_channel.delta;
gb->apu.channel_4_delta = 0; gb->apu.noise_channel.delta = 0;
bool old_bit = (gb->apu.noise_channel.counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1; bool old_bit = (gb->apu.noise_channel.counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1;
gb->apu.noise_channel.counter++; gb->apu.noise_channel.counter++;
gb->apu.noise_channel.counter &= 0x3FFF; gb->apu.noise_channel.counter &= 0x3FFF;
@ -744,10 +745,10 @@ void GB_apu_run(GB_gameboy_t *gb)
} }
if (cycles_left) { if (cycles_left) {
gb->apu.noise_channel.counter_countdown -= cycles_left; gb->apu.noise_channel.counter_countdown -= cycles_left;
gb->apu.channel_4_countdown_reloaded = false; gb->apu.noise_channel.countdown_reloaded = false;
} }
else { else {
gb->apu.channel_4_countdown_reloaded = true; gb->apu.noise_channel.countdown_reloaded = true;
} }
} }
} }
@ -1037,7 +1038,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
else if (gb->apu.is_active[index]) { else if (gb->apu.is_active[index]) {
nrx2_glitch(gb, &gb->apu.square_channels[index].current_volume, nrx2_glitch(gb, &gb->apu.square_channels[index].current_volume,
value, gb->io_registers[reg], &gb->apu.square_channels[index].volume_countdown, value, gb->io_registers[reg], &gb->apu.square_channels[index].volume_countdown,
&gb->apu.square_envelope_clock[index]); &gb->apu.square_channels[index].envelope_clock);
update_square_sample(gb, index); update_square_sample(gb, index);
} }
@ -1066,6 +1067,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) { 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--;
gb->apu.square_channels[index].current_sample_index &= 7; gb->apu.square_channels[index].current_sample_index &= 7;
gb->apu.square_channels[index].sample_surpressed = false;
} }
} }
} }
@ -1076,8 +1078,8 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
if (value & 0x80) { if (value & 0x80) {
/* Current sample index remains unchanged when restarting channels 1 or 2. It is only reset by /* Current sample index remains unchanged when restarting channels 1 or 2. It is only reset by
turning the APU off. */ turning the APU off. */
gb->apu.square_envelope_clock[index].locked = false; gb->apu.square_channels[index].envelope_clock.locked = false;
gb->apu.square_envelope_clock[index].clock = false; gb->apu.square_channels[index].envelope_clock.clock = false;
if (!gb->apu.is_active[index]) { 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; gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 6 - gb->apu.lf_div;
if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) { if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) {
@ -1090,11 +1092,12 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
if (!(value & 4) && !(((gb->apu.square_channels[index].sample_countdown - 1) / 2) & 0x400)) { if (!(value & 4) && !(((gb->apu.square_channels[index].sample_countdown - 1) / 2) & 0x400)) {
gb->apu.square_channels[index].current_sample_index++; gb->apu.square_channels[index].current_sample_index++;
gb->apu.square_channels[index].current_sample_index &= 0x7; gb->apu.square_channels[index].current_sample_index &= 0x7;
gb->apu.square_channels[index].sample_surpressed = false;
} }
/* Todo: verify with the schematics what's going on in here */ /* Todo: verify with the schematics what's going on in here */
else if (gb->apu.square_channels[index].sample_length == 0x7FF && else if (gb->apu.square_channels[index].sample_length == 0x7FF &&
old_sample_length != 0x7FF && old_sample_length != 0x7FF &&
(gb->apu.square_channels[index].current_sample_index & 0x80)) { (gb->apu.square_channels[index].sample_surpressed)) {
extra_delay += 2; extra_delay += 2;
} }
} }
@ -1117,8 +1120,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
if ((gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0xF8) != 0 && !gb->apu.is_active[index]) { 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; gb->apu.is_active[index] = true;
update_sample(gb, index, 0, 0); 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].sample_surpressed = true;
gb->apu.square_channels[index].current_sample_index |= 0x80;
} }
if (gb->apu.square_channels[index].pulse_length == 0) { if (gb->apu.square_channels[index].pulse_length == 0) {
gb->apu.square_channels[index].pulse_length = 0x40; gb->apu.square_channels[index].pulse_length = 0x40;
@ -1182,7 +1184,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
case GB_IO_NR30: case GB_IO_NR30:
gb->apu.wave_channel.enable = value & 0x80; gb->apu.wave_channel.enable = value & 0x80;
if (!gb->apu.wave_channel.enable) { if (!gb->apu.wave_channel.enable) {
gb->apu.channel_3_pulsed = false; gb->apu.wave_channel.pulsed = false;
if (gb->apu.is_active[GB_WAVE]) { if (gb->apu.is_active[GB_WAVE]) {
// Todo: I assume this happens on pre-CGB models; test this with an audible test // Todo: I assume this happens on pre-CGB models; test this with an audible test
if (gb->apu.wave_channel.sample_countdown == 0 && gb->model < GB_MODEL_AGB) { if (gb->apu.wave_channel.sample_countdown == 0 && gb->model < GB_MODEL_AGB) {
@ -1213,7 +1215,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
gb->apu.wave_channel.sample_length &= 0xFF; gb->apu.wave_channel.sample_length &= 0xFF;
gb->apu.wave_channel.sample_length |= (value & 7) << 8; gb->apu.wave_channel.sample_length |= (value & 7) << 8;
if (value & 0x80) { if (value & 0x80) {
gb->apu.channel_3_pulsed = true; gb->apu.wave_channel.pulsed = true;
/* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU /* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU
reads from it. */ reads from it. */
if (!GB_is_cgb(gb) && if (!GB_is_cgb(gb) &&
@ -1295,9 +1297,9 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
else if (gb->apu.is_active[GB_NOISE]) { else if (gb->apu.is_active[GB_NOISE]) {
nrx2_glitch(gb, &gb->apu.noise_channel.current_volume, nrx2_glitch(gb, &gb->apu.noise_channel.current_volume,
value, gb->io_registers[reg], &gb->apu.noise_channel.volume_countdown, value, gb->io_registers[reg], &gb->apu.noise_channel.volume_countdown,
&gb->apu.noise_envelope_clock); &gb->apu.noise_channel.envelope_clock);
update_sample(gb, GB_NOISE, update_sample(gb, GB_NOISE,
gb->apu.current_lfsr_sample ? gb->apu.noise_channel.current_lfsr_sample ?
gb->apu.noise_channel.current_volume : 0, gb->apu.noise_channel.current_volume : 0,
0); 0);
} }
@ -1310,7 +1312,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
bool old_bit = (effective_counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1; bool old_bit = (effective_counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1;
gb->io_registers[GB_IO_NR43] = value; gb->io_registers[GB_IO_NR43] = value;
bool new_bit = (effective_counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1; bool new_bit = (effective_counter >> (gb->io_registers[GB_IO_NR43] >> 4)) & 1;
if (gb->apu.channel_4_countdown_reloaded) { if (gb->apu.noise_channel.countdown_reloaded) {
unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2; unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2;
if (!divisor) divisor = 2; if (!divisor) divisor = 2;
if (gb->model > GB_MODEL_CGB_C) { if (gb->model > GB_MODEL_CGB_C) {
@ -1321,7 +1323,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
gb->apu.noise_channel.counter_countdown = gb->apu.noise_channel.counter_countdown =
divisor + (divisor == 2? 0 : (uint8_t[]){2, 1, 4, 3}[(gb->apu.noise_channel.alignment) & 3]); divisor + (divisor == 2? 0 : (uint8_t[]){2, 1, 4, 3}[(gb->apu.noise_channel.alignment) & 3]);
} }
gb->apu.channel_4_delta = 0; gb->apu.noise_channel.delta = 0;
} }
/* Step LFSR */ /* Step LFSR */
if (new_bit && (!old_bit || gb->model <= GB_MODEL_CGB_C)) { if (new_bit && (!old_bit || gb->model <= GB_MODEL_CGB_C)) {
@ -1340,15 +1342,15 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
case GB_IO_NR44: { case GB_IO_NR44: {
if (value & 0x80) { if (value & 0x80) {
gb->apu.noise_envelope_clock.locked = false; gb->apu.noise_channel.envelope_clock.locked = false;
gb->apu.noise_envelope_clock.clock = false; gb->apu.noise_channel.envelope_clock.clock = false;
if (!GB_is_cgb(gb) && (gb->apu.noise_channel.alignment & 3) != 0) { if (!GB_is_cgb(gb) && (gb->apu.noise_channel.alignment & 3) != 0) {
gb->apu.channel_4_dmg_delayed_start = 6; gb->apu.noise_channel.dmg_delayed_start = 6;
} }
else { else {
unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2; unsigned divisor = (gb->io_registers[GB_IO_NR43] & 0x07) << 2;
if (!divisor) divisor = 2; if (!divisor) divisor = 2;
gb->apu.channel_4_delta = 0; gb->apu.noise_channel.delta = 0;
gb->apu.noise_channel.counter_countdown = divisor + 4; gb->apu.noise_channel.counter_countdown = divisor + 4;
if (divisor == 2) { if (divisor == 2) {
if (gb->model <= GB_MODEL_CGB_C) { if (gb->model <= GB_MODEL_CGB_C) {
@ -1371,7 +1373,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
if (((gb->apu.noise_channel.alignment + 1) & 3) < 2) { if (((gb->apu.noise_channel.alignment + 1) & 3) < 2) {
if ((gb->io_registers[GB_IO_NR43] & 0x07) == 1) { if ((gb->io_registers[GB_IO_NR43] & 0x07) == 1) {
gb->apu.noise_channel.counter_countdown -= 2; gb->apu.noise_channel.counter_countdown -= 2;
gb->apu.channel_4_delta = 2; gb->apu.noise_channel.delta = 2;
} }
else { else {
gb->apu.noise_channel.counter_countdown -= 4; gb->apu.noise_channel.counter_countdown -= 4;
@ -1401,12 +1403,12 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
cases. */ cases. */
if (gb->apu.is_active[GB_NOISE]) { if (gb->apu.is_active[GB_NOISE]) {
update_sample(gb, GB_NOISE, update_sample(gb, GB_NOISE,
gb->apu.current_lfsr_sample ? gb->apu.noise_channel.current_lfsr_sample ?
gb->apu.noise_channel.current_volume : 0, gb->apu.noise_channel.current_volume : 0,
0); 0);
} }
gb->apu.noise_channel.lfsr = 0; gb->apu.noise_channel.lfsr = 0;
gb->apu.current_lfsr_sample = false; gb->apu.noise_channel.current_lfsr_sample = false;
gb->apu.noise_channel.volume_countdown = gb->io_registers[GB_IO_NR42] & 7; 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) { if (!gb->apu.is_active[GB_NOISE] && (gb->io_registers[GB_IO_NR42] & 0xF8) != 0) {

View File

@ -76,18 +76,19 @@ typedef struct
bool unshifted_sweep; bool unshifted_sweep;
bool enable_zombie_calculate_stepping; bool enable_zombie_calculate_stepping;
uint8_t channel_1_restart_hold;
uint16_t channel1_completed_addend;
struct { struct {
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
uint8_t current_volume; // Reloaded from NRX2 uint8_t current_volume; // Reloaded from NRX2
uint8_t volume_countdown; // Reloaded from NRX2 uint8_t volume_countdown; // Reloaded from NRX2
uint8_t current_sample_index; /* For save state compatibility, uint8_t current_sample_index;
highest bit is reused (See NR14/NR24's bool sample_surpressed;
write code)*/
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF) uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint16_t sample_length; // From NRX3, NRX4, in APU ticks uint16_t sample_length; // From NRX3, NRX4, in APU ticks
bool length_enabled; // NRX4 bool length_enabled; // NRX4
GB_envelope_clock_t envelope_clock;
} square_channels[2]; } square_channels[2];
struct { struct {
@ -100,9 +101,9 @@ typedef struct
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF) uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
uint8_t current_sample_index; uint8_t current_sample_index;
uint8_t current_sample_byte; // Current sample byte. uint8_t current_sample_byte; // Current sample byte.
GB_PADDING(int8_t, wave_form)[32];
bool wave_form_just_read; bool wave_form_just_read;
bool pulsed;
bool delayed_bugged_read;
} wave_channel; } wave_channel;
struct { struct {
@ -113,32 +114,24 @@ typedef struct
bool narrow; bool narrow;
uint8_t counter_countdown; // Counts from 0-7 to 0 to tick counter (Scaled from 512KHz to 2MHz) uint8_t counter_countdown; // Counts from 0-7 to 0 to tick counter (Scaled from 512KHz to 2MHz)
uint8_t __padding;
uint16_t counter; // A bit from this 14-bit register ticks LFSR uint16_t counter; // A bit from this 14-bit register ticks LFSR
bool length_enabled; // NR44 bool length_enabled; // NR44
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
// 1MHz. This variable keeps track of the alignment. // 1MHz. This variable keeps track of the alignment.
bool current_lfsr_sample;
int8_t delta;
bool countdown_reloaded;
uint8_t dmg_delayed_start;
GB_envelope_clock_t envelope_clock;
} noise_channel; } noise_channel;
/* Todo: merge these into their structs when breaking save state compatibility */ enum {
#define GB_SKIP_DIV_EVENT_INACTIVE 0 GB_SKIP_DIV_EVENT_INACTIVE,
#define GB_SKIP_DIV_EVENT_SKIPPED 1 GB_SKIP_DIV_EVENT_SKIPPED,
#define GB_SKIP_DIV_EVENT_SKIP 2 GB_SKIP_DIV_EVENT_SKIP,
uint8_t skip_div_event; } skip_div_event:8;
bool current_lfsr_sample;
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
uint8_t channel_1_restart_hold;
int8_t channel_4_delta;
bool channel_4_countdown_reloaded;
uint8_t channel_4_dmg_delayed_start;
uint16_t channel1_completed_addend;
GB_envelope_clock_t square_envelope_clock[2];
GB_envelope_clock_t noise_envelope_clock;
bool channel_3_pulsed;
bool channel_3_delayed_bugged_read;
} GB_apu_t; } GB_apu_t;
typedef enum { typedef enum {

View File

@ -1771,8 +1771,8 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
GB_log(gb, " Duty cycle %s%% (%s), current index %u/8%s\n", 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 *[]){"12.5", " 25", " 50", " 75"}[duty],
duty > 3? "" : (const char *[]){"_______-", "-______-", "-____---", "_------_"}[duty], duty > 3? "" : (const char *[]){"_______-", "-______-", "-____---", "_------_"}[duty],
gb->apu.square_channels[channel].current_sample_index & 0x7f, gb->apu.square_channels[channel].current_sample_index,
gb->apu.square_channels[channel].current_sample_index >> 7 ? " (suppressed)" : ""); gb->apu.square_channels[channel].sample_surpressed ? " (suppressed)" : "");
if (channel == GB_SQUARE_1) { if (channel == GB_SQUARE_1) {
GB_log(gb, " Frequency sweep %s and %s\n", GB_log(gb, " Frequency sweep %s and %s\n",
@ -2541,7 +2541,7 @@ static bool is_in_trivial_memory(uint16_t addr)
return false; return false;
} }
typedef uint16_t GB_opcode_address_getter_t(GB_gameboy_t *gb, uint8_t opcode); typedef uint16_t opcode_address_getter_t(GB_gameboy_t *gb, uint8_t opcode);
uint16_t trivial_1(GB_gameboy_t *gb, uint8_t opcode) uint16_t trivial_1(GB_gameboy_t *gb, uint8_t opcode)
{ {
@ -2631,7 +2631,7 @@ static uint16_t jp_hl(GB_gameboy_t *gb, uint8_t opcode)
return gb->hl; return gb->hl;
} }
static GB_opcode_address_getter_t *opcodes[256] = { static opcode_address_getter_t *opcodes[256] = {
/* X0 X1 X2 X3 X4 X5 X6 X7 */ /* X0 X1 X2 X3 X4 X5 X6 X7 */
/* X8 X9 Xa Xb Xc Xd Xe Xf */ /* 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_1, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 0X */
@ -2709,7 +2709,7 @@ static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *add
return JUMP_TO_NONE; return JUMP_TO_NONE;
} }
GB_opcode_address_getter_t *getter = opcodes[opcode]; opcode_address_getter_t *getter = opcodes[opcode];
if (!getter) { if (!getter) {
gb->n_watchpoints = n_watchpoints; gb->n_watchpoints = n_watchpoints;
return JUMP_TO_NONE; return JUMP_TO_NONE;

View File

@ -107,7 +107,7 @@ typedef struct __attribute__((packed)) {
uint8_t x; uint8_t x;
uint8_t tile; uint8_t tile;
uint8_t flags; uint8_t flags;
} GB_object_t; } object_t;
static void display_vblank(GB_gameboy_t *gb) static void display_vblank(GB_gameboy_t *gb)
{ {
@ -470,7 +470,7 @@ static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
} }
/* This reverse sorts the visible objects by location and priority */ /* This reverse sorts the visible objects by location and priority */
GB_object_t *objects = (GB_object_t *) &gb->oam; object_t *objects = (object_t *) &gb->oam;
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0;
signed y = objects[index].y - 16; signed y = objects[index].y - 16;
if (y <= gb->current_line && y + (height_16? 16 : 8) > gb->current_line) { if (y <= gb->current_line && y + (height_16? 16 : 8) > gb->current_line) {
@ -825,11 +825,11 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
} }
} }
static uint16_t get_object_line_address(GB_gameboy_t *gb, const GB_object_t *object) static uint16_t get_object_line_address(GB_gameboy_t *gb, const object_t *object)
{ {
/* TODO: what does the PPU read if DMA is active? */ /* TODO: what does the PPU read if DMA is active? */
if (gb->oam_ppu_blocked) { if (gb->oam_ppu_blocked) {
static const GB_object_t blocked = {0xFF, 0xFF, 0xFF, 0xFF}; static const object_t blocked = {0xFF, 0xFF, 0xFF, 0xFF};
object = &blocked; object = &blocked;
} }
@ -864,7 +864,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
} }
return; return;
} }
GB_object_t *objects = (GB_object_t *) &gb->oam; object_t *objects = (object_t *) &gb->oam;
GB_STATE_MACHINE(gb, display, cycles, 2) { GB_STATE_MACHINE(gb, display, cycles, 2) {
GB_STATE(gb, display, 1); GB_STATE(gb, display, 1);
@ -1208,7 +1208,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
gb->cycles_for_line++; gb->cycles_for_line++;
GB_SLEEP(gb, display, 40, 1); GB_SLEEP(gb, display, 40, 1);
const GB_object_t *object = &objects[gb->visible_objs[gb->n_visible_objs - 1]]; const object_t *object = &objects[gb->visible_objs[gb->n_visible_objs - 1]];
uint16_t line_address = get_object_line_address(gb, object); uint16_t line_address = get_object_line_address(gb, object);
@ -1553,7 +1553,7 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_h
*sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8; *sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8;
uint8_t oam_to_dest_index[40] = {0,}; uint8_t oam_to_dest_index[40] = {0,};
for (signed y = 0; y < LINES; y++) { for (signed y = 0; y < LINES; y++) {
GB_object_t *sprite = (GB_object_t *) &gb->oam; object_t *sprite = (object_t *) &gb->oam;
uint8_t sprites_in_line = 0; uint8_t sprites_in_line = 0;
for (uint8_t i = 0; i < 40; i++, sprite++) { for (uint8_t i = 0; i < 40; i++, sprite++) {
signed sprite_y = sprite->y - 16; signed sprite_y = sprite->y - 16;

View File

@ -12,7 +12,6 @@ void GB_STAT_update(GB_gameboy_t *gb);
void GB_lcd_off(GB_gameboy_t *gb); void GB_lcd_off(GB_gameboy_t *gb);
enum { enum {
GB_OBJECT_PRIORITY_UNDEFINED, // For save state compatibility
GB_OBJECT_PRIORITY_X, GB_OBJECT_PRIORITY_X,
GB_OBJECT_PRIORITY_INDEX, GB_OBJECT_PRIORITY_INDEX,
}; };

145
Core/gb.c
View File

@ -753,7 +753,7 @@ typedef struct {
uint8_t padding4[3]; uint8_t padding4[3];
uint8_t high; uint8_t high;
uint8_t padding5[3]; uint8_t padding5[3];
} GB_vba_rtc_time_t; } vba_rtc_time_t;
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
uint32_t magic; uint32_t magic;
@ -762,7 +762,7 @@ typedef struct __attribute__((packed)) {
uint8_t reserved; uint8_t reserved;
uint64_t last_rtc_second; uint64_t last_rtc_second;
uint8_t rtc_data[4]; uint8_t rtc_data[4];
} GB_tpp1_rtc_save_t; } tpp1_rtc_save_t;
typedef union { typedef union {
struct __attribute__((packed)) { struct __attribute__((packed)) {
@ -771,17 +771,17 @@ typedef union {
} sameboy_legacy; } sameboy_legacy;
struct { struct {
/* Used by VBA versions with 32-bit timestamp*/ /* Used by VBA versions with 32-bit timestamp*/
GB_vba_rtc_time_t rtc_real, rtc_latched; vba_rtc_time_t rtc_real, rtc_latched;
uint32_t last_rtc_second; /* Always little endian */ uint32_t last_rtc_second; /* Always little endian */
} vba32; } vba32;
struct { struct {
/* Used by BGB and VBA versions with 64-bit timestamp*/ /* Used by BGB and VBA versions with 64-bit timestamp*/
GB_vba_rtc_time_t rtc_real, rtc_latched; vba_rtc_time_t rtc_real, rtc_latched;
uint64_t last_rtc_second; /* Always little endian */ uint64_t last_rtc_second; /* Always little endian */
} vba64; } vba64;
} GB_rtc_save_t; } rtc_save_t;
static void GB_fill_tpp1_save_data(GB_gameboy_t *gb, GB_tpp1_rtc_save_t *data) static void fill_tpp1_save_data(GB_gameboy_t *gb, tpp1_rtc_save_t *data)
{ {
data->magic = BE32('TPP1'); data->magic = BE32('TPP1');
data->version = BE16(0x100); data->version = BE16(0x100);
@ -805,10 +805,10 @@ int GB_save_battery_size(GB_gameboy_t *gb)
} }
if (gb->cartridge_type->mbc_type == GB_TPP1) { if (gb->cartridge_type->mbc_type == GB_TPP1) {
return gb->mbc_ram_size + sizeof(GB_tpp1_rtc_save_t); return gb->mbc_ram_size + sizeof(tpp1_rtc_save_t);
} }
GB_rtc_save_t rtc_save_size; rtc_save_t rtc_save_size;
return gb->mbc_ram_size + (gb->cartridge_type->has_rtc ? sizeof(rtc_save_size.vba64) : 0); return gb->mbc_ram_size + (gb->cartridge_type->has_rtc ? sizeof(rtc_save_size.vba64) : 0);
} }
@ -824,8 +824,8 @@ int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size)
if (gb->cartridge_type->mbc_type == GB_TPP1) { if (gb->cartridge_type->mbc_type == GB_TPP1) {
buffer += gb->mbc_ram_size; buffer += gb->mbc_ram_size;
GB_tpp1_rtc_save_t rtc_save; tpp1_rtc_save_t rtc_save;
GB_fill_tpp1_save_data(gb, &rtc_save); fill_tpp1_save_data(gb, &rtc_save);
memcpy(buffer, &rtc_save, sizeof(rtc_save)); memcpy(buffer, &rtc_save, sizeof(rtc_save));
} }
else if (gb->cartridge_type->mbc_type == GB_HUC3) { else if (gb->cartridge_type->mbc_type == GB_HUC3) {
@ -834,26 +834,26 @@ int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size)
#ifdef GB_BIG_ENDIAN #ifdef GB_BIG_ENDIAN
GB_huc3_rtc_time_t rtc_save = { GB_huc3_rtc_time_t rtc_save = {
__builtin_bswap64(gb->last_rtc_second), __builtin_bswap64(gb->last_rtc_second),
__builtin_bswap16(gb->huc3_minutes), __builtin_bswap16(gb->huc3.minutes),
__builtin_bswap16(gb->huc3_days), __builtin_bswap16(gb->huc3.days),
__builtin_bswap16(gb->huc3_alarm_minutes), __builtin_bswap16(gb->huc3.alarm_minutes),
__builtin_bswap16(gb->huc3_alarm_days), __builtin_bswap16(gb->huc3.alarm_days),
gb->huc3_alarm_enabled, gb->huc3.alarm_enabled,
}; };
#else #else
GB_huc3_rtc_time_t rtc_save = { GB_huc3_rtc_time_t rtc_save = {
gb->last_rtc_second, gb->last_rtc_second,
gb->huc3_minutes, gb->huc3.minutes,
gb->huc3_days, gb->huc3.days,
gb->huc3_alarm_minutes, gb->huc3.alarm_minutes,
gb->huc3_alarm_days, gb->huc3.alarm_days,
gb->huc3_alarm_enabled, gb->huc3.alarm_enabled,
}; };
#endif #endif
memcpy(buffer, &rtc_save, sizeof(rtc_save)); memcpy(buffer, &rtc_save, sizeof(rtc_save));
} }
else if (gb->cartridge_type->has_rtc) { else if (gb->cartridge_type->has_rtc) {
GB_rtc_save_t rtc_save = {{{{0,}},},}; rtc_save_t rtc_save = {{{{0,}},},};
rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds; 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.minutes = gb->rtc_real.minutes;
rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours; rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours;
@ -892,8 +892,8 @@ int GB_save_battery(GB_gameboy_t *gb, const char *path)
return EIO; return EIO;
} }
if (gb->cartridge_type->mbc_type == GB_TPP1) { if (gb->cartridge_type->mbc_type == GB_TPP1) {
GB_tpp1_rtc_save_t rtc_save; tpp1_rtc_save_t rtc_save;
GB_fill_tpp1_save_data(gb, &rtc_save); fill_tpp1_save_data(gb, &rtc_save);
if (fwrite(&rtc_save, sizeof(rtc_save), 1, f) != 1) { if (fwrite(&rtc_save, sizeof(rtc_save), 1, f) != 1) {
fclose(f); fclose(f);
@ -904,20 +904,20 @@ int GB_save_battery(GB_gameboy_t *gb, const char *path)
#ifdef GB_BIG_ENDIAN #ifdef GB_BIG_ENDIAN
GB_huc3_rtc_time_t rtc_save = { GB_huc3_rtc_time_t rtc_save = {
__builtin_bswap64(gb->last_rtc_second), __builtin_bswap64(gb->last_rtc_second),
__builtin_bswap16(gb->huc3_minutes), __builtin_bswap16(gb->huc3.minutes),
__builtin_bswap16(gb->huc3_days), __builtin_bswap16(gb->huc3.days),
__builtin_bswap16(gb->huc3_alarm_minutes), __builtin_bswap16(gb->huc3.alarm_minutes),
__builtin_bswap16(gb->huc3_alarm_days), __builtin_bswap16(gb->huc3.alarm_days),
gb->huc3_alarm_enabled, gb->huc3.alarm_enabled,
}; };
#else #else
GB_huc3_rtc_time_t rtc_save = { GB_huc3_rtc_time_t rtc_save = {
gb->last_rtc_second, gb->last_rtc_second,
gb->huc3_minutes, gb->huc3.minutes,
gb->huc3_days, gb->huc3.days,
gb->huc3_alarm_minutes, gb->huc3.alarm_minutes,
gb->huc3_alarm_days, gb->huc3.alarm_days,
gb->huc3_alarm_enabled, gb->huc3.alarm_enabled,
}; };
#endif #endif
@ -927,7 +927,7 @@ int GB_save_battery(GB_gameboy_t *gb, const char *path)
} }
} }
else if (gb->cartridge_type->has_rtc) { else if (gb->cartridge_type->has_rtc) {
GB_rtc_save_t rtc_save = {{{{0,}},},}; rtc_save_t rtc_save = {{{{0,}},},};
rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds; 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.minutes = gb->rtc_real.minutes;
rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours; rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours;
@ -955,7 +955,7 @@ int GB_save_battery(GB_gameboy_t *gb, const char *path)
return errno; return errno;
} }
static void GB_load_tpp1_save_data(GB_gameboy_t *gb, const GB_tpp1_rtc_save_t *data) static void load_tpp1_save_data(GB_gameboy_t *gb, const tpp1_rtc_save_t *data)
{ {
gb->last_rtc_second = LE64(data->last_rtc_second); gb->last_rtc_second = LE64(data->last_rtc_second);
unrolled for (unsigned i = 4; i--;) { unrolled for (unsigned i = 4; i--;) {
@ -971,13 +971,13 @@ void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t
} }
if (gb->cartridge_type->mbc_type == GB_TPP1) { if (gb->cartridge_type->mbc_type == GB_TPP1) {
GB_tpp1_rtc_save_t rtc_save; tpp1_rtc_save_t rtc_save;
if (size - gb->mbc_ram_size < sizeof(rtc_save)) { if (size - gb->mbc_ram_size < sizeof(rtc_save)) {
goto reset_rtc; goto reset_rtc;
} }
memcpy(&rtc_save, buffer + gb->mbc_ram_size, sizeof(rtc_save)); memcpy(&rtc_save, buffer + gb->mbc_ram_size, sizeof(rtc_save));
GB_load_tpp1_save_data(gb, &rtc_save); load_tpp1_save_data(gb, &rtc_save);
if (gb->last_rtc_second > time(NULL)) { if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */ /* We must reset RTC here, or it will not advance. */
@ -994,18 +994,18 @@ void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t
memcpy(&rtc_save, buffer + gb->mbc_ram_size, sizeof(rtc_save)); memcpy(&rtc_save, buffer + gb->mbc_ram_size, sizeof(rtc_save));
#ifdef GB_BIG_ENDIAN #ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second); gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes); gb->huc3.minutes = __builtin_bswap16(rtc_save.minutes);
gb->huc3_days = __builtin_bswap16(rtc_save.days); gb->huc3.days = __builtin_bswap16(rtc_save.days);
gb->huc3_alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes); gb->huc3.alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes);
gb->huc3_alarm_days = __builtin_bswap16(rtc_save.alarm_days); gb->huc3.alarm_days = __builtin_bswap16(rtc_save.alarm_days);
gb->huc3_alarm_enabled = rtc_save.alarm_enabled; gb->huc3.alarm_enabled = rtc_save.alarm_enabled;
#else #else
gb->last_rtc_second = rtc_save.last_rtc_second; gb->last_rtc_second = rtc_save.last_rtc_second;
gb->huc3_minutes = rtc_save.minutes; gb->huc3.minutes = rtc_save.minutes;
gb->huc3_days = rtc_save.days; gb->huc3.days = rtc_save.days;
gb->huc3_alarm_minutes = rtc_save.alarm_minutes; gb->huc3.alarm_minutes = rtc_save.alarm_minutes;
gb->huc3_alarm_days = rtc_save.alarm_days; gb->huc3.alarm_days = rtc_save.alarm_days;
gb->huc3_alarm_enabled = rtc_save.alarm_enabled; gb->huc3.alarm_enabled = rtc_save.alarm_enabled;
#endif #endif
if (gb->last_rtc_second > time(NULL)) { if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */ /* We must reset RTC here, or it will not advance. */
@ -1014,7 +1014,7 @@ void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t
return; return;
} }
GB_rtc_save_t rtc_save; rtc_save_t rtc_save;
memcpy(&rtc_save, buffer + gb->mbc_ram_size, MIN(sizeof(rtc_save), size)); memcpy(&rtc_save, buffer + gb->mbc_ram_size, MIN(sizeof(rtc_save), size));
switch (size - gb->mbc_ram_size) { switch (size - gb->mbc_ram_size) {
case sizeof(rtc_save.sameboy_legacy): case sizeof(rtc_save.sameboy_legacy):
@ -1076,9 +1076,9 @@ void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t
reset_rtc: reset_rtc:
gb->last_rtc_second = time(NULL); gb->last_rtc_second = time(NULL);
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
gb->huc3_days = 0xFFFF; gb->huc3.days = 0xFFFF;
gb->huc3_minutes = 0xFFF; gb->huc3.minutes = 0xFFF;
gb->huc3_alarm_enabled = false; gb->huc3.alarm_enabled = false;
exit: exit:
return; return;
} }
@ -1096,12 +1096,12 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
} }
if (gb->cartridge_type->mbc_type == GB_TPP1) { if (gb->cartridge_type->mbc_type == GB_TPP1) {
GB_tpp1_rtc_save_t rtc_save; tpp1_rtc_save_t rtc_save;
if (fread(&rtc_save, sizeof(rtc_save), 1, f) != 1) { if (fread(&rtc_save, sizeof(rtc_save), 1, f) != 1) {
goto reset_rtc; goto reset_rtc;
} }
GB_load_tpp1_save_data(gb, &rtc_save); load_tpp1_save_data(gb, &rtc_save);
if (gb->last_rtc_second > time(NULL)) { if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */ /* We must reset RTC here, or it will not advance. */
@ -1117,18 +1117,18 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
} }
#ifdef GB_BIG_ENDIAN #ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second); gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes); gb->huc3.minutes = __builtin_bswap16(rtc_save.minutes);
gb->huc3_days = __builtin_bswap16(rtc_save.days); gb->huc3.days = __builtin_bswap16(rtc_save.days);
gb->huc3_alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes); gb->huc3.alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes);
gb->huc3_alarm_days = __builtin_bswap16(rtc_save.alarm_days); gb->huc3.alarm_days = __builtin_bswap16(rtc_save.alarm_days);
gb->huc3_alarm_enabled = rtc_save.alarm_enabled; gb->huc3.alarm_enabled = rtc_save.alarm_enabled;
#else #else
gb->last_rtc_second = rtc_save.last_rtc_second; gb->last_rtc_second = rtc_save.last_rtc_second;
gb->huc3_minutes = rtc_save.minutes; gb->huc3.minutes = rtc_save.minutes;
gb->huc3_days = rtc_save.days; gb->huc3.days = rtc_save.days;
gb->huc3_alarm_minutes = rtc_save.alarm_minutes; gb->huc3.alarm_minutes = rtc_save.alarm_minutes;
gb->huc3_alarm_days = rtc_save.alarm_days; gb->huc3.alarm_days = rtc_save.alarm_days;
gb->huc3_alarm_enabled = rtc_save.alarm_enabled; gb->huc3.alarm_enabled = rtc_save.alarm_enabled;
#endif #endif
if (gb->last_rtc_second > time(NULL)) { if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */ /* We must reset RTC here, or it will not advance. */
@ -1137,7 +1137,7 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
return; return;
} }
GB_rtc_save_t rtc_save; rtc_save_t rtc_save;
switch (fread(&rtc_save, 1, sizeof(rtc_save), f)) { switch (fread(&rtc_save, 1, sizeof(rtc_save), f)) {
case sizeof(rtc_save.sameboy_legacy): case sizeof(rtc_save.sameboy_legacy):
memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real)); memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
@ -1198,9 +1198,9 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
reset_rtc: reset_rtc:
gb->last_rtc_second = time(NULL); gb->last_rtc_second = time(NULL);
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
gb->huc3_days = 0xFFFF; gb->huc3.days = 0xFFFF;
gb->huc3_minutes = 0xFFF; gb->huc3.minutes = 0xFFF;
gb->huc3_alarm_enabled = false; gb->huc3.alarm_enabled = false;
exit: exit:
fclose(f); fclose(f);
return; return;
@ -1711,8 +1711,7 @@ void GB_reset(GB_gameboy_t *gb)
} }
} }
/* Todo: Ugly, fixme, see comment in the timer state machine */ GB_set_internal_div_counter(gb, 8);
gb->div_state = 3;
GB_apu_update_cycles_per_sample(gb); GB_apu_update_cycles_per_sample(gb);
@ -1910,10 +1909,10 @@ void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t
unsigned GB_time_to_alarm(GB_gameboy_t *gb) unsigned GB_time_to_alarm(GB_gameboy_t *gb)
{ {
if (gb->cartridge_type->mbc_type != GB_HUC3) return 0; if (gb->cartridge_type->mbc_type != GB_HUC3) return 0;
if (!gb->huc3_alarm_enabled) return 0; if (!gb->huc3.alarm_enabled) return 0;
if (!(gb->huc3_alarm_days & 0x2000)) return 0; if (!(gb->huc3.alarm_days & 0x2000)) return 0;
unsigned current_time = (gb->huc3_days & 0x1FFF) * 24 * 60 * 60 + gb->huc3_minutes * 60 + (time(NULL) % 60); unsigned current_time = (gb->huc3.days & 0x1FFF) * 24 * 60 * 60 + gb->huc3.minutes * 60 + (time(NULL) % 60);
unsigned alarm_time = (gb->huc3_alarm_days & 0x1FFF) * 24 * 60 * 60 + gb->huc3_alarm_minutes * 60; unsigned alarm_time = (gb->huc3.alarm_days & 0x1FFF) * 24 * 60 * 60 + gb->huc3.alarm_minutes * 60;
if (current_time > alarm_time) return 0; if (current_time > alarm_time) return 0;
return alarm_time - current_time; return alarm_time - current_time;
} }

View File

@ -26,7 +26,7 @@
#include "workboy.h" #include "workboy.h"
#include "random.h" #include "random.h"
#define GB_STRUCT_VERSION 13 #define GB_STRUCT_VERSION 14
#define GB_MODEL_FAMILY_MASK 0xF00 #define GB_MODEL_FAMILY_MASK 0xF00
#define GB_MODEL_DMG_FAMILY 0x000 #define GB_MODEL_DMG_FAMILY 0x000
@ -35,9 +35,6 @@
#define GB_MODEL_PAL_BIT 0x40 #define GB_MODEL_PAL_BIT 0x40
#define GB_MODEL_NO_SFC_BIT 0x80 #define GB_MODEL_NO_SFC_BIT 0x80
#define GB_MODEL_PAL_BIT_OLD 0x1000
#define GB_MODEL_NO_SFC_BIT_OLD 0x2000
#ifdef GB_INTERNAL #ifdef GB_INTERNAL
#if __clang__ #if __clang__
#define unrolled _Pragma("unroll") #define unrolled _Pragma("unroll")
@ -471,6 +468,7 @@ struct GB_gameboy_internal_s {
struct { struct {
uint8_t rom_bank:8; uint8_t rom_bank:8;
uint8_t ram_bank:3; uint8_t ram_bank:3;
bool rtc_mapped:1;
} mbc3; } mbc3;
struct { struct {
@ -490,7 +488,20 @@ struct GB_gameboy_internal_s {
uint8_t rom_bank:7; uint8_t rom_bank:7;
uint8_t padding:1; uint8_t padding:1;
uint8_t ram_bank:4; uint8_t ram_bank:4;
uint8_t mode;
uint8_t access_index;
uint16_t minutes, days;
uint16_t alarm_minutes, alarm_days;
bool alarm_enabled;
uint8_t read;
uint8_t access_flags;
} huc3; } huc3;
struct {
uint16_t rom_bank;
uint8_t ram_bank;
uint8_t mode;
} tpp1;
}; };
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */ uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
bool camera_registers_mapped; bool camera_registers_mapped;
@ -498,18 +509,6 @@ struct GB_gameboy_internal_s {
uint8_t rumble_strength; uint8_t rumble_strength;
bool cart_ir; bool cart_ir;
// TODO: move to huc3/mbc3/tpp1 struct when breaking save compat
uint8_t huc3_mode;
uint8_t huc3_access_index;
uint16_t huc3_minutes, huc3_days;
uint16_t huc3_alarm_minutes, huc3_alarm_days;
bool huc3_alarm_enabled;
uint8_t huc3_read;
uint8_t huc3_access_flags;
bool mbc3_rtc_mapped;
uint16_t tpp1_rom_bank;
uint8_t tpp1_ram_bank;
uint8_t tpp1_mode;
); );
@ -543,7 +542,6 @@ struct GB_gameboy_internal_s {
GB_SECTION(rtc, GB_SECTION(rtc,
GB_rtc_time_t rtc_real, rtc_latched; GB_rtc_time_t rtc_real, rtc_latched;
uint64_t last_rtc_second; uint64_t last_rtc_second;
GB_PADDING(bool, rtc_latch);
uint32_t rtc_cycles; uint32_t rtc_cycles;
uint8_t tpp1_mr4; uint8_t tpp1_mr4;
); );
@ -579,7 +577,6 @@ struct GB_gameboy_internal_s {
uint8_t current_line; uint8_t current_line;
uint16_t ly_for_comparison; uint16_t ly_for_comparison;
GB_fifo_t bg_fifo, oam_fifo; GB_fifo_t bg_fifo, oam_fifo;
GB_PADDING(uint8_t, fetcher_x);
uint8_t fetcher_y; uint8_t fetcher_y;
uint16_t cycles_for_line; uint16_t cycles_for_line;
uint8_t current_tile; uint8_t current_tile;

View File

@ -112,9 +112,9 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb)
gb->mbc_ram_bank = gb->huc3.ram_bank; gb->mbc_ram_bank = gb->huc3.ram_bank;
break; break;
case GB_TPP1: case GB_TPP1:
gb->mbc_rom_bank = gb->tpp1_rom_bank; gb->mbc_rom_bank = gb->tpp1.rom_bank;
gb->mbc_ram_bank = gb->tpp1_ram_bank; gb->mbc_ram_bank = gb->tpp1.ram_bank;
gb->mbc_ram_enable = (gb->tpp1_mode == 2) || (gb->tpp1_mode == 3); gb->mbc_ram_enable = (gb->tpp1.mode == 2) || (gb->tpp1.mode == 3);
break; break;
} }
} }
@ -127,7 +127,7 @@ void GB_configure_cart(GB_gameboy_t *gb)
gb->rom[0x14a] == 0x65) { gb->rom[0x14a] == 0x65) {
static const GB_cartridge_t tpp1 = {GB_TPP1, GB_STANDARD_MBC, true, true, true, true}; static const GB_cartridge_t tpp1 = {GB_TPP1, GB_STANDARD_MBC, true, true, true, true};
gb->cartridge_type = &tpp1; gb->cartridge_type = &tpp1;
gb->tpp1_rom_bank = 1; gb->tpp1.rom_bank = 1;
} }
if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) { if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {

View File

@ -2,17 +2,17 @@
#include <stdbool.h> #include <stdbool.h>
#include "gb.h" #include "gb.h"
typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr); typedef uint8_t 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 void write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
typedef enum { typedef enum {
GB_BUS_MAIN, /* In DMG: Cart and RAM. In CGB: Cart only */ GB_BUS_MAIN, /* In DMG: Cart and RAM. In CGB: Cart only */
GB_BUS_RAM, /* In CGB only. */ GB_BUS_RAM, /* In CGB only. */
GB_BUS_VRAM, GB_BUS_VRAM,
GB_BUS_INTERNAL, /* Anything in highram. Might not be the most correct name. */ GB_BUS_INTERNAL, /* Anything in highram. Might not be the most correct name. */
} GB_bus_t; } bus_t;
static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr) static bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr)
{ {
if (addr < 0x8000) { if (addr < 0x8000) {
return GB_BUS_MAIN; return GB_BUS_MAIN;
@ -304,18 +304,18 @@ static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr)
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
{ {
if (gb->cartridge_type->mbc_type == GB_HUC3) { if (gb->cartridge_type->mbc_type == GB_HUC3) {
switch (gb->huc3_mode) { switch (gb->huc3.mode) {
case 0xC: // RTC read case 0xC: // RTC read
if (gb->huc3_access_flags == 0x2) { if (gb->huc3.access_flags == 0x2) {
return 1; return 1;
} }
return gb->huc3_read; return gb->huc3.read;
case 0xD: // RTC status case 0xD: // RTC status
return 1; return 1;
case 0xE: // IR mode case 0xE: // IR mode
return gb->effective_ir_input; // TODO: What are the other bits? return gb->effective_ir_input; // TODO: What are the other bits?
default: default:
GB_log(gb, "Unsupported HuC-3 mode %x read: %04x\n", gb->huc3_mode, addr); GB_log(gb, "Unsupported HuC-3 mode %x read: %04x\n", gb->huc3.mode, addr);
return 1; // TODO: What happens in this case? return 1; // TODO: What happens in this case?
case 0: // TODO: R/O RAM? (or is it disabled?) case 0: // TODO: R/O RAM? (or is it disabled?)
case 0xA: // RAM case 0xA: // RAM
@ -324,12 +324,12 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
} }
if (gb->cartridge_type->mbc_type == GB_TPP1) { if (gb->cartridge_type->mbc_type == GB_TPP1) {
switch (gb->tpp1_mode) { switch (gb->tpp1.mode) {
case 0: case 0:
switch (addr & 3) { switch (addr & 3) {
case 0: return gb->tpp1_rom_bank; case 0: return gb->tpp1.rom_bank;
case 1: return gb->tpp1_rom_bank >> 8; case 1: return gb->tpp1.rom_bank >> 8;
case 2: return gb->tpp1_ram_bank; case 2: return gb->tpp1.ram_bank;
case 3: return gb->rumble_strength | gb->tpp1_mr4; case 3: return gb->rumble_strength | gb->tpp1_mr4;
} }
case 2: case 2:
@ -353,7 +353,7 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
} }
if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3 && if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3 &&
gb->mbc3_rtc_mapped) { gb->mbc3.rtc_mapped) {
/* RTC read */ /* RTC read */
if (gb->mbc_ram_bank <= 4) { if (gb->mbc_ram_bank <= 4) {
gb->rtc_latched.seconds &= 0x3F; gb->rtc_latched.seconds &= 0x3F;
@ -651,7 +651,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
return gb->hram[addr - 0xFF80]; return gb->hram[addr - 0xFF80];
} }
static GB_read_function_t * const read_map[] = static read_function_t *const read_map[] =
{ {
read_rom, read_rom, read_rom, read_rom, /* 0XXX, 1XXX, 2XXX, 3XXX */ 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_mbc_rom, read_mbc_rom, read_mbc_rom, read_mbc_rom, /* 4XXX, 5XXX, 6XXX, 7XXX */
@ -706,7 +706,7 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break; case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break;
case 0x4000: case 0x5000: case 0x4000: case 0x5000:
gb->mbc3.ram_bank = value; gb->mbc3.ram_bank = value;
gb->mbc3_rtc_mapped = value & 8; gb->mbc3.rtc_mapped = value & 8;
break; break;
case 0x6000: case 0x7000: case 0x6000: case 0x7000:
memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real)); memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real));
@ -741,8 +741,8 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
case GB_HUC3: case GB_HUC3:
switch (addr & 0xF000) { switch (addr & 0xF000) {
case 0x0000: case 0x1000: case 0x0000: case 0x1000:
gb->huc3_mode = value & 0xF; gb->huc3.mode = value & 0xF;
gb->mbc_ram_enable = gb->huc3_mode == 0xA; gb->mbc_ram_enable = gb->huc3.mode == 0xA;
break; break;
case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break; case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break;
case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break; case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break;
@ -751,15 +751,15 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
case GB_TPP1: case GB_TPP1:
switch (addr & 3) { switch (addr & 3) {
case 0: case 0:
gb->tpp1_rom_bank &= 0xFF00; gb->tpp1.rom_bank &= 0xFF00;
gb->tpp1_rom_bank |= value; gb->tpp1.rom_bank |= value;
break; break;
case 1: case 1:
gb->tpp1_rom_bank &= 0xFF; gb->tpp1.rom_bank &= 0xFF;
gb->tpp1_rom_bank |= value << 8; gb->tpp1.rom_bank |= value << 8;
break; break;
case 2: case 2:
gb->tpp1_ram_bank = value; gb->tpp1.ram_bank = value;
break; break;
case 3: case 3:
switch (value) { switch (value) {
@ -767,7 +767,7 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
case 2: case 2:
case 3: case 3:
case 5: case 5:
gb->tpp1_mode = value; gb->tpp1.mode = value;
break; break;
case 0x10: case 0x10:
memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real)); memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real));
@ -823,59 +823,59 @@ static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
static bool huc3_write(GB_gameboy_t *gb, uint8_t value) static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
{ {
switch (gb->huc3_mode) { switch (gb->huc3.mode) {
case 0xB: // RTC Write case 0xB: // RTC Write
switch (value >> 4) { switch (value >> 4) {
case 1: case 1:
if (gb->huc3_access_index < 3) { if (gb->huc3.access_index < 3) {
gb->huc3_read = (gb->huc3_minutes >> (gb->huc3_access_index * 4)) & 0xF; gb->huc3.read = (gb->huc3.minutes >> (gb->huc3.access_index * 4)) & 0xF;
} }
else if (gb->huc3_access_index < 7) { else if (gb->huc3.access_index < 7) {
gb->huc3_read = (gb->huc3_days >> ((gb->huc3_access_index - 3) * 4)) & 0xF; gb->huc3.read = (gb->huc3.days >> ((gb->huc3.access_index - 3) * 4)) & 0xF;
} }
else { else {
// GB_log(gb, "Attempting to read from unsupported HuC-3 register: %03x\n", gb->huc3_access_index); // GB_log(gb, "Attempting to read from unsupported HuC-3 register: %03x\n", gb->huc3.access_index);
} }
gb->huc3_access_index++; gb->huc3.access_index++;
break; break;
case 2: case 2:
case 3: case 3:
if (gb->huc3_access_index < 3) { if (gb->huc3.access_index < 3) {
gb->huc3_minutes &= ~(0xF << (gb->huc3_access_index * 4)); gb->huc3.minutes &= ~(0xF << (gb->huc3.access_index * 4));
gb->huc3_minutes |= ((value & 0xF) << (gb->huc3_access_index * 4)); gb->huc3.minutes |= ((value & 0xF) << (gb->huc3.access_index * 4));
} }
else if (gb->huc3_access_index < 7) { else if (gb->huc3.access_index < 7) {
gb->huc3_days &= ~(0xF << ((gb->huc3_access_index - 3) * 4)); gb->huc3.days &= ~(0xF << ((gb->huc3.access_index - 3) * 4));
gb->huc3_days |= ((value & 0xF) << ((gb->huc3_access_index - 3) * 4)); gb->huc3.days |= ((value & 0xF) << ((gb->huc3.access_index - 3) * 4));
} }
else if (gb->huc3_access_index >= 0x58 && gb->huc3_access_index <= 0x5a) { else if (gb->huc3.access_index >= 0x58 && gb->huc3.access_index <= 0x5a) {
gb->huc3_alarm_minutes &= ~(0xF << ((gb->huc3_access_index - 0x58) * 4)); gb->huc3.alarm_minutes &= ~(0xF << ((gb->huc3.access_index - 0x58) * 4));
gb->huc3_alarm_minutes |= ((value & 0xF) << ((gb->huc3_access_index - 0x58) * 4)); gb->huc3.alarm_minutes |= ((value & 0xF) << ((gb->huc3.access_index - 0x58) * 4));
} }
else if (gb->huc3_access_index >= 0x5b && gb->huc3_access_index <= 0x5e) { else if (gb->huc3.access_index >= 0x5b && gb->huc3.access_index <= 0x5e) {
gb->huc3_alarm_days &= ~(0xF << ((gb->huc3_access_index - 0x5b) * 4)); gb->huc3.alarm_days &= ~(0xF << ((gb->huc3.access_index - 0x5b) * 4));
gb->huc3_alarm_days |= ((value & 0xF) << ((gb->huc3_access_index - 0x5b) * 4)); gb->huc3.alarm_days |= ((value & 0xF) << ((gb->huc3.access_index - 0x5b) * 4));
} }
else if (gb->huc3_access_index == 0x5f) { else if (gb->huc3.access_index == 0x5f) {
gb->huc3_alarm_enabled = value & 1; gb->huc3.alarm_enabled = value & 1;
} }
else { else {
// GB_log(gb, "Attempting to write %x to unsupported HuC-3 register: %03x\n", value & 0xF, gb->huc3_access_index); // GB_log(gb, "Attempting to write %x to unsupported HuC-3 register: %03x\n", value & 0xF, gb->huc3.access_index);
} }
if ((value >> 4) == 3) { if ((value >> 4) == 3) {
gb->huc3_access_index++; gb->huc3.access_index++;
} }
break; break;
case 4: case 4:
gb->huc3_access_index &= 0xF0; gb->huc3.access_index &= 0xF0;
gb->huc3_access_index |= value & 0xF; gb->huc3.access_index |= value & 0xF;
break; break;
case 5: case 5:
gb->huc3_access_index &= 0x0F; gb->huc3.access_index &= 0x0F;
gb->huc3_access_index |= (value & 0xF) << 4; gb->huc3.access_index |= (value & 0xF) << 4;
break; break;
case 6: case 6:
gb->huc3_access_flags = (value & 0xF); gb->huc3.access_flags = (value & 0xF);
break; break;
default: default:
@ -917,7 +917,7 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
} }
if (gb->cartridge_type->mbc_type == GB_TPP1) { if (gb->cartridge_type->mbc_type == GB_TPP1) {
switch (gb->tpp1_mode) { switch (gb->tpp1.mode) {
case 3: case 3:
break; break;
case 5: case 5:
@ -941,7 +941,7 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
return; return;
} }
if (gb->cartridge_type->has_rtc && gb->mbc3_rtc_mapped) { if (gb->cartridge_type->has_rtc && gb->mbc3.rtc_mapped) {
if (gb->mbc_ram_bank <= 4) { if (gb->mbc_ram_bank <= 4) {
if (gb->mbc_ram_bank == 0) { if (gb->mbc_ram_bank == 0) {
gb->rtc_cycles = 0; gb->rtc_cycles = 0;
@ -1191,6 +1191,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
return; return;
case GB_IO_DIV: case GB_IO_DIV:
GB_set_internal_div_counter(gb, 0);
/* Reset the div state machine */ /* Reset the div state machine */
gb->div_state = 0; gb->div_state = 0;
gb->div_cycles = 0; gb->div_cycles = 0;
@ -1388,7 +1389,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
static GB_write_function_t * const write_map[] = static 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, /* 0XXX, 1XXX, 2XXX, 3XXX */
write_mbc, write_mbc, write_mbc, write_mbc, /* 4XXX, 5XXX, 6XXX, 7XXX */ write_mbc, write_mbc, write_mbc, write_mbc, /* 4XXX, 5XXX, 6XXX, 7XXX */

View File

@ -276,30 +276,6 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb)
static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save, bool *attempt_bess) static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save, bool *attempt_bess)
{ {
*attempt_bess = false; *attempt_bess = false;
if (save->ram_size == 0 && (&save->ram_size)[-1] == gb->ram_size) {
/* This is a save state with a bad printer struct from a 32-bit OS */
memmove(save->extra_oam + 4, save->extra_oam, (uintptr_t)&save->ram_size - (uintptr_t)&save->extra_oam);
}
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 (save->model & GB_MODEL_PAL_BIT_OLD) {
save->model &= ~GB_MODEL_PAL_BIT_OLD;
save->model |= GB_MODEL_PAL_BIT;
}
if (save->model & GB_MODEL_NO_SFC_BIT_OLD) {
save->model &= ~GB_MODEL_NO_SFC_BIT_OLD;
save->model |= GB_MODEL_NO_SFC_BIT;
}
if (gb->version != save->version) { if (gb->version != save->version) {
GB_log(gb, "The save state is for a different version of SameBoy.\n"); GB_log(gb, "The save state is for a different version of SameBoy.\n");
@ -328,15 +304,9 @@ static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t
} }
if (gb->ram_size != save->ram_size) { 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"); GB_log(gb, "The save state has non-matching RAM size. Try changing the emulated model.\n");
return false; return false;
} }
}
switch (save->model) { switch (save->model) {
case GB_MODEL_DMG_B: return true; case GB_MODEL_DMG_B: return true;
@ -407,62 +377,12 @@ static void sanitize_state(GB_gameboy_t *gb)
gb->lcd_x = gb->position_in_line; gb->lcd_x = gb->position_in_line;
} }
if (gb->object_priority == GB_OBJECT_PRIORITY_UNDEFINED) {
gb->object_priority = gb->cgb_mode? GB_OBJECT_PRIORITY_INDEX : GB_OBJECT_PRIORITY_X;
}
if (gb->sgb) { if (gb->sgb) {
if (gb->sgb->player_count != 1 && gb->sgb->player_count != 2 && gb->sgb->player_count != 4) { if (gb->sgb->player_count != 1 && gb->sgb->player_count != 2 && gb->sgb->player_count != 4) {
gb->sgb->player_count = 1; gb->sgb->player_count = 1;
} }
gb->sgb->current_player &= gb->sgb->player_count - 1; gb->sgb->current_player &= gb->sgb->player_count - 1;
} }
if (gb->sgb && !gb->sgb->v14_3) {
#ifdef GB_BIG_ENDIAN
for (unsigned i = 0; i < sizeof(gb->sgb->border.raw_data) / 2; i++) {
gb->sgb->border.raw_data[i] = LE16(gb->sgb->border.raw_data[i]);
}
for (unsigned i = 0; i < sizeof(gb->sgb->pending_border.raw_data) / 2; i++) {
gb->sgb->pending_border.raw_data[i] = LE16(gb->sgb->pending_border.raw_data[i]);
}
for (unsigned i = 0; i < sizeof(gb->sgb->effective_palettes) / 2; i++) {
gb->sgb->effective_palettes[i] = LE16(gb->sgb->effective_palettes[i]);
}
for (unsigned i = 0; i < sizeof(gb->sgb->ram_palettes) / 2; i++) {
gb->sgb->ram_palettes[i] = LE16(gb->sgb->ram_palettes[i]);
}
#endif
uint8_t converted_tiles[sizeof(gb->sgb->border.tiles)] = {0,};
for (unsigned tile = 0; tile < sizeof(gb->sgb->border.tiles_legacy) / 64; tile++) {
for (unsigned y = 0; y < 8; y++) {
unsigned base = tile * 32 + y * 2;
for (unsigned x = 0; x < 8; x++) {
uint8_t pixel = gb->sgb->border.tiles_legacy[tile * 8 * 8 + y * 8 + x];
if (pixel & 1) converted_tiles[base] |= (1 << (7 ^ x));
if (pixel & 2) converted_tiles[base + 1] |= (1 << (7 ^ x));
if (pixel & 4) converted_tiles[base + 16] |= (1 << (7 ^ x));
if (pixel & 8) converted_tiles[base + 17] |= (1 << (7 ^ x));
}
}
}
memcpy(gb->sgb->border.tiles, converted_tiles, sizeof(converted_tiles));
memset(converted_tiles, 0, sizeof(converted_tiles));
for (unsigned tile = 0; tile < sizeof(gb->sgb->pending_border.tiles_legacy) / 64; tile++) {
for (unsigned y = 0; y < 8; y++) {
unsigned base = tile * 32 + y * 2;
for (unsigned x = 0; x < 8; x++) {
uint8_t pixel = gb->sgb->pending_border.tiles_legacy[tile * 8 * 8 + y * 8 + x];
if (pixel & 1) converted_tiles[base] |= (1 << (7 ^ x));
if (pixel & 2) converted_tiles[base + 1] |= (1 << (7 ^ x));
if (pixel & 4) converted_tiles[base + 16] |= (1 << (7 ^ x));
if (pixel & 8) converted_tiles[base + 17] |= (1 << (7 ^ x));
}
}
}
memcpy(gb->sgb->pending_border.tiles, converted_tiles, sizeof(converted_tiles));
}
} }
static bool dump_section(virtual_file_t *file, const void *src, uint32_t size) static bool dump_section(virtual_file_t *file, const void *src, uint32_t size)
@ -503,7 +423,7 @@ static int save_bess_mbc_block(GB_gameboy_t *gb, virtual_file_t *file)
case GB_MBC3: case GB_MBC3:
pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->mbc_ram_enable? 0xA : 0x0}; pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->mbc_ram_enable? 0xA : 0x0};
pairs[1] = (BESS_MBC_pair_t){LE16(0x2000), gb->mbc3.rom_bank}; pairs[1] = (BESS_MBC_pair_t){LE16(0x2000), gb->mbc3.rom_bank};
pairs[2] = (BESS_MBC_pair_t){LE16(0x4000), gb->mbc3.ram_bank | (gb->mbc3_rtc_mapped? 8 : 0)}; pairs[2] = (BESS_MBC_pair_t){LE16(0x4000), gb->mbc3.ram_bank | (gb->mbc3.rtc_mapped? 8 : 0)};
mbc_block.size = 3 * sizeof(pairs[0]); mbc_block.size = 3 * sizeof(pairs[0]);
break; break;
case GB_MBC5: case GB_MBC5:
@ -521,17 +441,17 @@ static int save_bess_mbc_block(GB_gameboy_t *gb, virtual_file_t *file)
mbc_block.size = 4 * sizeof(pairs[0]); mbc_block.size = 4 * sizeof(pairs[0]);
case GB_HUC3: case GB_HUC3:
pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->huc3_mode}; pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->huc3.mode};
pairs[1] = (BESS_MBC_pair_t){LE16(0x2000), gb->huc3.rom_bank}; pairs[1] = (BESS_MBC_pair_t){LE16(0x2000), gb->huc3.rom_bank};
pairs[2] = (BESS_MBC_pair_t){LE16(0x4000), gb->huc3.ram_bank}; pairs[2] = (BESS_MBC_pair_t){LE16(0x4000), gb->huc3.ram_bank};
mbc_block.size = 3 * sizeof(pairs[0]); mbc_block.size = 3 * sizeof(pairs[0]);
break; break;
case GB_TPP1: case GB_TPP1:
pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->tpp1_rom_bank}; pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->tpp1.rom_bank};
pairs[1] = (BESS_MBC_pair_t){LE16(0x0001), gb->tpp1_rom_bank >> 8}; pairs[1] = (BESS_MBC_pair_t){LE16(0x0001), gb->tpp1.rom_bank >> 8};
pairs[2] = (BESS_MBC_pair_t){LE16(0x0002), gb->tpp1_rom_bank}; pairs[2] = (BESS_MBC_pair_t){LE16(0x0002), gb->tpp1.rom_bank};
pairs[3] = (BESS_MBC_pair_t){LE16(0x0003), gb->tpp1_mode}; pairs[3] = (BESS_MBC_pair_t){LE16(0x0003), gb->tpp1.mode};
mbc_block.size = 4 * sizeof(pairs[0]); mbc_block.size = 4 * sizeof(pairs[0]);
break; break;
} }
@ -566,7 +486,6 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file, bool appe
uint32_t sgb_offset = 0; uint32_t sgb_offset = 0;
if (GB_is_hle_sgb(gb)) { if (GB_is_hle_sgb(gb)) {
gb->sgb->v14_3 = true;
sgb_offset = file->tell(file) + 4; sgb_offset = file->tell(file) + 4;
if (!dump_section(file, gb->sgb, sizeof(*gb->sgb))) goto error; if (!dump_section(file, gb->sgb, sizeof(*gb->sgb))) goto error;
} }
@ -746,11 +665,11 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file, bool appe
bess_huc3.data = (GB_huc3_rtc_time_t) { bess_huc3.data = (GB_huc3_rtc_time_t) {
LE64(gb->last_rtc_second), LE64(gb->last_rtc_second),
LE16(gb->huc3_minutes), LE16(gb->huc3.minutes),
LE16(gb->huc3_days), LE16(gb->huc3.days),
LE16(gb->huc3_alarm_minutes), LE16(gb->huc3.alarm_minutes),
LE16(gb->huc3_alarm_days), LE16(gb->huc3.alarm_days),
gb->huc3_alarm_enabled, gb->huc3.alarm_enabled,
}; };
if (file->write(file, &bess_huc3, sizeof(bess_huc3)) != sizeof(bess_huc3)) { if (file->write(file, &bess_huc3, sizeof(bess_huc3)) != sizeof(bess_huc3)) {
goto error; goto error;
@ -1116,11 +1035,11 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
if (gb->rtc_mode == GB_RTC_MODE_SYNC_TO_HOST) { if (gb->rtc_mode == GB_RTC_MODE_SYNC_TO_HOST) {
save.last_rtc_second = MIN(LE64(bess_huc3.data.last_rtc_second), time(NULL)); save.last_rtc_second = MIN(LE64(bess_huc3.data.last_rtc_second), time(NULL));
} }
save.huc3_minutes = LE16(bess_huc3.data.minutes); save.huc3.minutes = LE16(bess_huc3.data.minutes);
save.huc3_days = LE16(bess_huc3.data.days); save.huc3.days = LE16(bess_huc3.data.days);
save.huc3_alarm_minutes = LE16(bess_huc3.data.alarm_minutes); save.huc3.alarm_minutes = LE16(bess_huc3.data.alarm_minutes);
save.huc3_alarm_days = LE16(bess_huc3.data.alarm_days); save.huc3.alarm_days = LE16(bess_huc3.data.alarm_days);
save.huc3_alarm_enabled = bess_huc3.data.alarm_enabled; save.huc3.alarm_enabled = bess_huc3.data.alarm_enabled;
break; break;
case BE32('TPP1'): case BE32('TPP1'):
if (!found_core) goto parse_error; if (!found_core) goto parse_error;

View File

@ -6,10 +6,7 @@
typedef struct GB_sgb_s GB_sgb_t; typedef struct GB_sgb_s GB_sgb_t;
typedef struct { typedef struct {
union {
uint8_t tiles[0x100 * 8 * 4]; uint8_t tiles[0x100 * 8 * 4];
uint8_t tiles_legacy[0x100 * 8 * 8]; /* High nibble not used; TODO: Remove when breaking save-state compatibility! */
};
union { union {
struct { struct {
uint16_t map[32 * 32]; uint16_t map[32 * 32];
@ -59,11 +56,6 @@ struct GB_sgb_s {
/* GB Header */ /* GB Header */
uint8_t received_header[0x54]; uint8_t received_header[0x54];
/* Multiplayer (cont) */
GB_PADDING(bool, mlt_lock);
bool v14_3; // True on save states created on 0.14.3 or newer; Remove when breaking save state compatibility!
}; };
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value); void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);

View File

@ -4,7 +4,7 @@
#include "gb.h" #include "gb.h"
typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode); typedef void opcode_t(GB_gameboy_t *gb, uint8_t opcode);
typedef enum { typedef enum {
/* Default behavior. If the CPU writes while another component reads, it reads the old value */ /* Default behavior. If the CPU writes while another component reads, it reads the old value */
@ -23,10 +23,10 @@ typedef enum {
GB_CONFLICT_WX, GB_CONFLICT_WX,
GB_CONFLICT_CGB_LCDC, GB_CONFLICT_CGB_LCDC,
GB_CONFLICT_NR10, GB_CONFLICT_NR10,
} GB_conflict_t; } conflict_t;
/* Todo: How does double speed mode affect these? */ /* Todo: How does double speed mode affect these? */
static const GB_conflict_t cgb_conflict_map[0x80] = { static const conflict_t cgb_conflict_map[0x80] = {
[GB_IO_LCDC] = GB_CONFLICT_CGB_LCDC, [GB_IO_LCDC] = GB_CONFLICT_CGB_LCDC,
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU, [GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
[GB_IO_LYC] = GB_CONFLICT_WRITE_CPU, [GB_IO_LYC] = GB_CONFLICT_WRITE_CPU,
@ -41,7 +41,7 @@ static const GB_conflict_t cgb_conflict_map[0x80] = {
}; };
/* Todo: verify on an MGB */ /* Todo: verify on an MGB */
static const GB_conflict_t dmg_conflict_map[0x80] = { static const conflict_t dmg_conflict_map[0x80] = {
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU, [GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
[GB_IO_LYC] = GB_CONFLICT_READ_OLD, [GB_IO_LYC] = GB_CONFLICT_READ_OLD,
[GB_IO_LCDC] = GB_CONFLICT_DMG_LCDC, [GB_IO_LCDC] = GB_CONFLICT_DMG_LCDC,
@ -60,7 +60,7 @@ static const GB_conflict_t dmg_conflict_map[0x80] = {
}; };
/* Todo: Verify on an SGB1 */ /* Todo: Verify on an SGB1 */
static const GB_conflict_t sgb_conflict_map[0x80] = { static const conflict_t sgb_conflict_map[0x80] = {
[GB_IO_IF] = GB_CONFLICT_WRITE_CPU, [GB_IO_IF] = GB_CONFLICT_WRITE_CPU,
[GB_IO_LYC] = GB_CONFLICT_READ_OLD, [GB_IO_LYC] = GB_CONFLICT_READ_OLD,
[GB_IO_LCDC] = GB_CONFLICT_SGB_LCDC, [GB_IO_LCDC] = GB_CONFLICT_SGB_LCDC,
@ -109,9 +109,9 @@ static uint8_t cycle_write_if(GB_gameboy_t *gb, uint8_t value)
static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{ {
assert(gb->pending_cycles); assert(gb->pending_cycles);
GB_conflict_t conflict = GB_CONFLICT_READ_OLD; conflict_t conflict = GB_CONFLICT_READ_OLD;
if ((addr & 0xFF80) == 0xFF00) { if ((addr & 0xFF80) == 0xFF00) {
const GB_conflict_t *map = NULL; const conflict_t *map = NULL;
if (GB_is_cgb(gb)) { if (GB_is_cgb(gb)) {
map = cgb_conflict_map; map = cgb_conflict_map;
} }
@ -1530,7 +1530,7 @@ static void cb_prefix(GB_gameboy_t *gb, uint8_t opcode)
} }
} }
static GB_opcode_t *opcodes[256] = { static opcode_t *opcodes[256] = {
/* X0 X1 X2 X3 X4 X5 X6 X7 */ /* X0 X1 X2 X3 X4 X5 X6 X7 */
/* X8 X9 Xa Xb Xc Xd Xe Xf */ /* 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 */ nop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rlca, /* 0X */

View File

@ -3,7 +3,7 @@
#include "gb.h" #include "gb.h"
typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc); typedef void 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) static void ill(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
{ {
@ -716,7 +716,7 @@ static void cb_prefix(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
} }
} }
static GB_opcode_t *opcodes[256] = { static opcode_t *opcodes[256] = {
/* X0 X1 X2 X3 X4 X5 X6 X7 */ /* X0 X1 X2 X3 X4 X5 X6 X7 */
/* X8 X9 Xa Xb Xc Xd Xe Xf */ /* 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 */ nop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rlca, /* 0X */

View File

@ -4,7 +4,7 @@
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
static size_t GB_map_find_symbol_index(GB_symbol_map_t *map, uint16_t addr) static size_t map_find_symbol_index(GB_symbol_map_t *map, uint16_t addr)
{ {
if (!map->symbols) { if (!map->symbols) {
return 0; return 0;
@ -26,7 +26,7 @@ static size_t GB_map_find_symbol_index(GB_symbol_map_t *map, uint16_t addr)
GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name) 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); size_t index = map_find_symbol_index(map, addr);
map->symbols = realloc(map->symbols, (map->n_symbols + 1) * sizeof(map->symbols[0])); 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])); memmove(&map->symbols[index + 1], &map->symbols[index], (map->n_symbols - index) * sizeof(map->symbols[0]));
@ -39,7 +39,7 @@ GB_bank_symbol_t *GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const c
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr) const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr)
{ {
if (!map) return NULL; if (!map) return NULL;
size_t index = GB_map_find_symbol_index(map, addr); size_t index = map_find_symbol_index(map, addr);
if (index >= map->n_symbols || map->symbols[index].addr != addr) { if (index >= map->n_symbols || map->symbols[index].addr != addr) {
index--; index--;
} }

View File

@ -8,7 +8,7 @@
#include <sys/time.h> #include <sys/time.h>
#endif #endif
static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128}; static const unsigned TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
#ifndef GB_DISABLE_TIMEKEEPING #ifndef GB_DISABLE_TIMEKEEPING
static int64_t get_nanoseconds(void) static int64_t get_nanoseconds(void)
@ -99,7 +99,7 @@ void GB_timing_sync(GB_gameboy_t *gb)
#define IR_THRESHOLD 19900 #define IR_THRESHOLD 19900
#define IR_MAX IR_THRESHOLD * 2 + IR_DECAY #define IR_MAX IR_THRESHOLD * 2 + IR_DECAY
static void GB_ir_run(GB_gameboy_t *gb, uint32_t cycles) static void ir_run(GB_gameboy_t *gb, uint32_t cycles)
{ {
if (gb->model == GB_MODEL_AGB) return; if (gb->model == GB_MODEL_AGB) return;
if (gb->infrared_input || gb->cart_ir || (gb->io_registers[GB_IO_RP] & 1)) { if (gb->infrared_input || gb->cart_ir || (gb->io_registers[GB_IO_RP] & 1)) {
@ -142,11 +142,11 @@ static void increase_tima(GB_gameboy_t *gb)
} }
} }
static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value) void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value)
{ {
/* TIMA increases when a specific high-bit becomes a low-bit. */ /* TIMA increases when a specific high-bit becomes a low-bit. */
uint16_t triggers = gb->div_counter & ~value; uint16_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])) { if ((gb->io_registers[GB_IO_TAC] & 4) && (triggers & TAC_TRIGGER_BITS[gb->io_registers[GB_IO_TAC] & 3])) {
increase_tima(gb); increase_tima(gb);
} }
@ -166,7 +166,7 @@ static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value)
gb->div_counter = value; gb->div_counter = value;
} }
static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles) static void timers_run(GB_gameboy_t *gb, uint8_t cycles)
{ {
if (gb->stopped) { if (gb->stopped) {
if (GB_is_cgb(gb)) { if (GB_is_cgb(gb)) {
@ -178,11 +178,8 @@ static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles)
GB_STATE_MACHINE(gb, div, cycles, 1) { GB_STATE_MACHINE(gb, div, cycles, 1) {
GB_STATE(gb, div, 1); GB_STATE(gb, div, 1);
GB_STATE(gb, div, 2); GB_STATE(gb, div, 2);
GB_STATE(gb, div, 3);
} }
GB_set_internal_div_counter(gb, 0);
main:
GB_SLEEP(gb, div, 1, 3); GB_SLEEP(gb, div, 1, 3);
while (true) { while (true) {
advance_tima_state_machine(gb); advance_tima_state_machine(gb);
@ -190,14 +187,6 @@ main:
gb->apu.apu_cycles += 4 << !gb->cgb_double_speed; gb->apu.apu_cycles += 4 << !gb->cgb_double_speed;
GB_SLEEP(gb, div, 2, 4); 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) static void advance_serial(GB_gameboy_t *gb, uint8_t cycles)
@ -247,7 +236,7 @@ static void advance_serial(GB_gameboy_t *gb, uint8_t cycles)
} }
static void GB_rtc_run(GB_gameboy_t *gb, uint8_t cycles) static void rtc_run(GB_gameboy_t *gb, uint8_t cycles)
{ {
if (gb->cartridge_type->mbc_type != GB_HUC3 && !gb->cartridge_type->has_rtc) return; if (gb->cartridge_type->mbc_type != GB_HUC3 && !gb->cartridge_type->has_rtc) return;
gb->rtc_cycles += cycles; gb->rtc_cycles += cycles;
@ -274,10 +263,10 @@ static void GB_rtc_run(GB_gameboy_t *gb, uint8_t cycles)
if (gb->cartridge_type->mbc_type == GB_HUC3) { if (gb->cartridge_type->mbc_type == GB_HUC3) {
while (gb->last_rtc_second / 60 < current_time / 60) { while (gb->last_rtc_second / 60 < current_time / 60) {
gb->last_rtc_second += 60; gb->last_rtc_second += 60;
gb->huc3_minutes++; gb->huc3.minutes++;
if (gb->huc3_minutes == 60 * 24) { if (gb->huc3.minutes == 60 * 24) {
gb->huc3_days++; gb->huc3.days++;
gb->huc3_minutes = 0; gb->huc3.minutes = 0;
} }
} }
return; return;
@ -366,7 +355,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
// Affected by speed boost // Affected by speed boost
gb->dma_cycles += cycles; gb->dma_cycles += cycles;
GB_timers_run(gb, cycles); timers_run(gb, cycles);
if (!gb->stopped) { if (!gb->stopped) {
advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode
} }
@ -414,8 +403,8 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
} }
GB_apu_run(gb); GB_apu_run(gb);
GB_display_run(gb, cycles); GB_display_run(gb, cycles);
GB_ir_run(gb, cycles); ir_run(gb, cycles);
GB_rtc_run(gb, cycles); rtc_run(gb, cycles);
} }
/* /*
@ -428,8 +417,8 @@ 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. */ /* Glitch only happens when old_tac is enabled. */
if (!(old_tac & 4)) return; if (!(old_tac & 4)) return;
unsigned old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3]; unsigned old_clocks = TAC_TRIGGER_BITS[old_tac & 3];
unsigned new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3]; unsigned new_clocks = TAC_TRIGGER_BITS[new_tac & 3];
/* The bit used for overflow testing must have been 1 */ /* The bit used for overflow testing must have been 1 */
if (gb->div_counter & old_clocks) { if (gb->div_counter & old_clocks) {

View File

@ -7,6 +7,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac); 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 */ bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */
void GB_timing_sync(GB_gameboy_t *gb); void GB_timing_sync(GB_gameboy_t *gb);
void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value);
enum { enum {
GB_TIMA_RUNNING = 0, GB_TIMA_RUNNING = 0,