Save state compatibility breaking cleanup
This commit is contained in:
parent
5565c2540b
commit
fbf1bb7f98
@ -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!!! */
|
||||||
|
|
||||||
|
104
Core/apu.c
104
Core/apu.c
@ -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) {
|
||||||
|
41
Core/apu.h
41
Core/apu.h
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
145
Core/gb.c
@ -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;
|
||||||
}
|
}
|
||||||
|
33
Core/gb.h
33
Core/gb.h
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
109
Core/memory.c
109
Core/memory.c
@ -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 */
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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 */
|
||||||
|
@ -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 */
|
||||||
|
@ -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--;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user