mirror of
https://github.com/libretro/sameduck.git
synced 2024-11-23 09:09:41 +00:00
Quick & Dirty conversion to a Mega Duck emulator
This commit is contained in:
parent
c36bdc22f6
commit
316a282501
@ -60,8 +60,6 @@
|
||||
addr += 0x8000;
|
||||
break;
|
||||
case GBMemoryExternalRAM:
|
||||
bank_backup = gb->mbc_ram_bank;
|
||||
gb->mbc_ram_bank = self.selectedBank;
|
||||
addr += 0xA000;
|
||||
break;
|
||||
case GBMemoryRAM:
|
||||
@ -86,7 +84,6 @@
|
||||
gb->cgb_vram_bank = bank_backup;
|
||||
break;
|
||||
case GBMemoryExternalRAM:
|
||||
gb->mbc_ram_bank = bank_backup;
|
||||
break;
|
||||
case GBMemoryRAM:
|
||||
gb->cgb_ram_bank = bank_backup;
|
||||
@ -133,8 +130,6 @@
|
||||
addr += 0x8000;
|
||||
break;
|
||||
case GBMemoryExternalRAM:
|
||||
bank_backup = gb->mbc_ram_bank;
|
||||
gb->mbc_ram_bank = self.selectedBank;
|
||||
addr += 0xA000;
|
||||
break;
|
||||
case GBMemoryRAM:
|
||||
@ -163,7 +158,6 @@
|
||||
gb->cgb_vram_bank = bank_backup;
|
||||
break;
|
||||
case GBMemoryExternalRAM:
|
||||
gb->mbc_ram_bank = bank_backup;
|
||||
break;
|
||||
case GBMemoryRAM:
|
||||
gb->cgb_ram_bank = bank_backup;
|
||||
|
68
Core/apu.c
68
Core/apu.c
@ -33,16 +33,16 @@ bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index)
|
||||
|
||||
switch (index) {
|
||||
case GB_SQUARE_1:
|
||||
return gb->io_registers[GB_IO_NR12] & 0xF8;
|
||||
return gb->io_registers[GB_IO_NR12] & 0x8F;
|
||||
|
||||
case GB_SQUARE_2:
|
||||
return gb->io_registers[GB_IO_NR22] & 0xF8;
|
||||
return gb->io_registers[GB_IO_NR22] & 0x8F;
|
||||
|
||||
case GB_WAVE:
|
||||
return gb->apu.wave_channel.enable;
|
||||
|
||||
case GB_NOISE:
|
||||
return gb->io_registers[GB_IO_NR42] & 0xF8;
|
||||
return gb->io_registers[GB_IO_NR42] & 0x8F;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -258,7 +258,7 @@ static void update_square_sample(GB_gameboy_t *gb, unsigned index)
|
||||
DMG, MGB or SGB/2 */
|
||||
static void nrx2_glitch(uint8_t *volume, uint8_t value, uint8_t old_value)
|
||||
{
|
||||
if (value & 8) {
|
||||
if (value & 0x80) {
|
||||
(*volume)++;
|
||||
}
|
||||
|
||||
@ -266,11 +266,11 @@ static void nrx2_glitch(uint8_t *volume, uint8_t value, uint8_t old_value)
|
||||
*volume = 0x10 - *volume;
|
||||
}
|
||||
|
||||
if ((value & 7) && !(old_value & 7) && *volume && !(value & 8)) {
|
||||
if ((value & 0x70) && !(old_value & 0x70) && *volume && !(value & 0x80)) {
|
||||
(*volume)--;
|
||||
}
|
||||
|
||||
if ((old_value & 7) && (value & 8)) {
|
||||
if ((old_value & 0x70) && (value & 0x880)) {
|
||||
(*volume)--;
|
||||
}
|
||||
|
||||
@ -281,7 +281,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];
|
||||
|
||||
if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 7)) {
|
||||
if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 0x70)) {
|
||||
if (!gb->apu.square_channels[index].volume_countdown || !--gb->apu.square_channels[index].volume_countdown) {
|
||||
if (gb->cgb_double_speed) {
|
||||
if (index == GB_SQUARE_1) {
|
||||
@ -292,15 +292,15 @@ static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index)
|
||||
}
|
||||
}
|
||||
|
||||
if ((nrx2 & 8) && gb->apu.square_channels[index].current_volume < 0xF) {
|
||||
if ((nrx2 & 0x80) && gb->apu.square_channels[index].current_volume < 0xF) {
|
||||
gb->apu.square_channels[index].current_volume++;
|
||||
}
|
||||
|
||||
else if (!(nrx2 & 8) && gb->apu.square_channels[index].current_volume > 0) {
|
||||
else if (!(nrx2 & 0x80) && gb->apu.square_channels[index].current_volume > 0) {
|
||||
gb->apu.square_channels[index].current_volume--;
|
||||
}
|
||||
|
||||
gb->apu.square_channels[index].volume_countdown = nrx2 & 7;
|
||||
gb->apu.square_channels[index].volume_countdown = (nrx2 >> 4) & 7;
|
||||
|
||||
if (gb->apu.is_active[index]) {
|
||||
update_square_sample(gb, index);
|
||||
@ -313,20 +313,20 @@ static void tick_noise_envelope(GB_gameboy_t *gb)
|
||||
{
|
||||
uint8_t nr42 = gb->io_registers[GB_IO_NR42];
|
||||
|
||||
if (gb->apu.noise_channel.volume_countdown || (nr42 & 7)) {
|
||||
if (gb->apu.noise_channel.volume_countdown || (nr42 & 0x70)) {
|
||||
if (!gb->apu.noise_channel.volume_countdown || !--gb->apu.noise_channel.volume_countdown) {
|
||||
if (gb->cgb_double_speed) {
|
||||
gb->apu.pcm_mask[0] &= (gb->apu.noise_channel.current_volume << 2) | 0x1F;
|
||||
}
|
||||
if ((nr42 & 8) && gb->apu.noise_channel.current_volume < 0xF) {
|
||||
if ((nr42 & 0x80) && gb->apu.noise_channel.current_volume < 0xF) {
|
||||
gb->apu.noise_channel.current_volume++;
|
||||
}
|
||||
|
||||
else if (!(nr42 & 8) && gb->apu.noise_channel.current_volume > 0) {
|
||||
else if (!(nr42 & 0x80) && gb->apu.noise_channel.current_volume > 0) {
|
||||
gb->apu.noise_channel.current_volume--;
|
||||
}
|
||||
|
||||
gb->apu.noise_channel.volume_countdown = nr42 & 7;
|
||||
gb->apu.noise_channel.volume_countdown = (nr42 >> 4) & 7;
|
||||
|
||||
if (gb->apu.is_active[GB_NOISE]) {
|
||||
update_sample(gb, GB_NOISE,
|
||||
@ -360,7 +360,7 @@ void GB_apu_div_event(GB_gameboy_t *gb)
|
||||
}
|
||||
}
|
||||
|
||||
if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0 && (gb->io_registers[GB_IO_NR42] & 7)) {
|
||||
if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0 && (gb->io_registers[GB_IO_NR42] & 0x70)) {
|
||||
tick_noise_envelope(gb);
|
||||
}
|
||||
}
|
||||
@ -579,17 +579,17 @@ uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg)
|
||||
return value;
|
||||
}
|
||||
|
||||
static const char read_mask[GB_IO_WAV_END - GB_IO_NR10 + 1] = {
|
||||
/* NRX0 NRX1 NRX2 NRX3 NRX4 */
|
||||
0x80, 0x3F, 0x00, 0xFF, 0xBF, // NR1X
|
||||
0xFF, 0x3F, 0x00, 0xFF, 0xBF, // NR2X
|
||||
static const char read_mask[GB_IO_NR51 - GB_IO_NR10 + 1] = {
|
||||
0x80, 0x00, 0x3F, 0xFF, 0xBF, // NR1X
|
||||
0x3F, 0xFF, 0x00, 0xFF, 0xBF, // NR2X
|
||||
0x7F, 0xFF, 0x9F, 0xFF, 0xBF, // NR3X
|
||||
0xFF, 0xFF, 0x00, 0x00, 0xBF, // NR4X
|
||||
0x00, 0x00, 0x70, 0xFF, 0xFF, // NR5X
|
||||
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Unused
|
||||
0xFF, // "NR40"
|
||||
// Wave RAM
|
||||
0, /* ... */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0xFF, 0x00, 0x00, 0xBF, // NR4X
|
||||
0x00, 0x00, 0x70, // NR5X
|
||||
};
|
||||
|
||||
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) {
|
||||
@ -695,11 +695,11 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
case GB_IO_NR12:
|
||||
case GB_IO_NR22: {
|
||||
unsigned index = reg == GB_IO_NR22? GB_SQUARE_2: GB_SQUARE_1;
|
||||
if (((value & 0x7) == 0) && ((gb->io_registers[reg] & 0x7) != 0)) {
|
||||
if ((((value >> 4) & 0x7) == 0) && (((gb->io_registers[reg] >> 4) & 0x7) != 0)) {
|
||||
/* Envelope disabled */
|
||||
gb->apu.square_channels[index].volume_countdown = 0;
|
||||
}
|
||||
if ((value & 0xF8) == 0) {
|
||||
if ((value & 0x8F) == 0) {
|
||||
/* This disables the DAC */
|
||||
gb->io_registers[reg] = value;
|
||||
gb->apu.is_active[index] = false;
|
||||
@ -756,7 +756,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
/* Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier.*/
|
||||
gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 4 - gb->apu.lf_div;
|
||||
}
|
||||
gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4;
|
||||
gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0xF;
|
||||
|
||||
/* The volume changes caused by NRX4 sound start take effect instantly (i.e. the effect the previously
|
||||
started sound). The playback itself is not instant which is why we don't update the sample for other
|
||||
@ -765,9 +765,9 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
update_square_sample(gb, index);
|
||||
}
|
||||
|
||||
gb->apu.square_channels[index].volume_countdown = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 7;
|
||||
gb->apu.square_channels[index].volume_countdown = (gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4) & 7;
|
||||
|
||||
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] & 0x8F) != 0 && !gb->apu.is_active[index]) {
|
||||
gb->apu.is_active[index] = true;
|
||||
update_sample(gb, index, 0, 0);
|
||||
/* We use the highest bit in current_sample_index to mark this sample is not actually playing yet, */
|
||||
@ -915,11 +915,11 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
}
|
||||
|
||||
case GB_IO_NR42: {
|
||||
if (((value & 0x7) == 0) && ((gb->io_registers[reg] & 0x7) != 0)) {
|
||||
if (((value & 0x70) == 0) && ((gb->io_registers[reg] & 0x70) != 0)) {
|
||||
/* Envelope disabled */
|
||||
gb->apu.noise_channel.volume_countdown = 0;
|
||||
}
|
||||
if ((value & 0xF8) == 0) {
|
||||
if ((value & 0x8F) == 0) {
|
||||
/* This disables the DAC */
|
||||
gb->io_registers[reg] = value;
|
||||
gb->apu.is_active[GB_NOISE] = false;
|
||||
@ -936,10 +936,10 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
}
|
||||
|
||||
case GB_IO_NR43: {
|
||||
gb->apu.noise_channel.narrow = value & 8;
|
||||
unsigned divisor = (value & 0x07) << 1;
|
||||
gb->apu.noise_channel.narrow = value & 0x80;
|
||||
unsigned divisor = (value & 0x70) << 1;
|
||||
if (!divisor) divisor = 1;
|
||||
gb->apu.noise_channel.sample_length = (divisor << (value >> 4)) - 1;
|
||||
gb->apu.noise_channel.sample_length = (divisor << (value & 0xF)) - 1;
|
||||
|
||||
/* Todo: changing the frequency sometimes delays the next sample. This is probably
|
||||
due to how the frequency is actually calculated in the noise channel, which is probably
|
||||
|
@ -80,8 +80,6 @@ static uint16_t bank_for_addr(GB_gameboy_t *gb, uint16_t addr)
|
||||
typedef struct {
|
||||
uint16_t rom0_bank;
|
||||
uint16_t rom_bank;
|
||||
uint8_t mbc_ram_bank;
|
||||
bool mbc_ram_enable;
|
||||
uint8_t ram_bank;
|
||||
uint8_t vram_bank;
|
||||
} banking_state_t;
|
||||
@ -90,8 +88,6 @@ static inline void save_banking_state(GB_gameboy_t *gb, banking_state_t *state)
|
||||
{
|
||||
state->rom0_bank = gb->mbc_rom0_bank;
|
||||
state->rom_bank = gb->mbc_rom_bank;
|
||||
state->mbc_ram_bank = gb->mbc_ram_bank;
|
||||
state->mbc_ram_enable = gb->mbc_ram_enable;
|
||||
state->ram_bank = gb->cgb_ram_bank;
|
||||
state->vram_bank = gb->cgb_vram_bank;
|
||||
}
|
||||
@ -101,8 +97,6 @@ static inline void restore_banking_state(GB_gameboy_t *gb, banking_state_t *stat
|
||||
|
||||
gb->mbc_rom0_bank = state->rom0_bank;
|
||||
gb->mbc_rom_bank = state->rom_bank;
|
||||
gb->mbc_ram_bank = state->mbc_ram_bank;
|
||||
gb->mbc_ram_enable = state->mbc_ram_enable;
|
||||
gb->cgb_ram_bank = state->ram_bank;
|
||||
gb->cgb_vram_bank = state->vram_bank;
|
||||
}
|
||||
@ -111,8 +105,6 @@ static inline void switch_banking_state(GB_gameboy_t *gb, uint16_t bank)
|
||||
{
|
||||
gb->mbc_rom0_bank = bank;
|
||||
gb->mbc_rom_bank = bank;
|
||||
gb->mbc_ram_bank = bank;
|
||||
gb->mbc_ram_enable = true;
|
||||
if (GB_is_cgb(gb)) {
|
||||
gb->cgb_ram_bank = bank & 7;
|
||||
gb->cgb_vram_bank = bank & 1;
|
||||
@ -1530,58 +1522,8 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
||||
return true;
|
||||
}
|
||||
|
||||
const GB_cartridge_t *cartridge = gb->cartridge_type;
|
||||
|
||||
if (cartridge->has_ram) {
|
||||
GB_log(gb, "Cartridge includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size);
|
||||
}
|
||||
else {
|
||||
GB_log(gb, "No cartridge RAM\n");
|
||||
}
|
||||
|
||||
if (cartridge->mbc_type) {
|
||||
if (gb->is_mbc30) {
|
||||
GB_log(gb, "MBC30\n");
|
||||
}
|
||||
else {
|
||||
static const char *const mapper_names[] = {
|
||||
[GB_MBC1] = "MBC1",
|
||||
[GB_MBC2] = "MBC2",
|
||||
[GB_MBC3] = "MBC3",
|
||||
[GB_MBC5] = "MBC5",
|
||||
[GB_HUC1] = "HUC-1",
|
||||
[GB_HUC3] = "HUC-3",
|
||||
};
|
||||
GB_log(gb, "%s\n", mapper_names[cartridge->mbc_type]);
|
||||
}
|
||||
GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank);
|
||||
if (cartridge->has_ram) {
|
||||
GB_log(gb, "Current mapped RAM bank: %x\n", gb->mbc_ram_bank);
|
||||
if (gb->cartridge_type->mbc_type != GB_HUC1) {
|
||||
GB_log(gb, "RAM is curently %s\n", gb->mbc_ram_enable? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_STANDARD_MBC1_WIRING) {
|
||||
GB_log(gb, "MBC1 banking mode is %s\n", gb->mbc1.mode == 1 ? "RAM" : "ROM");
|
||||
}
|
||||
if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_MBC1M_WIRING) {
|
||||
GB_log(gb, "MBC1 uses MBC1M wiring. \n");
|
||||
GB_log(gb, "Current mapped ROM0 bank: %x\n", gb->mbc_rom0_bank);
|
||||
GB_log(gb, "MBC1 multicart banking mode is %s\n", gb->mbc1.mode == 1 ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
GB_log(gb, "No MBC\n");
|
||||
}
|
||||
|
||||
if (cartridge->has_rumble) {
|
||||
GB_log(gb, "Cart contains a Rumble Pak\n");
|
||||
}
|
||||
|
||||
if (cartridge->has_rtc) {
|
||||
GB_log(gb, "Cart contains a real time clock\n");
|
||||
}
|
||||
GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank);
|
||||
GB_log(gb, "Current mapped ROM0 bank: %x\n", gb->mbc_rom0_bank);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1662,13 +1604,13 @@ static bool lcd(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
||||
GB_log(gb, "LCDC:\n");
|
||||
GB_log(gb, " LCD enabled: %s\n",(gb->io_registers[GB_IO_LCDC] & 128)? "Enabled" : "Disabled");
|
||||
GB_log(gb, " %s: %s\n", (gb->cgb_mode? "Sprite priority flags" : "Background and Window"),
|
||||
(gb->io_registers[GB_IO_LCDC] & 1)? "Enabled" : "Disabled");
|
||||
GB_log(gb, " Objects: %s\n", (gb->io_registers[GB_IO_LCDC] & 2)? "Enabled" : "Disabled");
|
||||
GB_log(gb, " Object size: %s\n", (gb->io_registers[GB_IO_LCDC] & 4)? "8x16" : "8x8");
|
||||
GB_log(gb, " Background tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 8)? "$9C00" : "$9800");
|
||||
(gb->io_registers[GB_IO_LCDC] & 64)? "Enabled" : "Disabled");
|
||||
GB_log(gb, " Objects: %s\n", (gb->io_registers[GB_IO_LCDC] & 1)? "Enabled" : "Disabled");
|
||||
GB_log(gb, " Object size: %s\n", (gb->io_registers[GB_IO_LCDC] & 2)? "8x16" : "8x8");
|
||||
GB_log(gb, " Background tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 4)? "$9C00" : "$9800");
|
||||
GB_log(gb, " Background and Window Tileset: %s\n", (gb->io_registers[GB_IO_LCDC] & 16)? "$8000" : "$8800");
|
||||
GB_log(gb, " Window: %s\n", (gb->io_registers[GB_IO_LCDC] & 32)? "Enabled" : "Disabled");
|
||||
GB_log(gb, " Window tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 64)? "$9C00" : "$9800");
|
||||
GB_log(gb, " Window tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 8)? "$9C00" : "$9800");
|
||||
|
||||
GB_log(gb, "\nSTAT:\n");
|
||||
static const char *modes[] = {"Mode 0, H-Blank", "Mode 1, V-Blank", "Mode 2, OAM", "Mode 3, Rendering"};
|
||||
|
@ -415,7 +415,7 @@ static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
|
||||
|
||||
/* This reverse sorts the visible objects by location and priority */
|
||||
GB_object_t *objects = (GB_object_t *) &gb->oam;
|
||||
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0;
|
||||
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 2) != 0;
|
||||
signed y = objects[index].y - 16;
|
||||
if (y <= gb->current_line && y + (height_16? 16 : 8) > gb->current_line) {
|
||||
unsigned j = 0;
|
||||
@ -443,7 +443,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
||||
|
||||
if (fifo_size(&gb->oam_fifo)) {
|
||||
oam_fifo_item = fifo_pop(&gb->oam_fifo);
|
||||
if (oam_fifo_item->pixel && (gb->io_registers[GB_IO_LCDC] & 2)) {
|
||||
if (oam_fifo_item->pixel && (gb->io_registers[GB_IO_LCDC] & 1)) {
|
||||
draw_oam = true;
|
||||
bg_priority |= oam_fifo_item->bg_priority;
|
||||
}
|
||||
@ -461,7 +461,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
||||
|
||||
/* Mixing */
|
||||
|
||||
if ((gb->io_registers[GB_IO_LCDC] & 0x1) == 0) {
|
||||
if ((gb->io_registers[GB_IO_LCDC] & 0x40) == 0) {
|
||||
if (gb->cgb_mode) {
|
||||
bg_priority = false;
|
||||
}
|
||||
@ -583,10 +583,10 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
||||
}
|
||||
|
||||
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
|
||||
if (gb->io_registers[GB_IO_LCDC] & 0x08 && !gb->wx_triggered) {
|
||||
if (gb->io_registers[GB_IO_LCDC] & 0x04 && !gb->wx_triggered) {
|
||||
map = 0x1C00;
|
||||
}
|
||||
else if (gb->io_registers[GB_IO_LCDC] & 0x40 && gb->wx_triggered) {
|
||||
else if (gb->io_registers[GB_IO_LCDC] & 0x8 && gb->wx_triggered) {
|
||||
map = 0x1C00;
|
||||
}
|
||||
|
||||
@ -715,7 +715,7 @@ static uint16_t get_object_line_address(GB_gameboy_t *gb, const GB_object_t *obj
|
||||
object = &blocked;
|
||||
}
|
||||
|
||||
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; /* Todo: Which T-cycle actually reads this? */
|
||||
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 2) != 0; /* Todo: Which T-cycle actually reads this? */
|
||||
uint8_t tile_y = (gb->current_line - object->y) & (height_16? 0xF : 7);
|
||||
|
||||
if (object->flags & 0x40) { /* Flip Y */
|
||||
@ -1044,7 +1044,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||
|
||||
gb->during_object_fetch = true;
|
||||
while (gb->n_visible_objs != 0 &&
|
||||
(gb->io_registers[GB_IO_LCDC] & 2 || GB_is_cgb(gb)) &&
|
||||
(gb->io_registers[GB_IO_LCDC] & 1 || GB_is_cgb(gb)) &&
|
||||
gb->obj_comparators[gb->n_visible_objs - 1] == (uint8_t)(gb->position_in_line + 8)) {
|
||||
|
||||
while (gb->fetcher_state < 5 || fifo_size(&gb->bg_fifo) == 0) {
|
||||
@ -1376,7 +1376,7 @@ void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette
|
||||
break;
|
||||
}
|
||||
|
||||
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb->io_registers[GB_IO_LCDC] & 0x08)) {
|
||||
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb->io_registers[GB_IO_LCDC] & 0x04)) {
|
||||
map = 0x1c00;
|
||||
}
|
||||
|
||||
@ -1425,7 +1425,7 @@ void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette
|
||||
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height)
|
||||
{
|
||||
uint8_t count = 0;
|
||||
*sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8;
|
||||
*sprite_height = (gb->io_registers[GB_IO_LCDC] & 2) ? 16:8;
|
||||
uint8_t oam_to_dest_index[40] = {0,};
|
||||
for (unsigned y = 0; y < LINES; y++) {
|
||||
GB_object_t *sprite = (GB_object_t *) &gb->oam;
|
||||
|
393
Core/gb.c
393
Core/gb.c
@ -161,7 +161,6 @@ void GB_init(GB_gameboy_t *gb, GB_model_t model)
|
||||
gb->input_callback = default_input_callback;
|
||||
gb->async_input_callback = default_async_input_callback;
|
||||
#endif
|
||||
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
|
||||
gb->clock_multiplier = 1.0;
|
||||
|
||||
if (model & GB_MODEL_NO_SFC_BIT) {
|
||||
@ -187,9 +186,6 @@ void GB_free(GB_gameboy_t *gb)
|
||||
if (gb->vram) {
|
||||
free(gb->vram);
|
||||
}
|
||||
if (gb->mbc_ram) {
|
||||
free(gb->mbc_ram);
|
||||
}
|
||||
if (gb->rom) {
|
||||
free(gb->rom);
|
||||
}
|
||||
@ -245,7 +241,6 @@ void GB_borrow_sgb_border(GB_gameboy_t *gb)
|
||||
if (!gb->boot_rom_load_callback) return; // Can't borrow a border without this callback
|
||||
GB_gameboy_t sgb;
|
||||
GB_init(&sgb, GB_MODEL_SGB);
|
||||
sgb.cartridge_type = gb->cartridge_type;
|
||||
sgb.rom = gb->rom;
|
||||
sgb.rom_size = gb->rom_size;
|
||||
sgb.turbo = true;
|
||||
@ -301,7 +296,6 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path)
|
||||
memset(gb->rom, 0xFF, gb->rom_size); /* Pad with 0xFFs */
|
||||
fread(gb->rom, 1, gb->rom_size, f);
|
||||
fclose(f);
|
||||
GB_configure_cart(gb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -483,44 +477,7 @@ done:;
|
||||
memset(gb->rom + gb->rom_size, 0, needed_size - gb->rom_size);
|
||||
gb->rom_size = needed_size;
|
||||
}
|
||||
|
||||
GB_configure_cart(gb);
|
||||
|
||||
// Fix a common wrong MBC error
|
||||
if (gb->rom[0x147] == 3) { // MBC1 + RAM + Battery
|
||||
bool needs_fix = false;
|
||||
if (gb->rom_size >= 0x21 * 0x4000) {
|
||||
for (unsigned i = 0x20 * 0x4000; i < 0x21 * 0x4000; i++) {
|
||||
if (gb->rom[i]) {
|
||||
needs_fix = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!needs_fix && gb->rom_size >= 0x41 * 0x4000) {
|
||||
for (unsigned i = 0x40 * 0x4000; i < 0x41 * 0x4000; i++) {
|
||||
if (gb->rom[i]) {
|
||||
needs_fix = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!needs_fix && gb->rom_size >= 0x61 * 0x4000) {
|
||||
for (unsigned i = 0x60 * 0x4000; i < 0x61 * 0x4000; i++) {
|
||||
if (gb->rom[i]) {
|
||||
needs_fix = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needs_fix) {
|
||||
gb->rom[0x147] = 0x10; // MBC3 + RTC + RAM + Battery
|
||||
GB_configure_cart(gb);
|
||||
gb->rom[0x147] = 0x3;
|
||||
GB_log(gb, "ROM claims to use MBC1 but appears to require MBC3 or 5, assuming MBC3.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (old_rom) {
|
||||
free(old_rom);
|
||||
}
|
||||
@ -553,7 +510,6 @@ void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t siz
|
||||
gb->rom = malloc(gb->rom_size);
|
||||
memset(gb->rom, 0xff, gb->rom_size);
|
||||
memcpy(gb->rom, buffer, size);
|
||||
GB_configure_cart(gb);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@ -596,351 +552,27 @@ typedef union {
|
||||
|
||||
int GB_save_battery_size(GB_gameboy_t *gb)
|
||||
{
|
||||
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
|
||||
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
return gb->mbc_ram_size + sizeof(GB_huc3_rtc_time_t);
|
||||
}
|
||||
GB_rtc_save_t rtc_save_size;
|
||||
return gb->mbc_ram_size + (gb->cartridge_type->has_rtc ? sizeof(rtc_save_size.vba64) : 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size)
|
||||
{
|
||||
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
|
||||
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
|
||||
|
||||
if (size < GB_save_battery_size(gb)) return EIO;
|
||||
|
||||
memcpy(buffer, gb->mbc_ram, gb->mbc_ram_size);
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
buffer += gb->mbc_ram_size;
|
||||
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
GB_huc3_rtc_time_t rtc_save = {
|
||||
__builtin_bswap64(gb->last_rtc_second),
|
||||
__builtin_bswap16(gb->huc3_minutes),
|
||||
__builtin_bswap16(gb->huc3_days),
|
||||
__builtin_bswap16(gb->huc3_alarm_minutes),
|
||||
__builtin_bswap16(gb->huc3_alarm_days),
|
||||
gb->huc3_alarm_enabled,
|
||||
};
|
||||
#else
|
||||
GB_huc3_rtc_time_t rtc_save = {
|
||||
gb->last_rtc_second,
|
||||
gb->huc3_minutes,
|
||||
gb->huc3_days,
|
||||
gb->huc3_alarm_minutes,
|
||||
gb->huc3_alarm_days,
|
||||
gb->huc3_alarm_enabled,
|
||||
};
|
||||
#endif
|
||||
memcpy(buffer, &rtc_save, sizeof(rtc_save));
|
||||
}
|
||||
else if (gb->cartridge_type->has_rtc) {
|
||||
GB_rtc_save_t rtc_save = {{{{0,}},},};
|
||||
rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds;
|
||||
rtc_save.vba64.rtc_real.minutes = gb->rtc_real.minutes;
|
||||
rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours;
|
||||
rtc_save.vba64.rtc_real.days = gb->rtc_real.days;
|
||||
rtc_save.vba64.rtc_real.high = gb->rtc_real.high;
|
||||
rtc_save.vba64.rtc_latched.seconds = gb->rtc_latched.seconds;
|
||||
rtc_save.vba64.rtc_latched.minutes = gb->rtc_latched.minutes;
|
||||
rtc_save.vba64.rtc_latched.hours = gb->rtc_latched.hours;
|
||||
rtc_save.vba64.rtc_latched.days = gb->rtc_latched.days;
|
||||
rtc_save.vba64.rtc_latched.high = gb->rtc_latched.high;
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
rtc_save.vba64.last_rtc_second = __builtin_bswap64(gb->last_rtc_second);
|
||||
#else
|
||||
rtc_save.vba64.last_rtc_second = gb->last_rtc_second;
|
||||
#endif
|
||||
memcpy(buffer + gb->mbc_ram_size, &rtc_save.vba64, sizeof(rtc_save.vba64));
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
return errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GB_save_battery(GB_gameboy_t *gb, const char *path)
|
||||
{
|
||||
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
|
||||
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) {
|
||||
GB_log(gb, "Could not open battery save: %s.\n", strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
|
||||
fclose(f);
|
||||
return EIO;
|
||||
}
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
GB_huc3_rtc_time_t rtc_save = {
|
||||
__builtin_bswap64(gb->last_rtc_second),
|
||||
__builtin_bswap16(gb->huc3_minutes),
|
||||
__builtin_bswap16(gb->huc3_days),
|
||||
__builtin_bswap16(gb->huc3_alarm_minutes),
|
||||
__builtin_bswap16(gb->huc3_alarm_days),
|
||||
gb->huc3_alarm_enabled,
|
||||
};
|
||||
#else
|
||||
GB_huc3_rtc_time_t rtc_save = {
|
||||
gb->last_rtc_second,
|
||||
gb->huc3_minutes,
|
||||
gb->huc3_days,
|
||||
gb->huc3_alarm_minutes,
|
||||
gb->huc3_alarm_days,
|
||||
gb->huc3_alarm_enabled,
|
||||
};
|
||||
#endif
|
||||
|
||||
if (fwrite(&rtc_save, sizeof(rtc_save), 1, f) != 1) {
|
||||
fclose(f);
|
||||
return EIO;
|
||||
}
|
||||
}
|
||||
else if (gb->cartridge_type->has_rtc) {
|
||||
GB_rtc_save_t rtc_save = {{{{0,}},},};
|
||||
rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds;
|
||||
rtc_save.vba64.rtc_real.minutes = gb->rtc_real.minutes;
|
||||
rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours;
|
||||
rtc_save.vba64.rtc_real.days = gb->rtc_real.days;
|
||||
rtc_save.vba64.rtc_real.high = gb->rtc_real.high;
|
||||
rtc_save.vba64.rtc_latched.seconds = gb->rtc_latched.seconds;
|
||||
rtc_save.vba64.rtc_latched.minutes = gb->rtc_latched.minutes;
|
||||
rtc_save.vba64.rtc_latched.hours = gb->rtc_latched.hours;
|
||||
rtc_save.vba64.rtc_latched.days = gb->rtc_latched.days;
|
||||
rtc_save.vba64.rtc_latched.high = gb->rtc_latched.high;
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
rtc_save.vba64.last_rtc_second = __builtin_bswap64(gb->last_rtc_second);
|
||||
#else
|
||||
rtc_save.vba64.last_rtc_second = gb->last_rtc_second;
|
||||
#endif
|
||||
if (fwrite(&rtc_save.vba64, 1, sizeof(rtc_save.vba64), f) != sizeof(rtc_save.vba64)) {
|
||||
fclose(f);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
fclose(f);
|
||||
return errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size)
|
||||
{
|
||||
memcpy(gb->mbc_ram, buffer, MIN(gb->mbc_ram_size, size));
|
||||
if (size <= gb->mbc_ram_size) {
|
||||
goto reset_rtc;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
GB_huc3_rtc_time_t rtc_save;
|
||||
if (size - gb->mbc_ram_size < sizeof(rtc_save)) {
|
||||
goto reset_rtc;
|
||||
}
|
||||
memcpy(&rtc_save, buffer + gb->mbc_ram_size, sizeof(rtc_save));
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
|
||||
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes);
|
||||
gb->huc3_days = __builtin_bswap16(rtc_save.days);
|
||||
gb->huc3_alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes);
|
||||
gb->huc3_alarm_days = __builtin_bswap16(rtc_save.alarm_days);
|
||||
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
|
||||
#else
|
||||
gb->last_rtc_second = rtc_save.last_rtc_second;
|
||||
gb->huc3_minutes = rtc_save.minutes;
|
||||
gb->huc3_days = rtc_save.days;
|
||||
gb->huc3_alarm_minutes = rtc_save.alarm_minutes;
|
||||
gb->huc3_alarm_days = rtc_save.alarm_days;
|
||||
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
|
||||
#endif
|
||||
if (gb->last_rtc_second > time(NULL)) {
|
||||
/* We must reset RTC here, or it will not advance. */
|
||||
goto reset_rtc;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GB_rtc_save_t rtc_save;
|
||||
memcpy(&rtc_save, buffer + gb->mbc_ram_size, MIN(sizeof(rtc_save), size));
|
||||
switch (size - gb->mbc_ram_size) {
|
||||
case sizeof(rtc_save.sameboy_legacy):
|
||||
memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
|
||||
memcpy(&gb->rtc_latched, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
|
||||
gb->last_rtc_second = rtc_save.sameboy_legacy.last_rtc_second;
|
||||
break;
|
||||
|
||||
case sizeof(rtc_save.vba32):
|
||||
gb->rtc_real.seconds = rtc_save.vba32.rtc_real.seconds;
|
||||
gb->rtc_real.minutes = rtc_save.vba32.rtc_real.minutes;
|
||||
gb->rtc_real.hours = rtc_save.vba32.rtc_real.hours;
|
||||
gb->rtc_real.days = rtc_save.vba32.rtc_real.days;
|
||||
gb->rtc_real.high = rtc_save.vba32.rtc_real.high;
|
||||
gb->rtc_latched.seconds = rtc_save.vba32.rtc_latched.seconds;
|
||||
gb->rtc_latched.minutes = rtc_save.vba32.rtc_latched.minutes;
|
||||
gb->rtc_latched.hours = rtc_save.vba32.rtc_latched.hours;
|
||||
gb->rtc_latched.days = rtc_save.vba32.rtc_latched.days;
|
||||
gb->rtc_latched.high = rtc_save.vba32.rtc_latched.high;
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
gb->last_rtc_second = __builtin_bswap32(rtc_save.vba32.last_rtc_second);
|
||||
#else
|
||||
gb->last_rtc_second = rtc_save.vba32.last_rtc_second;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case sizeof(rtc_save.vba64):
|
||||
gb->rtc_real.seconds = rtc_save.vba64.rtc_real.seconds;
|
||||
gb->rtc_real.minutes = rtc_save.vba64.rtc_real.minutes;
|
||||
gb->rtc_real.hours = rtc_save.vba64.rtc_real.hours;
|
||||
gb->rtc_real.days = rtc_save.vba64.rtc_real.days;
|
||||
gb->rtc_real.high = rtc_save.vba64.rtc_real.high;
|
||||
gb->rtc_latched.seconds = rtc_save.vba64.rtc_latched.seconds;
|
||||
gb->rtc_latched.minutes = rtc_save.vba64.rtc_latched.minutes;
|
||||
gb->rtc_latched.hours = rtc_save.vba64.rtc_latched.hours;
|
||||
gb->rtc_latched.days = rtc_save.vba64.rtc_latched.days;
|
||||
gb->rtc_latched.high = rtc_save.vba64.rtc_latched.high;
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
gb->last_rtc_second = __builtin_bswap64(rtc_save.vba64.last_rtc_second);
|
||||
#else
|
||||
gb->last_rtc_second = rtc_save.vba64.last_rtc_second;
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
goto reset_rtc;
|
||||
}
|
||||
if (gb->last_rtc_second > time(NULL)) {
|
||||
/* We must reset RTC here, or it will not advance. */
|
||||
goto reset_rtc;
|
||||
}
|
||||
|
||||
if (gb->last_rtc_second < 852076800) { /* 1/1/97. There weren't any RTC games that time,
|
||||
so if the value we read is lower it means it wasn't
|
||||
really RTC data. */
|
||||
goto reset_rtc;
|
||||
}
|
||||
goto exit;
|
||||
reset_rtc:
|
||||
gb->last_rtc_second = time(NULL);
|
||||
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
|
||||
gb->huc3_days = 0xFFFF;
|
||||
gb->huc3_minutes = 0xFFF;
|
||||
gb->huc3_alarm_enabled = false;
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Loading will silently stop if the format is incomplete */
|
||||
void GB_load_battery(GB_gameboy_t *gb, const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fread(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
|
||||
goto reset_rtc;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
GB_huc3_rtc_time_t rtc_save;
|
||||
if (fread(&rtc_save, sizeof(rtc_save), 1, f) != 1) {
|
||||
goto reset_rtc;
|
||||
}
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
|
||||
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes);
|
||||
gb->huc3_days = __builtin_bswap16(rtc_save.days);
|
||||
gb->huc3_alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes);
|
||||
gb->huc3_alarm_days = __builtin_bswap16(rtc_save.alarm_days);
|
||||
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
|
||||
#else
|
||||
gb->last_rtc_second = rtc_save.last_rtc_second;
|
||||
gb->huc3_minutes = rtc_save.minutes;
|
||||
gb->huc3_days = rtc_save.days;
|
||||
gb->huc3_alarm_minutes = rtc_save.alarm_minutes;
|
||||
gb->huc3_alarm_days = rtc_save.alarm_days;
|
||||
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
|
||||
#endif
|
||||
if (gb->last_rtc_second > time(NULL)) {
|
||||
/* We must reset RTC here, or it will not advance. */
|
||||
goto reset_rtc;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GB_rtc_save_t rtc_save;
|
||||
switch (fread(&rtc_save, 1, sizeof(rtc_save), f)) {
|
||||
case sizeof(rtc_save.sameboy_legacy):
|
||||
memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
|
||||
memcpy(&gb->rtc_latched, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
|
||||
gb->last_rtc_second = rtc_save.sameboy_legacy.last_rtc_second;
|
||||
break;
|
||||
|
||||
case sizeof(rtc_save.vba32):
|
||||
gb->rtc_real.seconds = rtc_save.vba32.rtc_real.seconds;
|
||||
gb->rtc_real.minutes = rtc_save.vba32.rtc_real.minutes;
|
||||
gb->rtc_real.hours = rtc_save.vba32.rtc_real.hours;
|
||||
gb->rtc_real.days = rtc_save.vba32.rtc_real.days;
|
||||
gb->rtc_real.high = rtc_save.vba32.rtc_real.high;
|
||||
gb->rtc_latched.seconds = rtc_save.vba32.rtc_latched.seconds;
|
||||
gb->rtc_latched.minutes = rtc_save.vba32.rtc_latched.minutes;
|
||||
gb->rtc_latched.hours = rtc_save.vba32.rtc_latched.hours;
|
||||
gb->rtc_latched.days = rtc_save.vba32.rtc_latched.days;
|
||||
gb->rtc_latched.high = rtc_save.vba32.rtc_latched.high;
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
gb->last_rtc_second = __builtin_bswap32(rtc_save.vba32.last_rtc_second);
|
||||
#else
|
||||
gb->last_rtc_second = rtc_save.vba32.last_rtc_second;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case sizeof(rtc_save.vba64):
|
||||
gb->rtc_real.seconds = rtc_save.vba64.rtc_real.seconds;
|
||||
gb->rtc_real.minutes = rtc_save.vba64.rtc_real.minutes;
|
||||
gb->rtc_real.hours = rtc_save.vba64.rtc_real.hours;
|
||||
gb->rtc_real.days = rtc_save.vba64.rtc_real.days;
|
||||
gb->rtc_real.high = rtc_save.vba64.rtc_real.high;
|
||||
gb->rtc_latched.seconds = rtc_save.vba64.rtc_latched.seconds;
|
||||
gb->rtc_latched.minutes = rtc_save.vba64.rtc_latched.minutes;
|
||||
gb->rtc_latched.hours = rtc_save.vba64.rtc_latched.hours;
|
||||
gb->rtc_latched.days = rtc_save.vba64.rtc_latched.days;
|
||||
gb->rtc_latched.high = rtc_save.vba64.rtc_latched.high;
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
gb->last_rtc_second = __builtin_bswap64(rtc_save.vba64.last_rtc_second);
|
||||
#else
|
||||
gb->last_rtc_second = rtc_save.vba64.last_rtc_second;
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
goto reset_rtc;
|
||||
}
|
||||
if (gb->last_rtc_second > time(NULL)) {
|
||||
/* We must reset RTC here, or it will not advance. */
|
||||
goto reset_rtc;
|
||||
}
|
||||
|
||||
if (gb->last_rtc_second < 852076800) { /* 1/1/97. There weren't any RTC games that time,
|
||||
so if the value we read is lower it means it wasn't
|
||||
really RTC data. */
|
||||
goto reset_rtc;
|
||||
}
|
||||
goto exit;
|
||||
reset_rtc:
|
||||
gb->last_rtc_second = time(NULL);
|
||||
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
|
||||
gb->huc3_days = 0xFFFF;
|
||||
gb->huc3_minutes = 0xFFF;
|
||||
gb->huc3_alarm_enabled = false;
|
||||
exit:
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -964,7 +596,6 @@ uint8_t GB_run(GB_gameboy_t *gb)
|
||||
gb->cycles_since_run = 0;
|
||||
GB_cpu_run(gb);
|
||||
if (gb->vblank_just_occured) {
|
||||
GB_rtc_run(gb);
|
||||
GB_debugger_handle_async_commands(gb);
|
||||
GB_rewind_push(gb);
|
||||
}
|
||||
@ -1361,7 +992,6 @@ static void request_boot_rom(GB_gameboy_t *gb)
|
||||
|
||||
void GB_reset(GB_gameboy_t *gb)
|
||||
{
|
||||
uint32_t mbc_ram_size = gb->mbc_ram_size;
|
||||
GB_model_t model = gb->model;
|
||||
memset(gb, 0, (size_t)GB_GET_SECTION((GB_gameboy_t *) 0, unsaved));
|
||||
gb->model = model;
|
||||
@ -1371,7 +1001,6 @@ void GB_reset(GB_gameboy_t *gb)
|
||||
gb->last_rtc_second = time(NULL);
|
||||
gb->cgb_ram_bank = 1;
|
||||
gb->io_registers[GB_IO_JOYP] = 0xCF;
|
||||
gb->mbc_ram_size = mbc_ram_size;
|
||||
if (GB_is_cgb(gb)) {
|
||||
gb->ram_size = 0x1000 * 8;
|
||||
gb->vram_size = 0x2000 * 2;
|
||||
@ -1474,9 +1103,9 @@ void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *
|
||||
*bank = gb->cgb_ram_bank;
|
||||
return gb->ram;
|
||||
case GB_DIRECT_ACCESS_CART_RAM:
|
||||
*size = gb->mbc_ram_size;
|
||||
*bank = gb->mbc_ram_bank;
|
||||
return gb->mbc_ram;
|
||||
*size = 0;
|
||||
*bank = 0;
|
||||
return NULL;
|
||||
case GB_DIRECT_ACCESS_VRAM:
|
||||
*size = gb->vram_size;
|
||||
*bank = gb->cgb_vram_bank;
|
||||
@ -1609,11 +1238,5 @@ 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)
|
||||
{
|
||||
if (gb->cartridge_type->mbc_type != GB_HUC3) return 0;
|
||||
if (!gb->huc3_alarm_enabled) 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 alarm_time = (gb->huc3_alarm_days & 0x1FFF) * 24 * 60 * 60 + gb->huc3_alarm_minutes * 60;
|
||||
if (current_time > alarm_time) return 0;
|
||||
return alarm_time - current_time;
|
||||
return 0;
|
||||
}
|
||||
|
113
Core/gb.h
113
Core/gb.h
@ -13,7 +13,6 @@
|
||||
#include "debugger.h"
|
||||
#include "display.h"
|
||||
#include "joypad.h"
|
||||
#include "mbc.h"
|
||||
#include "memory.h"
|
||||
#include "printer.h"
|
||||
#include "timing.h"
|
||||
@ -128,66 +127,52 @@ typedef enum {
|
||||
#define GB_MAX_IR_QUEUE 256
|
||||
|
||||
enum {
|
||||
/* Joypad and Serial */
|
||||
GB_IO_JOYP = 0x00, // Joypad (R/W)
|
||||
GB_IO_SB = 0x01, // Serial transfer data (R/W)
|
||||
GB_IO_SC = 0x02, // Serial Transfer Control (R/W)
|
||||
|
||||
/* Missing */
|
||||
GB_IO_JOYP = 0x00,
|
||||
GB_IO_SB = 0x01,
|
||||
GB_IO_SC = 0x02,
|
||||
GB_IO_DIV = 0x04,
|
||||
GB_IO_TIMA = 0x05,
|
||||
GB_IO_TMA = 0x06,
|
||||
GB_IO_TAC = 0x07,
|
||||
GB_IO_IF = 0x0F,
|
||||
GB_IO_LCDC = 0x10,
|
||||
GB_IO_STAT = 0x11,
|
||||
GB_IO_SCY = 0x12,
|
||||
GB_IO_SCX = 0x13,
|
||||
GB_IO_OBP0 = 0x14,
|
||||
GB_IO_OBP1 = 0x15,
|
||||
GB_IO_WY = 0x16,
|
||||
GB_IO_WX = 0x17,
|
||||
GB_IO_LY = 0x18,
|
||||
GB_IO_LYC = 0x19,
|
||||
GB_IO_DMA = 0x1A,
|
||||
GB_IO_BGP = 0x1B,
|
||||
GB_IO_NR10 = 0x20,
|
||||
GB_IO_NR12 = 0x21,
|
||||
GB_IO_NR11 = 0x22,
|
||||
GB_IO_NR13 = 0x23,
|
||||
GB_IO_NR14 = 0x24,
|
||||
GB_IO_NR21 = 0x25,
|
||||
GB_IO_NR22 = 0x27,
|
||||
GB_IO_NR23 = 0x28,
|
||||
GB_IO_NR24 = 0x29,
|
||||
GB_IO_NR30 = 0x2A,
|
||||
GB_IO_NR31 = 0x2B,
|
||||
GB_IO_NR32 = 0x2C,
|
||||
GB_IO_NR33 = 0x2D,
|
||||
GB_IO_NR34 = 0x2E,
|
||||
GB_IO_WAV_START = 0x30,
|
||||
GB_IO_WAV_END = 0x3F,
|
||||
GB_IO_NR41 = 0x40,
|
||||
GB_IO_NR43 = 0x41,
|
||||
GB_IO_NR42 = 0x42,
|
||||
GB_IO_NR44 = 0x43,
|
||||
GB_IO_NR50 = 0x44,
|
||||
GB_IO_NR52 = 0x45,
|
||||
GB_IO_NR51 = 0x46,
|
||||
GB_IO_IE = 0xFF,
|
||||
|
||||
/* Timers */
|
||||
GB_IO_DIV = 0x04, // Divider Register (R/W)
|
||||
GB_IO_TIMA = 0x05, // Timer counter (R/W)
|
||||
GB_IO_TMA = 0x06, // Timer Modulo (R/W)
|
||||
GB_IO_TAC = 0x07, // Timer Control (R/W)
|
||||
|
||||
/* Missing */
|
||||
|
||||
GB_IO_IF = 0x0f, // Interrupt Flag (R/W)
|
||||
|
||||
/* Sound */
|
||||
GB_IO_NR10 = 0x10, // Channel 1 Sweep register (R/W)
|
||||
GB_IO_NR11 = 0x11, // Channel 1 Sound length/Wave pattern duty (R/W)
|
||||
GB_IO_NR12 = 0x12, // Channel 1 Volume Envelope (R/W)
|
||||
GB_IO_NR13 = 0x13, // Channel 1 Frequency lo (Write Only)
|
||||
GB_IO_NR14 = 0x14, // Channel 1 Frequency hi (R/W)
|
||||
/* NR20 does not exist */
|
||||
GB_IO_NR21 = 0x16, // Channel 2 Sound Length/Wave Pattern Duty (R/W)
|
||||
GB_IO_NR22 = 0x17, // Channel 2 Volume Envelope (R/W)
|
||||
GB_IO_NR23 = 0x18, // Channel 2 Frequency lo data (W)
|
||||
GB_IO_NR24 = 0x19, // Channel 2 Frequency hi data (R/W)
|
||||
GB_IO_NR30 = 0x1a, // Channel 3 Sound on/off (R/W)
|
||||
GB_IO_NR31 = 0x1b, // Channel 3 Sound Length
|
||||
GB_IO_NR32 = 0x1c, // Channel 3 Select output level (R/W)
|
||||
GB_IO_NR33 = 0x1d, // Channel 3 Frequency's lower data (W)
|
||||
GB_IO_NR34 = 0x1e, // Channel 3 Frequency's higher data (R/W)
|
||||
/* NR40 does not exist */
|
||||
GB_IO_NR41 = 0x20, // Channel 4 Sound Length (R/W)
|
||||
GB_IO_NR42 = 0x21, // Channel 4 Volume Envelope (R/W)
|
||||
GB_IO_NR43 = 0x22, // Channel 4 Polynomial Counter (R/W)
|
||||
GB_IO_NR44 = 0x23, // Channel 4 Counter/consecutive, Inital (R/W)
|
||||
GB_IO_NR50 = 0x24, // Channel control / ON-OFF / Volume (R/W)
|
||||
GB_IO_NR51 = 0x25, // Selection of Sound output terminal (R/W)
|
||||
GB_IO_NR52 = 0x26, // Sound on/off
|
||||
|
||||
/* Missing */
|
||||
|
||||
GB_IO_WAV_START = 0x30, // Wave pattern start
|
||||
GB_IO_WAV_END = 0x3f, // Wave pattern end
|
||||
|
||||
/* Graphics */
|
||||
GB_IO_LCDC = 0x40, // LCD Control (R/W)
|
||||
GB_IO_STAT = 0x41, // LCDC Status (R/W)
|
||||
GB_IO_SCY = 0x42, // Scroll Y (R/W)
|
||||
GB_IO_SCX = 0x43, // Scroll X (R/W)
|
||||
GB_IO_LY = 0x44, // LCDC Y-Coordinate (R)
|
||||
GB_IO_LYC = 0x45, // LY Compare (R/W)
|
||||
GB_IO_DMA = 0x46, // DMA Transfer and Start Address (W)
|
||||
GB_IO_BGP = 0x47, // BG Palette Data (R/W) - Non CGB Mode Only
|
||||
GB_IO_OBP0 = 0x48, // Object Palette 0 Data (R/W) - Non CGB Mode Only
|
||||
GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only
|
||||
GB_IO_WY = 0x4a, // Window Y Position (R/W)
|
||||
GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W)
|
||||
// Has some undocumented compatibility flags written at boot.
|
||||
// Unfortunately it is not readable or writable after boot has finished, so research of this
|
||||
// register is quite limited. The value written to this register, however, can be controlled
|
||||
@ -396,9 +381,6 @@ struct GB_gameboy_internal_s {
|
||||
/* MBC */
|
||||
GB_SECTION(mbc,
|
||||
uint16_t mbc_rom_bank;
|
||||
uint8_t mbc_ram_bank;
|
||||
uint32_t mbc_ram_size;
|
||||
bool mbc_ram_enable;
|
||||
union {
|
||||
struct {
|
||||
uint8_t bank_low:5;
|
||||
@ -556,19 +538,12 @@ struct GB_gameboy_internal_s {
|
||||
/* ROM */
|
||||
uint8_t *rom;
|
||||
uint32_t rom_size;
|
||||
const GB_cartridge_t *cartridge_type;
|
||||
enum {
|
||||
GB_STANDARD_MBC1_WIRING,
|
||||
GB_MBC1M_WIRING,
|
||||
} mbc1_wiring;
|
||||
bool is_mbc30;
|
||||
|
||||
unsigned pending_cycles;
|
||||
|
||||
/* Various RAMs */
|
||||
uint8_t *ram;
|
||||
uint8_t *vram;
|
||||
uint8_t *mbc_ram;
|
||||
|
||||
/* I/O */
|
||||
uint32_t *screen;
|
||||
|
167
Core/mbc.c
167
Core/mbc.c
@ -1,167 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "gb.h"
|
||||
|
||||
const GB_cartridge_t GB_cart_defs[256] = {
|
||||
// From http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header#0147_-_Cartridge_Type
|
||||
/* MBC SUBTYPE RAM BAT. RTC RUMB. */
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 00h ROM ONLY
|
||||
{ GB_MBC1 , GB_STANDARD_MBC, false, false, false, false}, // 01h MBC1
|
||||
{ GB_MBC1 , GB_STANDARD_MBC, true , false, false, false}, // 02h MBC1+RAM
|
||||
{ GB_MBC1 , GB_STANDARD_MBC, true , true , false, false}, // 03h MBC1+RAM+BATTERY
|
||||
[5] =
|
||||
{ GB_MBC2 , GB_STANDARD_MBC, true , false, false, false}, // 05h MBC2
|
||||
{ GB_MBC2 , GB_STANDARD_MBC, true , true , false, false}, // 06h MBC2+BATTERY
|
||||
[8] =
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, true , false, false, false}, // 08h ROM+RAM
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, true , true , false, false}, // 09h ROM+RAM+BATTERY
|
||||
[0xB] =
|
||||
/* Todo: Not supported yet */
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Bh MMM01
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Ch MMM01+RAM
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Dh MMM01+RAM+BATTERY
|
||||
[0xF] =
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, false, true, true , false}, // 0Fh MBC3+TIMER+BATTERY
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, true , true, true , false}, // 10h MBC3+TIMER+RAM+BATTERY
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, false, false, false, false}, // 11h MBC3
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, true , false, false, false}, // 12h MBC3+RAM
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, true , true , false, false}, // 13h MBC3+RAM+BATTERY
|
||||
[0x19] =
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, false}, // 19h MBC5
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, false}, // 1Ah MBC5+RAM
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, false}, // 1Bh MBC5+RAM+BATTERY
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY
|
||||
[0xFC] =
|
||||
{ GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported)
|
||||
{ GB_HUC3 , GB_STANDARD_MBC, true , true , true, false}, // FEh HuC3
|
||||
{ GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY
|
||||
};
|
||||
|
||||
void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
||||
{
|
||||
switch (gb->cartridge_type->mbc_type) {
|
||||
case GB_NO_MBC: return;
|
||||
case GB_MBC1:
|
||||
switch (gb->mbc1_wiring) {
|
||||
case GB_STANDARD_MBC1_WIRING:
|
||||
gb->mbc_rom_bank = gb->mbc1.bank_low | (gb->mbc1.bank_high << 5);
|
||||
if (gb->mbc1.mode == 0) {
|
||||
gb->mbc_ram_bank = 0;
|
||||
gb->mbc_rom0_bank = 0;
|
||||
}
|
||||
else {
|
||||
gb->mbc_ram_bank = gb->mbc1.bank_high;
|
||||
gb->mbc_rom0_bank = gb->mbc1.bank_high << 5;
|
||||
}
|
||||
if ((gb->mbc_rom_bank & 0x1F) == 0) {
|
||||
gb->mbc_rom_bank++;
|
||||
}
|
||||
break;
|
||||
case GB_MBC1M_WIRING:
|
||||
gb->mbc_rom_bank = (gb->mbc1.bank_low & 0xF) | (gb->mbc1.bank_high << 4);
|
||||
if (gb->mbc1.mode == 0) {
|
||||
gb->mbc_ram_bank = 0;
|
||||
gb->mbc_rom0_bank = 0;
|
||||
}
|
||||
else {
|
||||
gb->mbc_rom0_bank = gb->mbc1.bank_high << 4;
|
||||
gb->mbc_ram_bank = 0;
|
||||
}
|
||||
if ((gb->mbc1.bank_low & 0x1F) == 0) {
|
||||
gb->mbc_rom_bank++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GB_MBC2:
|
||||
gb->mbc_rom_bank = gb->mbc2.rom_bank;
|
||||
if ((gb->mbc_rom_bank & 0xF) == 0) {
|
||||
gb->mbc_rom_bank = 1;
|
||||
}
|
||||
break;
|
||||
case GB_MBC3:
|
||||
gb->mbc_rom_bank = gb->mbc3.rom_bank;
|
||||
gb->mbc_ram_bank = gb->mbc3.ram_bank;
|
||||
if (!gb->is_mbc30) {
|
||||
gb->mbc_rom_bank &= 0x7F;
|
||||
}
|
||||
if (gb->mbc_rom_bank == 0) {
|
||||
gb->mbc_rom_bank = 1;
|
||||
}
|
||||
break;
|
||||
case GB_MBC5:
|
||||
gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8);
|
||||
gb->mbc_ram_bank = gb->mbc5.ram_bank;
|
||||
break;
|
||||
case GB_HUC1:
|
||||
if (gb->huc1.mode == 0) {
|
||||
gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6);
|
||||
gb->mbc_ram_bank = 0;
|
||||
}
|
||||
else {
|
||||
gb->mbc_rom_bank = gb->huc1.bank_low;
|
||||
gb->mbc_ram_bank = gb->huc1.bank_high;
|
||||
}
|
||||
break;
|
||||
case GB_HUC3:
|
||||
gb->mbc_rom_bank = gb->huc3.rom_bank;
|
||||
gb->mbc_ram_bank = gb->huc3.ram_bank;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GB_configure_cart(GB_gameboy_t *gb)
|
||||
{
|
||||
gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]];
|
||||
|
||||
if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {
|
||||
GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n");
|
||||
gb->cartridge_type = &GB_cart_defs[0x11];
|
||||
}
|
||||
else if (gb->rom[0x147] != 0 && memcmp(gb->cartridge_type, &GB_cart_defs[0], sizeof(GB_cart_defs[0])) == 0) {
|
||||
GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]);
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->has_ram) {
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC2) {
|
||||
gb->mbc_ram_size = 0x200;
|
||||
}
|
||||
else {
|
||||
static const unsigned ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
|
||||
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
|
||||
}
|
||||
|
||||
if (gb->mbc_ram_size) {
|
||||
gb->mbc_ram = malloc(gb->mbc_ram_size);
|
||||
}
|
||||
|
||||
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */
|
||||
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
|
||||
}
|
||||
|
||||
/* MBC1 has at least 3 types of wiring (We currently support two (Standard and 4bit-MBC1M) of these).
|
||||
See http://forums.nesdev.com/viewtopic.php?f=20&t=14099 */
|
||||
|
||||
/* Attempt to "guess" wiring */
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC1) {
|
||||
if (gb->rom_size >= 0x44000 && memcmp(gb->rom + 0x104, gb->rom + 0x40104, 0x30) == 0) {
|
||||
gb->mbc1_wiring = GB_MBC1M_WIRING;
|
||||
}
|
||||
}
|
||||
|
||||
/* Detect MBC30 */
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC3) {
|
||||
if (gb->rom_size > 0x200000 || gb->mbc_ram_size > 0x8000) {
|
||||
gb->is_mbc30 = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set MBC5's bank to 1 correctly */
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC5) {
|
||||
gb->mbc5.rom_bank_low = 1;
|
||||
}
|
||||
}
|
32
Core/mbc.h
32
Core/mbc.h
@ -1,32 +0,0 @@
|
||||
#ifndef MBC_h
|
||||
#define MBC_h
|
||||
#include "gb_struct_def.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
GB_NO_MBC,
|
||||
GB_MBC1,
|
||||
GB_MBC2,
|
||||
GB_MBC3,
|
||||
GB_MBC5,
|
||||
GB_HUC1,
|
||||
GB_HUC3,
|
||||
} mbc_type;
|
||||
enum {
|
||||
GB_STANDARD_MBC,
|
||||
GB_CAMERA,
|
||||
} mbc_subtype;
|
||||
bool has_ram;
|
||||
bool has_battery;
|
||||
bool has_rtc;
|
||||
bool has_rumble;
|
||||
} GB_cartridge_t;
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
extern const GB_cartridge_t GB_cart_defs[256];
|
||||
void GB_update_mbc_mappings(GB_gameboy_t *gb);
|
||||
void GB_configure_cart(GB_gameboy_t *gb);
|
||||
#endif
|
||||
|
||||
#endif /* MBC_h */
|
274
Core/memory.c
274
Core/memory.c
@ -56,9 +56,6 @@ void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address)
|
||||
gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch(gb->oam[gb->accessed_oam_row + 1],
|
||||
gb->oam[gb->accessed_oam_row - 7],
|
||||
gb->oam[gb->accessed_oam_row - 3]);
|
||||
for (unsigned i = 2; i < 8; i++) {
|
||||
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,14 +117,6 @@ static bool effective_ir_input(GB_gameboy_t *gb)
|
||||
|
||||
static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr)
|
||||
{
|
||||
if (addr < 0x100 && !gb->boot_rom_finished) {
|
||||
return gb->boot_rom[addr];
|
||||
}
|
||||
|
||||
if (addr >= 0x200 && addr < 0x900 && GB_is_cgb(gb) && !gb->boot_rom_finished) {
|
||||
return gb->boot_rom[addr];
|
||||
}
|
||||
|
||||
if (!gb->rom_size) {
|
||||
return 0xFF;
|
||||
}
|
||||
@ -163,65 +152,7 @@ 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)
|
||||
{
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
switch (gb->huc3_mode) {
|
||||
case 0xC: // RTC read
|
||||
if (gb->huc3_access_flags == 0x2) {
|
||||
return 1;
|
||||
}
|
||||
return gb->huc3_read;
|
||||
case 0xD: // RTC status
|
||||
return 1;
|
||||
case 0xE: // IR mode
|
||||
return effective_ir_input(gb); // TODO: What are the other bits?
|
||||
default:
|
||||
GB_log(gb, "Unsupported HuC-3 mode %x read: %04x\n", gb->huc3_mode, addr);
|
||||
return 1; // TODO: What happens in this case?
|
||||
case 0: // TODO: R/O RAM? (or is it disabled?)
|
||||
case 0xA: // RAM
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!gb->mbc_ram_enable) &&
|
||||
gb->cartridge_type->mbc_subtype != GB_CAMERA &&
|
||||
gb->cartridge_type->mbc_type != GB_HUC1 &&
|
||||
gb->cartridge_type->mbc_type != GB_HUC3) {
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) {
|
||||
return 0xc0 | effective_ir_input(gb);
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3 &&
|
||||
gb->mbc3_rtc_mapped && gb->mbc_ram_bank <= 4) {
|
||||
/* RTC read */
|
||||
gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */
|
||||
return gb->rtc_latched.data[gb->mbc_ram_bank];
|
||||
}
|
||||
|
||||
if (gb->camera_registers_mapped) {
|
||||
return GB_camera_read_register(gb, addr);
|
||||
}
|
||||
|
||||
if (!gb->mbc_ram || !gb->mbc_ram_size) {
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->mbc_subtype == GB_CAMERA && gb->mbc_ram_bank == 0 && addr >= 0xa100 && addr < 0xaf00) {
|
||||
return GB_camera_read_image(gb, addr - 0xa100);
|
||||
}
|
||||
|
||||
uint8_t effective_bank = gb->mbc_ram_bank;
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC3 && !gb->is_mbc30) {
|
||||
effective_bank &= 0x3;
|
||||
}
|
||||
uint8_t ret = gb->mbc_ram[((addr & 0x1FFF) + effective_bank * 0x2000) & (gb->mbc_ram_size - 1)];
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC2) {
|
||||
ret |= 0xF0;
|
||||
}
|
||||
return ret;
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static uint8_t read_ram(GB_gameboy_t *gb, uint16_t addr)
|
||||
@ -442,7 +373,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
case GB_IO_UNKNOWN5:
|
||||
return GB_is_cgb(gb)? gb->io_registers[addr & 0xFF] | 0x8F : 0xFF;
|
||||
default:
|
||||
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) {
|
||||
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_NR51) {
|
||||
return GB_apu_read(gb, addr & 0xFF);
|
||||
}
|
||||
return 0xFF;
|
||||
@ -493,75 +424,9 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
|
||||
static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
{
|
||||
switch (gb->cartridge_type->mbc_type) {
|
||||
case GB_NO_MBC: return;
|
||||
case GB_MBC1:
|
||||
switch (addr & 0xF000) {
|
||||
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
||||
case 0x2000: case 0x3000: gb->mbc1.bank_low = value; break;
|
||||
case 0x4000: case 0x5000: gb->mbc1.bank_high = value; break;
|
||||
case 0x6000: case 0x7000: gb->mbc1.mode = value; break;
|
||||
}
|
||||
break;
|
||||
case GB_MBC2:
|
||||
switch (addr & 0x4100) {
|
||||
case 0x0000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
||||
case 0x0100: gb->mbc2.rom_bank = value; break;
|
||||
}
|
||||
break;
|
||||
case GB_MBC3:
|
||||
switch (addr & 0xF000) {
|
||||
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
||||
case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break;
|
||||
case 0x4000: case 0x5000:
|
||||
gb->mbc3.ram_bank = value;
|
||||
gb->mbc3_rtc_mapped = value & 8;
|
||||
break;
|
||||
case 0x6000: case 0x7000:
|
||||
if (!gb->rtc_latch && (value & 1)) { /* Todo: verify condition is correct */
|
||||
memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real));
|
||||
}
|
||||
gb->rtc_latch = value & 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GB_MBC5:
|
||||
switch (addr & 0xF000) {
|
||||
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
||||
case 0x2000: gb->mbc5.rom_bank_low = value; break;
|
||||
case 0x3000: gb->mbc5.rom_bank_high = value; break;
|
||||
case 0x4000: case 0x5000:
|
||||
if (gb->cartridge_type->has_rumble) {
|
||||
if (!!(value & 8) != gb->rumble_state) {
|
||||
gb->rumble_state = !gb->rumble_state;
|
||||
}
|
||||
value &= 7;
|
||||
}
|
||||
gb->mbc5.ram_bank = value;
|
||||
gb->camera_registers_mapped = (value & 0x10) && gb->cartridge_type->mbc_subtype == GB_CAMERA;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GB_HUC1:
|
||||
switch (addr & 0xF000) {
|
||||
case 0x0000: case 0x1000: gb->huc1.ir_mode = (value & 0xF) == 0xE; break;
|
||||
case 0x2000: case 0x3000: gb->huc1.bank_low = value; break;
|
||||
case 0x4000: case 0x5000: gb->huc1.bank_high = value; break;
|
||||
case 0x6000: case 0x7000: gb->huc1.mode = value; break;
|
||||
}
|
||||
break;
|
||||
case GB_HUC3:
|
||||
switch (addr & 0xF000) {
|
||||
case 0x0000: case 0x1000:
|
||||
gb->huc3_mode = value & 0xF;
|
||||
gb->mbc_ram_enable = gb->huc3_mode == 0xA;
|
||||
break;
|
||||
case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break;
|
||||
case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break;
|
||||
}
|
||||
break;
|
||||
if (addr == 0x0001) {
|
||||
gb->mbc_rom_bank = value;
|
||||
}
|
||||
GB_update_mbc_mappings(gb);
|
||||
}
|
||||
|
||||
static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
@ -586,135 +451,12 @@ static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
|
||||
}
|
||||
|
||||
static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
|
||||
{
|
||||
switch (gb->huc3_mode) {
|
||||
case 0xB: // RTC Write
|
||||
switch (value >> 4) {
|
||||
case 1:
|
||||
if (gb->huc3_access_index < 3) {
|
||||
gb->huc3_read = (gb->huc3_minutes >> (gb->huc3_access_index * 4)) & 0xF;
|
||||
}
|
||||
else if (gb->huc3_access_index < 7) {
|
||||
gb->huc3_read = (gb->huc3_days >> ((gb->huc3_access_index - 3) * 4)) & 0xF;
|
||||
}
|
||||
else {
|
||||
// GB_log(gb, "Attempting to read from unsupported HuC-3 register: %03x\n", gb->huc3_access_index);
|
||||
}
|
||||
gb->huc3_access_index++;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if (gb->huc3_access_index < 3) {
|
||||
gb->huc3_minutes &= ~(0xF << (gb->huc3_access_index * 4));
|
||||
gb->huc3_minutes |= ((value & 0xF) << (gb->huc3_access_index * 4));
|
||||
}
|
||||
else if (gb->huc3_access_index < 7) {
|
||||
gb->huc3_days &= ~(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) {
|
||||
gb->huc3_alarm_minutes &= ~(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) {
|
||||
gb->huc3_alarm_days &= ~(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) {
|
||||
gb->huc3_alarm_enabled = value & 1;
|
||||
}
|
||||
else {
|
||||
// GB_log(gb, "Attempting to write %x to unsupported HuC-3 register: %03x\n", value & 0xF, gb->huc3_access_index);
|
||||
}
|
||||
if ((value >> 4) == 3) {
|
||||
gb->huc3_access_index++;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
gb->huc3_access_index &= 0xF0;
|
||||
gb->huc3_access_index |= value & 0xF;
|
||||
break;
|
||||
case 5:
|
||||
gb->huc3_access_index &= 0x0F;
|
||||
gb->huc3_access_index |= (value & 0xF) << 4;
|
||||
break;
|
||||
case 6:
|
||||
gb->huc3_access_flags = (value & 0xF);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
case 0xD: // RTC status
|
||||
// Not sure what writes here mean, they're always 0xFE
|
||||
return true;
|
||||
case 0xE: { // IR mode
|
||||
bool old_input = effective_ir_input(gb);
|
||||
gb->cart_ir = value & 1;
|
||||
bool new_input = effective_ir_input(gb);
|
||||
if (new_input != old_input) {
|
||||
if (gb->infrared_callback) {
|
||||
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change);
|
||||
}
|
||||
gb->cycles_since_ir_change = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case 0xC:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
case 0: // Disabled
|
||||
case 0xA: // RAM
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
{
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
if (huc3_write(gb, value)) return;
|
||||
if (addr == 0xb000) {
|
||||
gb->mbc_rom0_bank = value * 2;
|
||||
gb->mbc_rom_bank = value * 2 + 1;
|
||||
}
|
||||
|
||||
if (gb->camera_registers_mapped) {
|
||||
GB_camera_write_register(gb, addr, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!gb->mbc_ram_enable)
|
||||
&& gb->cartridge_type->mbc_type != GB_HUC1) return;
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) {
|
||||
bool old_input = effective_ir_input(gb);
|
||||
gb->cart_ir = value & 1;
|
||||
bool new_input = effective_ir_input(gb);
|
||||
if (new_input != old_input) {
|
||||
if (gb->infrared_callback) {
|
||||
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change);
|
||||
}
|
||||
gb->cycles_since_ir_change = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->has_rtc && gb->mbc3_rtc_mapped && gb->mbc_ram_bank <= 4) {
|
||||
gb->rtc_latched.data[gb->mbc_ram_bank] = gb->rtc_real.data[gb->mbc_ram_bank] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gb->mbc_ram || !gb->mbc_ram_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t effective_bank = gb->mbc_ram_bank;
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC3 && !gb->is_mbc30) {
|
||||
effective_bank &= 0x3;
|
||||
}
|
||||
|
||||
gb->mbc_ram[((addr & 0x1FFF) + effective_bank * 0x2000) & (gb->mbc_ram_size - 1)] = value;
|
||||
}
|
||||
|
||||
static void write_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
@ -1121,7 +863,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
}
|
||||
|
||||
default:
|
||||
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) {
|
||||
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_NR51) {
|
||||
GB_apu_write(gb, addr & 0xFF, value);
|
||||
return;
|
||||
}
|
||||
|
@ -15,13 +15,7 @@ void GB_handle_rumble(GB_gameboy_t *gb)
|
||||
if (gb->rumble_mode == GB_RUMBLE_DISABLED) {
|
||||
return;
|
||||
}
|
||||
if (gb->cartridge_type->has_rumble) {
|
||||
if (gb->rumble_on_cycles + gb->rumble_off_cycles) {
|
||||
gb->rumble_callback(gb, gb->rumble_on_cycles / (double)(gb->rumble_on_cycles + gb->rumble_off_cycles));
|
||||
gb->rumble_on_cycles = gb->rumble_off_cycles = 0;
|
||||
}
|
||||
}
|
||||
else if (gb->rumble_mode == GB_RUMBLE_ALL_GAMES) {
|
||||
if (gb->rumble_mode == GB_RUMBLE_ALL_GAMES) {
|
||||
unsigned volume = (gb->io_registers[GB_IO_NR50] & 7) + 1 + ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1;
|
||||
unsigned ch4_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 8) + !!(gb->io_registers[GB_IO_NR51] & 0x80));
|
||||
unsigned ch1_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 1) + !!(gb->io_registers[GB_IO_NR51] & 0x10));
|
||||
|
@ -40,10 +40,6 @@ int GB_save_state(GB_gameboy_t *gb, const char *path)
|
||||
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
|
||||
}
|
||||
|
||||
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fwrite(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
|
||||
goto error;
|
||||
}
|
||||
@ -73,7 +69,6 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb)
|
||||
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
|
||||
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
|
||||
+ (GB_is_hle_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
|
||||
+ gb->mbc_ram_size
|
||||
+ gb->ram_size
|
||||
+ gb->vram_size;
|
||||
}
|
||||
@ -109,7 +104,6 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
|
||||
}
|
||||
|
||||
|
||||
buffer_write(gb->mbc_ram, gb->mbc_ram_size, &buffer);
|
||||
buffer_write(gb->ram, gb->ram_size, &buffer);
|
||||
buffer_write(gb->vram, gb->vram_size, &buffer);
|
||||
}
|
||||
@ -168,11 +162,6 @@ static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gb->mbc_ram_size < save->mbc_ram_size) {
|
||||
GB_log(gb, "The save state has non-matching MBC RAM size.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gb->vram_size != save->vram_size) {
|
||||
GB_log(gb, "The save state has non-matching VRAM size. Try changing the emulated model.\n");
|
||||
return false;
|
||||
@ -265,13 +254,7 @@ int GB_load_state(GB_gameboy_t *gb, const char *path)
|
||||
if (GB_is_hle_sgb(gb)) {
|
||||
if (!read_section(f, gb->sgb, sizeof(*gb->sgb), false)) goto error;
|
||||
}
|
||||
|
||||
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
|
||||
if (fread(gb->mbc_ram, 1, save.mbc_ram_size, f) != save.mbc_ram_size) {
|
||||
fclose(f);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
|
||||
if (fread(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
|
||||
fclose(f);
|
||||
return EIO;
|
||||
@ -385,12 +368,7 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
|
||||
if (GB_is_hle_sgb(gb)) {
|
||||
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb), false)) return -1;
|
||||
}
|
||||
|
||||
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
|
||||
if (buffer_read(gb->mbc_ram, save.mbc_ram_size, &buffer, &length) != save.mbc_ram_size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (buffer_read(gb->ram, gb->ram_size, &buffer, &length) != gb->ram_size) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -276,52 +276,3 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GB_rtc_run(GB_gameboy_t *gb)
|
||||
{
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
time_t current_time = time(NULL);
|
||||
while (gb->last_rtc_second / 60 < current_time / 60) {
|
||||
gb->last_rtc_second += 60;
|
||||
gb->huc3_minutes++;
|
||||
if (gb->huc3_minutes == 60 * 24) {
|
||||
gb->huc3_days++;
|
||||
gb->huc3_minutes = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */
|
||||
time_t current_time = time(NULL);
|
||||
|
||||
while (gb->last_rtc_second + 60 * 60 * 24 < current_time) {
|
||||
gb->last_rtc_second += 60 * 60 * 24;
|
||||
if (++gb->rtc_real.days == 0) {
|
||||
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
|
||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||
}
|
||||
gb->rtc_real.high ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (gb->last_rtc_second < current_time) {
|
||||
gb->last_rtc_second++;
|
||||
if (++gb->rtc_real.seconds == 60) {
|
||||
gb->rtc_real.seconds = 0;
|
||||
if (++gb->rtc_real.minutes == 60) {
|
||||
gb->rtc_real.minutes = 0;
|
||||
if (++gb->rtc_real.hours == 24) {
|
||||
gb->rtc_real.hours = 0;
|
||||
if (++gb->rtc_real.days == 0) {
|
||||
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
|
||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||
}
|
||||
gb->rtc_real.high ^= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
|
||||
void GB_rtc_run(GB_gameboy_t *gb);
|
||||
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac);
|
||||
bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */
|
||||
void GB_timing_sync(GB_gameboy_t *gb);
|
||||
|
@ -6,62 +6,42 @@
|
||||
00:FF06 IO_TMA
|
||||
00:FF07 IO_TAC
|
||||
00:FF0F IO_IF
|
||||
00:FF10 IO_NR10
|
||||
00:FF11 IO_NR11
|
||||
00:FF12 IO_NR12
|
||||
00:FF13 IO_NR13
|
||||
00:FF14 IO_NR14
|
||||
00:FF16 IO_NR21
|
||||
00:FF17 IO_NR22
|
||||
00:FF18 IO_NR23
|
||||
00:FF19 IO_NR24
|
||||
00:FF1A IO_NR30
|
||||
00:FF1B IO_NR31
|
||||
00:FF1C IO_NR32
|
||||
00:FF1D IO_NR33
|
||||
00:FF1E IO_NR34
|
||||
00:FF20 IO_NR41
|
||||
00:FF21 IO_NR42
|
||||
00:FF22 IO_NR43
|
||||
00:FF23 IO_NR44
|
||||
00:FF24 IO_NR50
|
||||
00:FF25 IO_NR51
|
||||
00:FF26 IO_NR52
|
||||
00:FF10 IO_LCDC
|
||||
00:FF11 IO_STAT
|
||||
00:FF12 IO_SCY
|
||||
00:FF13 IO_SCX
|
||||
00:FF14 IO_OBP0
|
||||
00:FF15 IO_OBP1
|
||||
00:FF16 IO_WY
|
||||
00:FF17 IO_WX
|
||||
00:FF18 IO_LY
|
||||
00:FF19 IO_LYC
|
||||
00:FF1A IO_DMA
|
||||
00:FF1B IO_BGP
|
||||
00:FF20 IO_NR10
|
||||
00:FF21 IO_NR12
|
||||
00:FF22 IO_NR11
|
||||
00:FF23 IO_NR13
|
||||
00:FF24 IO_NR14
|
||||
00:FF25 IO_NR21
|
||||
00:FF27 IO_NR22
|
||||
00:FF28 IO_NR23
|
||||
00:FF29 IO_NR24
|
||||
00:FF2A IO_NR30
|
||||
00:FF2B IO_NR31
|
||||
00:FF2C IO_NR32
|
||||
00:FF2D IO_NR33
|
||||
00:FF2E IO_NR34
|
||||
00:FF30 IO_WAV_START
|
||||
00:FF3F IO_WAV_END
|
||||
00:FF40 IO_LCDC
|
||||
00:FF41 IO_STAT
|
||||
00:FF42 IO_SCY
|
||||
00:FF43 IO_SCX
|
||||
00:FF44 IO_LY
|
||||
00:FF45 IO_LYC
|
||||
00:FF46 IO_DMA
|
||||
00:FF47 IO_BGP
|
||||
00:FF48 IO_OBP0
|
||||
00:FF49 IO_OBP1
|
||||
00:FF4A IO_WY
|
||||
00:FF4B IO_WX
|
||||
00:FF40 IO_NR41
|
||||
00:FF41 IO_NR43
|
||||
00:FF42 IO_NR42
|
||||
00:FF43 IO_NR44
|
||||
00:FF44 IO_NR50
|
||||
00:FF45 IO_NR52
|
||||
00:FF46 IO_NR51
|
||||
00:FF4C IO_KEY0
|
||||
00:FF4D IO_KEY1
|
||||
00:FF4F IO_VBK
|
||||
00:FF50 IO_BANK
|
||||
00:FF51 IO_HDMA1
|
||||
00:FF52 IO_HDMA2
|
||||
00:FF53 IO_HDMA3
|
||||
00:FF54 IO_HDMA4
|
||||
00:FF55 IO_HDMA5
|
||||
00:FF56 IO_RP
|
||||
00:FF68 IO_BGPI
|
||||
00:FF69 IO_BGPD
|
||||
00:FF6A IO_OBPI
|
||||
00:FF6B IO_OBPD
|
||||
00:FF6C IO_OPRI
|
||||
00:FF70 IO_SVBK
|
||||
00:FF72 IO_UNKNOWN2
|
||||
00:FF73 IO_UNKNOWN3
|
||||
00:FF74 IO_UNKNOWN4
|
||||
00:FF75 IO_UNKNOWN5
|
||||
00:FF76 IO_PCM_12
|
||||
00:FF77 IO_PCM_34
|
||||
00:FF7F IO_UNKNOWN8
|
||||
00:FFFF IO_IE
|
||||
|
Loading…
Reference in New Issue
Block a user