2016-03-30 20:07:55 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include "gb.h"
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr);
|
|
|
|
typedef void GB_write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
2016-03-30 20:07:55 +00:00
|
|
|
|
2016-08-03 20:31:10 +00:00
|
|
|
typedef enum {
|
|
|
|
GB_BUS_MAIN, /* In DMG: Cart and RAM. In CGB: Cart only */
|
|
|
|
GB_BUS_RAM, /* In CGB only. */
|
|
|
|
GB_BUS_VRAM,
|
|
|
|
GB_BUS_INTERNAL, /* Anything in highram. Might not be the most correct name. */
|
|
|
|
} GB_bus_t;
|
|
|
|
|
|
|
|
static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr)
|
|
|
|
{
|
|
|
|
if (addr < 0x8000) {
|
|
|
|
return GB_BUS_MAIN;
|
|
|
|
}
|
|
|
|
if (addr < 0xA000) {
|
|
|
|
return GB_BUS_VRAM;
|
|
|
|
}
|
|
|
|
if (addr < 0xC000) {
|
|
|
|
return GB_BUS_MAIN;
|
|
|
|
}
|
|
|
|
if (addr < 0xFE00) {
|
2018-06-16 10:59:33 +00:00
|
|
|
return GB_is_cgb(gb)? GB_BUS_RAM : GB_BUS_MAIN;
|
2016-08-03 20:31:10 +00:00
|
|
|
}
|
|
|
|
return GB_BUS_INTERNAL;
|
|
|
|
}
|
|
|
|
|
2018-03-27 12:46:00 +00:00
|
|
|
static uint8_t bitwise_glitch(uint8_t a, uint8_t b, uint8_t c)
|
|
|
|
{
|
|
|
|
return ((a ^ c) & (b ^ c)) ^ c;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t bitwise_glitch_read(uint8_t a, uint8_t b, uint8_t c)
|
|
|
|
{
|
|
|
|
return b | (a & c);
|
|
|
|
}
|
|
|
|
|
2018-03-27 12:55:12 +00:00
|
|
|
static uint8_t bitwise_glitch_read_increase(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
|
2018-03-27 12:46:00 +00:00
|
|
|
{
|
2018-03-27 16:06:36 +00:00
|
|
|
return (b & (a | c | d)) | (a & c & d);
|
2018-03-27 12:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address)
|
|
|
|
{
|
2018-06-16 10:59:33 +00:00
|
|
|
if (GB_is_cgb(gb)) return;
|
2018-03-27 12:46:00 +00:00
|
|
|
|
|
|
|
if (address >= 0xFE00 && address < 0xFF00) {
|
|
|
|
if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) {
|
|
|
|
gb->oam[gb->accessed_oam_row] = bitwise_glitch(gb->oam[gb->accessed_oam_row],
|
|
|
|
gb->oam[gb->accessed_oam_row - 8],
|
|
|
|
gb->oam[gb->accessed_oam_row - 4]);
|
|
|
|
gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch(gb->oam[gb->accessed_oam_row + 1],
|
|
|
|
gb->oam[gb->accessed_oam_row - 7],
|
|
|
|
gb->oam[gb->accessed_oam_row - 3]);
|
|
|
|
for (unsigned i = 2; i < 8; i++) {
|
|
|
|
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GB_trigger_oam_bug_read(GB_gameboy_t *gb, uint16_t address)
|
|
|
|
{
|
2018-06-16 10:59:33 +00:00
|
|
|
if (GB_is_cgb(gb)) return;
|
2018-03-27 12:46:00 +00:00
|
|
|
|
|
|
|
if (address >= 0xFE00 && address < 0xFF00) {
|
|
|
|
if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) {
|
|
|
|
gb->oam[gb->accessed_oam_row - 8] =
|
|
|
|
gb->oam[gb->accessed_oam_row] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row],
|
|
|
|
gb->oam[gb->accessed_oam_row - 8],
|
|
|
|
gb->oam[gb->accessed_oam_row - 4]);
|
|
|
|
gb->oam[gb->accessed_oam_row - 7] =
|
|
|
|
gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row + 1],
|
|
|
|
gb->oam[gb->accessed_oam_row - 7],
|
|
|
|
gb->oam[gb->accessed_oam_row - 3]);
|
|
|
|
for (unsigned i = 2; i < 8; i++) {
|
|
|
|
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address)
|
|
|
|
{
|
2018-06-16 10:59:33 +00:00
|
|
|
if (GB_is_cgb(gb)) return;
|
2018-03-27 12:46:00 +00:00
|
|
|
|
|
|
|
if (address >= 0xFE00 && address < 0xFF00) {
|
|
|
|
if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 0x20 && gb->accessed_oam_row < 0x98) {
|
2018-03-27 12:55:12 +00:00
|
|
|
gb->oam[gb->accessed_oam_row - 0x8] = bitwise_glitch_read_increase(gb->oam[gb->accessed_oam_row - 0x10],
|
|
|
|
gb->oam[gb->accessed_oam_row - 0x08],
|
|
|
|
gb->oam[gb->accessed_oam_row ],
|
|
|
|
gb->oam[gb->accessed_oam_row - 0x04]
|
|
|
|
);
|
|
|
|
gb->oam[gb->accessed_oam_row - 0x7] = bitwise_glitch_read_increase(gb->oam[gb->accessed_oam_row - 0x0f],
|
|
|
|
gb->oam[gb->accessed_oam_row - 0x07],
|
|
|
|
gb->oam[gb->accessed_oam_row + 0x01],
|
|
|
|
gb->oam[gb->accessed_oam_row - 0x03]
|
|
|
|
);
|
2018-03-27 12:46:00 +00:00
|
|
|
for (unsigned i = 0; i < 8; i++) {
|
|
|
|
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 0x10 + i] = gb->oam[gb->accessed_oam_row - 0x08 + i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 20:31:10 +00:00
|
|
|
static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr)
|
|
|
|
{
|
2018-03-22 18:09:01 +00:00
|
|
|
if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xFE00) return false;
|
2016-08-03 20:31:10 +00:00
|
|
|
return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src);
|
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
2016-06-18 17:29:11 +00:00
|
|
|
if (addr < 0x100 && !gb->boot_rom_finished) {
|
|
|
|
return gb->boot_rom[addr];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
2018-06-16 10:59:33 +00:00
|
|
|
if (addr >= 0x200 && addr < 0x900 && GB_is_cgb(gb) && !gb->boot_rom_finished) {
|
2016-06-18 17:29:11 +00:00
|
|
|
return gb->boot_rom[addr];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!gb->rom_size) {
|
|
|
|
return 0xFF;
|
|
|
|
}
|
2019-06-15 20:22:27 +00:00
|
|
|
unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000;
|
2016-07-09 14:34:55 +00:00
|
|
|
return gb->rom[effective_address & (gb->rom_size - 1)];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static uint8_t read_mbc_rom(GB_gameboy_t *gb, uint16_t addr)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
2019-06-15 20:22:27 +00:00
|
|
|
unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000;
|
2016-07-09 14:34:55 +00:00
|
|
|
return gb->rom[effective_address & (gb->rom_size - 1)];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
2017-06-18 18:27:07 +00:00
|
|
|
if (gb->vram_read_blocked) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return 0xFF;
|
|
|
|
}
|
2016-06-18 17:29:11 +00:00
|
|
|
return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
2017-02-22 22:27:08 +00:00
|
|
|
if ((!gb->mbc_ram_enable || !gb->mbc_ram_size) &&
|
|
|
|
gb->cartridge_type->mbc_subtype != GB_CAMERA &&
|
|
|
|
gb->cartridge_type->mbc_type != GB_HUC1) return 0xFF;
|
2016-07-09 14:34:55 +00:00
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
|
|
|
|
/* RTC read */
|
2016-08-21 19:33:57 +00:00
|
|
|
gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */
|
|
|
|
return gb->rtc_latched.data[gb->mbc_ram_bank - 8];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
2016-07-09 14:34:55 +00:00
|
|
|
|
2016-10-02 00:40:11 +00:00
|
|
|
if (gb->camera_registers_mapped) {
|
2016-10-02 14:14:58 +00:00
|
|
|
return GB_camera_read_register(gb, addr);
|
2016-10-02 00:40:11 +00:00
|
|
|
}
|
|
|
|
|
2016-07-09 14:34:55 +00:00
|
|
|
if (!gb->mbc_ram) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return 0xFF;
|
|
|
|
}
|
2016-07-09 14:34:55 +00:00
|
|
|
|
2016-10-02 14:14:58 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-09-30 20:34:06 +00:00
|
|
|
uint8_t ret = gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)];
|
|
|
|
if (gb->cartridge_type->mbc_type == GB_MBC2) {
|
|
|
|
ret |= 0xF0;
|
|
|
|
}
|
|
|
|
return ret;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static uint8_t read_ram(GB_gameboy_t *gb, uint16_t addr)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
|
|
|
return gb->ram[addr & 0x0FFF];
|
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static uint8_t read_banked_ram(GB_gameboy_t *gb, uint16_t addr)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
|
|
|
return gb->ram[(addr & 0x0FFF) + gb->cgb_ram_bank * 0x1000];
|
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
|
|
|
|
2018-02-25 21:23:55 +00:00
|
|
|
if (gb->hdma_on) {
|
|
|
|
return gb->last_opcode_read;
|
|
|
|
}
|
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
if (addr < 0xFE00) {
|
2018-12-12 21:44:00 +00:00
|
|
|
return read_banked_ram(gb, addr);
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (addr < 0xFF00) {
|
2019-01-19 17:32:26 +00:00
|
|
|
if (gb->oam_write_blocked && !GB_is_cgb(gb)) {
|
2018-03-27 12:46:00 +00:00
|
|
|
GB_trigger_oam_bug_read(gb, addr);
|
|
|
|
return 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
|
|
|
|
/* Todo: Does reading from OAM during DMA causes the OAM bug? */
|
|
|
|
return 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gb->oam_read_blocked) {
|
2018-06-16 10:59:33 +00:00
|
|
|
if (!GB_is_cgb(gb)) {
|
2018-03-27 12:46:00 +00:00
|
|
|
if (addr < 0xFEA0) {
|
|
|
|
if (gb->accessed_oam_row == 0) {
|
|
|
|
gb->oam[(addr & 0xf8)] =
|
|
|
|
gb->oam[0] = bitwise_glitch_read(gb->oam[0],
|
|
|
|
gb->oam[(addr & 0xf8)],
|
|
|
|
gb->oam[(addr & 0xfe)]);
|
|
|
|
gb->oam[(addr & 0xf8) + 1] =
|
|
|
|
gb->oam[1] = bitwise_glitch_read(gb->oam[1],
|
|
|
|
gb->oam[(addr & 0xf8) + 1],
|
|
|
|
gb->oam[(addr & 0xfe) | 1]);
|
|
|
|
for (unsigned i = 2; i < 8; i++) {
|
|
|
|
gb->oam[i] = gb->oam[(addr & 0xf8) + i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (gb->accessed_oam_row == 0xa0) {
|
|
|
|
gb->oam[0x9e] = bitwise_glitch_read(gb->oam[0x9c],
|
|
|
|
gb->oam[0x9e],
|
|
|
|
gb->oam[(addr & 0xf8) | 6]);
|
|
|
|
gb->oam[0x9f] = bitwise_glitch_read(gb->oam[0x9d],
|
|
|
|
gb->oam[0x9f],
|
|
|
|
gb->oam[(addr & 0xf8) | 7]);
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < 8; i++) {
|
|
|
|
gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr < 0xFEA0) {
|
|
|
|
return gb->oam[addr & 0xFF];
|
|
|
|
}
|
|
|
|
|
2018-07-03 18:43:46 +00:00
|
|
|
if (gb->oam_read_blocked) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return 0xFF;
|
|
|
|
}
|
2018-07-03 18:43:46 +00:00
|
|
|
|
|
|
|
switch (gb->model) {
|
|
|
|
case GB_MODEL_CGB_E:
|
|
|
|
case GB_MODEL_AGB:
|
|
|
|
return (addr & 0xF0) | ((addr >> 4) & 0xF);
|
2018-07-14 18:52:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
case GB_MODEL_CGB_D:
|
|
|
|
if (addr > 0xfec0) {
|
|
|
|
addr |= 0xf0;
|
|
|
|
}
|
|
|
|
return gb->extra_oam[addr - 0xfea0];
|
|
|
|
*/
|
|
|
|
|
2018-07-03 18:43:46 +00:00
|
|
|
case GB_MODEL_CGB_C:
|
2018-07-14 18:52:54 +00:00
|
|
|
/*
|
|
|
|
case GB_MODEL_CGB_B:
|
|
|
|
case GB_MODEL_CGB_A:
|
|
|
|
case GB_MODEL_CGB_0:
|
|
|
|
*/
|
2018-07-03 18:43:46 +00:00
|
|
|
addr &= ~0x18;
|
|
|
|
return gb->extra_oam[addr - 0xfea0];
|
|
|
|
|
2018-07-14 18:52:54 +00:00
|
|
|
case GB_MODEL_DMG_B:
|
2018-11-10 23:16:32 +00:00
|
|
|
case GB_MODEL_SGB_NTSC:
|
|
|
|
case GB_MODEL_SGB_PAL:
|
2019-10-08 12:10:24 +00:00
|
|
|
case GB_MODEL_SGB_NTSC_NO_SFC:
|
|
|
|
case GB_MODEL_SGB_PAL_NO_SFC:
|
2018-11-10 23:16:32 +00:00
|
|
|
case GB_MODEL_SGB2:
|
2019-07-15 20:02:58 +00:00
|
|
|
case GB_MODEL_SGB2_NO_SFC:
|
2018-07-03 18:43:46 +00:00
|
|
|
;
|
2016-08-06 15:58:44 +00:00
|
|
|
}
|
2018-03-27 12:46:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (addr < 0xFF00) {
|
|
|
|
|
2016-08-06 15:58:44 +00:00
|
|
|
return 0;
|
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (addr < 0xFF80) {
|
|
|
|
switch (addr & 0xFF) {
|
|
|
|
case GB_IO_IF:
|
2018-02-24 22:48:45 +00:00
|
|
|
return gb->io_registers[GB_IO_IF] | 0xE0;
|
2016-04-04 19:12:00 +00:00
|
|
|
case GB_IO_TAC:
|
|
|
|
return gb->io_registers[GB_IO_TAC] | 0xF8;
|
|
|
|
case GB_IO_STAT:
|
|
|
|
return gb->io_registers[GB_IO_STAT] | 0x80;
|
2020-02-15 13:32:06 +00:00
|
|
|
case GB_IO_OBJECT_PRIORITY:
|
|
|
|
if (!GB_is_cgb(gb)) {
|
2016-08-05 13:36:38 +00:00
|
|
|
return 0xFF;
|
|
|
|
}
|
2020-02-15 13:32:06 +00:00
|
|
|
return gb->io_registers[GB_IO_OBJECT_PRIORITY] | 0xFE;
|
2016-08-05 13:36:38 +00:00
|
|
|
|
2016-09-13 13:55:56 +00:00
|
|
|
case GB_IO_PCM_12:
|
2018-06-16 10:59:33 +00:00
|
|
|
if (!GB_is_cgb(gb)) return 0xFF;
|
2018-02-16 16:01:50 +00:00
|
|
|
return (gb->apu.is_active[GB_SQUARE_2] ? (gb->apu.samples[GB_SQUARE_2] << 4) : 0) |
|
|
|
|
(gb->apu.is_active[GB_SQUARE_1] ? (gb->apu.samples[GB_SQUARE_1]) : 0);
|
2016-09-13 13:55:56 +00:00
|
|
|
case GB_IO_PCM_34:
|
2018-06-16 10:59:33 +00:00
|
|
|
if (!GB_is_cgb(gb)) return 0xFF;
|
2018-02-16 16:01:50 +00:00
|
|
|
return (gb->apu.is_active[GB_NOISE] ? (gb->apu.samples[GB_NOISE] << 4) : 0) |
|
|
|
|
(gb->apu.is_active[GB_WAVE] ? (gb->apu.samples[GB_WAVE]) : 0);
|
2016-04-02 13:29:02 +00:00
|
|
|
case GB_IO_JOYP:
|
2019-06-07 15:27:25 +00:00
|
|
|
GB_timing_sync(gb);
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_TMA:
|
|
|
|
case GB_IO_LCDC:
|
|
|
|
case GB_IO_SCY:
|
|
|
|
case GB_IO_SCX:
|
|
|
|
case GB_IO_LY:
|
|
|
|
case GB_IO_LYC:
|
|
|
|
case GB_IO_BGP:
|
|
|
|
case GB_IO_OBP0:
|
|
|
|
case GB_IO_OBP1:
|
|
|
|
case GB_IO_WY:
|
|
|
|
case GB_IO_WX:
|
2016-09-05 19:13:28 +00:00
|
|
|
case GB_IO_SC:
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_SB:
|
2018-03-22 18:09:01 +00:00
|
|
|
case GB_IO_DMA:
|
2016-03-30 20:07:55 +00:00
|
|
|
return gb->io_registers[addr & 0xFF];
|
2016-08-06 10:56:29 +00:00
|
|
|
case GB_IO_TIMA:
|
|
|
|
if (gb->tima_reload_state == GB_TIMA_RELOADING) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return gb->io_registers[GB_IO_TIMA];
|
2016-10-21 21:49:32 +00:00
|
|
|
case GB_IO_DIV:
|
2018-02-23 11:16:05 +00:00
|
|
|
return gb->div_counter >> 8;
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_HDMA5:
|
2017-06-21 06:35:57 +00:00
|
|
|
if (!gb->cgb_mode) return 0xFF;
|
2017-02-08 19:58:15 +00:00
|
|
|
return ((gb->hdma_on || gb->hdma_on_hblank)? 0 : 0x80) | ((gb->hdma_steps_left - 1) & 0x7F);
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_SVBK:
|
|
|
|
if (!gb->cgb_mode) {
|
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
return gb->cgb_ram_bank | ~0x7;
|
|
|
|
case GB_IO_VBK:
|
2018-06-16 10:59:33 +00:00
|
|
|
if (!GB_is_cgb(gb)) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
return gb->cgb_vram_bank | ~0x1;
|
|
|
|
|
2017-06-21 06:35:57 +00:00
|
|
|
/* Todo: It seems that a CGB in DMG mode can access BGPI and OBPI, but not BGPD and OBPD? */
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_BGPI:
|
|
|
|
case GB_IO_OBPI:
|
2018-06-16 10:59:33 +00:00
|
|
|
if (!GB_is_cgb(gb)) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
return gb->io_registers[addr & 0xFF] | 0x40;
|
|
|
|
|
|
|
|
case GB_IO_BGPD:
|
|
|
|
case GB_IO_OBPD:
|
|
|
|
{
|
2016-10-01 19:10:44 +00:00
|
|
|
if (!gb->cgb_mode && gb->boot_rom_finished) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return 0xFF;
|
|
|
|
}
|
2019-01-19 17:32:26 +00:00
|
|
|
if (gb->cgb_palettes_blocked) {
|
2019-01-14 20:22:46 +00:00
|
|
|
return 0xFF;
|
|
|
|
}
|
2016-06-18 17:29:11 +00:00
|
|
|
uint8_t index_reg = (addr & 0xFF) - 1;
|
2016-03-30 20:07:55 +00:00
|
|
|
return ((addr & 0xFF) == GB_IO_BGPD?
|
2017-04-19 20:26:39 +00:00
|
|
|
gb->background_palettes_data :
|
|
|
|
gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F];
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case GB_IO_KEY1:
|
2016-04-05 20:21:51 +00:00
|
|
|
if (!gb->cgb_mode) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
return (gb->io_registers[GB_IO_KEY1] & 0x7F) | (gb->cgb_double_speed? 0xFE : 0x7E);
|
|
|
|
|
2016-07-18 19:05:11 +00:00
|
|
|
case GB_IO_RP: {
|
2017-06-21 06:35:57 +00:00
|
|
|
if (!gb->cgb_mode) return 0xFF;
|
2016-07-18 19:05:11 +00:00
|
|
|
/* You will read your own IR LED if it's on. */
|
|
|
|
bool read_value = gb->infrared_input || (gb->io_registers[GB_IO_RP] & 1);
|
|
|
|
uint8_t ret = (gb->io_registers[GB_IO_RP] & 0xC1) | 0x3C;
|
|
|
|
if ((gb->io_registers[GB_IO_RP] & 0xC0) == 0xC0 && read_value) {
|
2016-07-20 22:03:13 +00:00
|
|
|
ret |= 2;
|
2016-07-18 19:05:11 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2017-06-21 06:35:57 +00:00
|
|
|
case GB_IO_UNKNOWN2:
|
|
|
|
case GB_IO_UNKNOWN3:
|
2018-06-16 10:59:33 +00:00
|
|
|
return GB_is_cgb(gb)? gb->io_registers[addr & 0xFF] : 0xFF;
|
2017-06-21 06:35:57 +00:00
|
|
|
case GB_IO_UNKNOWN4:
|
|
|
|
return gb->cgb_mode? gb->io_registers[addr & 0xFF] : 0xFF;
|
|
|
|
case GB_IO_UNKNOWN5:
|
2018-06-16 10:59:33 +00:00
|
|
|
return GB_is_cgb(gb)? gb->io_registers[addr & 0xFF] | 0x8F : 0xFF;
|
2016-03-30 20:07:55 +00:00
|
|
|
default:
|
|
|
|
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) {
|
2016-06-18 17:29:11 +00:00
|
|
|
return GB_apu_read(gb, addr & 0xFF);
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
/* Hardware registers */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr == 0xFFFF) {
|
|
|
|
/* Interrupt Mask */
|
|
|
|
return gb->interrupt_enable;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* HRAM */
|
|
|
|
return gb->hram[addr - 0xFF80];
|
|
|
|
}
|
|
|
|
|
|
|
|
static GB_read_function_t * const read_map[] =
|
|
|
|
{
|
|
|
|
read_rom, read_rom, read_rom, read_rom, /* 0XXX, 1XXX, 2XXX, 3XXX */
|
|
|
|
read_mbc_rom, read_mbc_rom, read_mbc_rom, read_mbc_rom, /* 4XXX, 5XXX, 6XXX, 7XXX */
|
|
|
|
read_vram, read_vram, /* 8XXX, 9XXX */
|
|
|
|
read_mbc_ram, read_mbc_ram, /* AXXX, BXXX */
|
|
|
|
read_ram, read_banked_ram, /* CXXX, DXXX */
|
2018-12-12 21:44:00 +00:00
|
|
|
read_ram, read_high_memory, /* EXXX FXXX */
|
2016-03-30 20:07:55 +00:00
|
|
|
};
|
|
|
|
|
2019-11-08 14:14:16 +00:00
|
|
|
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback)
|
|
|
|
{
|
|
|
|
gb->read_memory_callback = callback;
|
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
2017-10-12 16:05:27 +00:00
|
|
|
if (gb->n_watchpoints) {
|
2016-10-21 21:49:32 +00:00
|
|
|
GB_debugger_test_read_watchpoint(gb, addr);
|
2017-10-12 16:05:27 +00:00
|
|
|
}
|
2016-08-03 20:31:10 +00:00
|
|
|
if (is_addr_in_dma_use(gb, addr)) {
|
|
|
|
addr = gb->dma_current_src;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
2019-08-16 14:38:43 +00:00
|
|
|
if (gb->read_memory_callback) {
|
|
|
|
uint8_t data = read_map[addr >> 12](gb, addr);
|
|
|
|
data = gb->read_memory_callback(gb, addr, data);
|
|
|
|
return data;
|
|
|
|
}
|
2016-03-30 20:07:55 +00:00
|
|
|
return read_map[addr >> 12](gb, addr);
|
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
2016-07-09 14:34:55 +00:00
|
|
|
switch (gb->cartridge_type->mbc_type) {
|
2016-10-17 15:51:43 +00:00
|
|
|
case GB_NO_MBC: return;
|
2016-07-09 14:34:55 +00:00
|
|
|
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;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
break;
|
2016-07-09 14:34:55 +00:00
|
|
|
case GB_MBC2:
|
2020-03-27 13:35:36 +00:00
|
|
|
switch (addr & 0x4100) {
|
|
|
|
case 0x0000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
|
|
|
case 0x0100: gb->mbc2.rom_bank = value; break;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
break;
|
2016-07-09 14:34:55 +00:00
|
|
|
case GB_MBC3:
|
|
|
|
switch (addr & 0xF000) {
|
|
|
|
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
|
|
|
case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break;
|
|
|
|
case 0x4000: case 0x5000: gb->mbc3.ram_bank = value; break;
|
2016-08-21 19:33:57 +00:00
|
|
|
case 0x6000: case 0x7000:
|
2016-10-02 00:40:11 +00:00
|
|
|
if (!gb->rtc_latch && (value & 1)) { /* Todo: verify condition is correct */
|
2016-08-21 19:33:57 +00:00
|
|
|
memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real));
|
|
|
|
}
|
|
|
|
gb->rtc_latch = value & 1;
|
|
|
|
break;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
break;
|
2016-07-09 14:34:55 +00:00
|
|
|
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;
|
2016-10-02 00:40:11 +00:00
|
|
|
case 0x4000: case 0x5000:
|
2016-10-22 12:37:03 +00:00
|
|
|
if (gb->cartridge_type->has_rumble) {
|
|
|
|
if (!!(value & 8) != gb->rumble_state) {
|
|
|
|
gb->rumble_state = !gb->rumble_state;
|
|
|
|
if (gb->rumble_callback) {
|
|
|
|
gb->rumble_callback(gb, gb->rumble_state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
value &= 7;
|
|
|
|
}
|
2016-10-02 00:40:11 +00:00
|
|
|
gb->mbc5.ram_bank = value;
|
|
|
|
gb->camera_registers_mapped = (value & 0x10) && gb->cartridge_type->mbc_subtype == GB_CAMERA;
|
|
|
|
break;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-02-16 19:07:35 +00:00
|
|
|
case GB_HUC1:
|
|
|
|
switch (addr & 0xF000) {
|
|
|
|
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
|
|
|
case 0x2000: case 0x3000: gb->huc1.bank_low = value; break;
|
|
|
|
case 0x4000: case 0x5000: gb->huc1.bank_high = value; break;
|
|
|
|
case 0x6000: case 0x7000: gb->huc1.mode = value; break;
|
|
|
|
}
|
|
|
|
break;
|
2016-10-17 15:51:43 +00:00
|
|
|
case GB_HUC3:
|
|
|
|
switch (addr & 0xF000) {
|
|
|
|
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
|
|
|
case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break;
|
|
|
|
case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break;
|
|
|
|
}
|
|
|
|
break;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
2016-07-09 14:34:55 +00:00
|
|
|
GB_update_mbc_mappings(gb);
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
2017-06-18 18:27:07 +00:00
|
|
|
if (gb->vram_write_blocked) {
|
2016-06-18 17:29:11 +00:00
|
|
|
//GB_log(gb, "Wrote %02x to %04x (VRAM) during mode 3\n", value, addr);
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-06-18 17:29:11 +00:00
|
|
|
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
2016-10-02 14:14:58 +00:00
|
|
|
if (gb->camera_registers_mapped) {
|
|
|
|
GB_camera_write_register(gb, addr, value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-21 22:52:40 +00:00
|
|
|
if (!gb->mbc_ram_enable || !gb->mbc_ram_size) return;
|
2016-07-09 14:34:55 +00:00
|
|
|
|
|
|
|
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
|
2019-01-14 18:32:52 +00:00
|
|
|
gb->rtc_latched.data[gb->mbc_ram_bank - 8] = gb->rtc_real.data[gb->mbc_ram_bank - 8] = value;
|
2019-01-18 00:36:14 +00:00
|
|
|
return;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
2016-07-09 14:34:55 +00:00
|
|
|
|
|
|
|
if (!gb->mbc_ram) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-07-09 14:34:55 +00:00
|
|
|
|
|
|
|
gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)] = value;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static void write_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
|
|
|
gb->ram[addr & 0x0FFF] = value;
|
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static void write_banked_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
|
|
|
gb->ram[(addr & 0x0FFF) + gb->cgb_ram_bank * 0x1000] = value;
|
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
|
|
|
if (addr < 0xFE00) {
|
2016-06-18 17:29:11 +00:00
|
|
|
GB_log(gb, "Wrote %02x to %04x (RAM Mirror)\n", value, addr);
|
2018-12-12 21:44:00 +00:00
|
|
|
write_banked_ram(gb, addr, value);
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-27 12:46:00 +00:00
|
|
|
if (addr < 0xFF00) {
|
|
|
|
if (gb->oam_write_blocked) {
|
|
|
|
GB_trigger_oam_bug(gb, addr);
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-03-27 12:46:00 +00:00
|
|
|
|
|
|
|
if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
|
|
|
|
/* Todo: Does writing to OAM during DMA causes the OAM bug? */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-16 10:59:33 +00:00
|
|
|
if (GB_is_cgb(gb)) {
|
2018-03-27 12:46:00 +00:00
|
|
|
if (addr < 0xFEA0) {
|
|
|
|
gb->oam[addr & 0xFF] = value;
|
|
|
|
}
|
2018-07-03 18:43:46 +00:00
|
|
|
switch (gb->model) {
|
2018-07-14 18:52:54 +00:00
|
|
|
/*
|
|
|
|
case GB_MODEL_CGB_D:
|
|
|
|
if (addr > 0xfec0) {
|
|
|
|
addr |= 0xf0;
|
|
|
|
}
|
|
|
|
gb->extra_oam[addr - 0xfea0] = value;
|
|
|
|
break;
|
|
|
|
*/
|
2018-07-03 18:43:46 +00:00
|
|
|
case GB_MODEL_CGB_C:
|
2018-07-14 18:52:54 +00:00
|
|
|
/*
|
|
|
|
case GB_MODEL_CGB_B:
|
|
|
|
case GB_MODEL_CGB_A:
|
|
|
|
case GB_MODEL_CGB_0:
|
|
|
|
*/
|
2018-07-03 18:43:46 +00:00
|
|
|
addr &= ~0x18;
|
|
|
|
gb->extra_oam[addr - 0xfea0] = value;
|
|
|
|
break;
|
2018-07-14 18:52:54 +00:00
|
|
|
case GB_MODEL_DMG_B:
|
2018-11-10 23:16:32 +00:00
|
|
|
case GB_MODEL_SGB_NTSC:
|
|
|
|
case GB_MODEL_SGB_PAL:
|
2019-10-08 12:10:24 +00:00
|
|
|
case GB_MODEL_SGB_NTSC_NO_SFC:
|
|
|
|
case GB_MODEL_SGB_PAL_NO_SFC:
|
2018-11-10 23:16:32 +00:00
|
|
|
case GB_MODEL_SGB2:
|
2019-07-15 20:02:58 +00:00
|
|
|
case GB_MODEL_SGB2_NO_SFC:
|
2018-07-14 18:52:54 +00:00
|
|
|
case GB_MODEL_CGB_E:
|
|
|
|
case GB_MODEL_AGB:
|
2018-07-03 18:43:46 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-03-27 12:46:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr < 0xFEA0) {
|
|
|
|
if (gb->accessed_oam_row == 0xa0) {
|
|
|
|
for (unsigned i = 0; i < 8; i++) {
|
|
|
|
if ((i & 6) != (addr & 6)) {
|
|
|
|
gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
gb->oam[(addr & 0xf8) + i] = bitwise_glitch(gb->oam[(addr & 0xf8) + i], gb->oam[0x9c], gb->oam[0x98 + i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gb->oam[addr & 0xFF] = value;
|
|
|
|
|
|
|
|
if (gb->accessed_oam_row == 0) {
|
|
|
|
gb->oam[0] = bitwise_glitch(gb->oam[0],
|
|
|
|
gb->oam[(addr & 0xf8)],
|
|
|
|
gb->oam[(addr & 0xfe)]);
|
|
|
|
gb->oam[1] = bitwise_glitch(gb->oam[1],
|
|
|
|
gb->oam[(addr & 0xf8) + 1],
|
|
|
|
gb->oam[(addr & 0xfe) | 1]);
|
|
|
|
for (unsigned i = 2; i < 8; i++) {
|
|
|
|
gb->oam[i] = gb->oam[(addr & 0xf8) + i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (gb->accessed_oam_row == 0) {
|
|
|
|
gb->oam[addr & 0x7] = value;
|
|
|
|
}
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-19 22:34:12 +00:00
|
|
|
/* Todo: Clean this code up: use a function table and move relevant code to display.c and timing.c
|
|
|
|
(APU read and writes are already at apu.c) */
|
2016-03-30 20:07:55 +00:00
|
|
|
if (addr < 0xFF80) {
|
|
|
|
/* Hardware registers */
|
|
|
|
switch (addr & 0xFF) {
|
2020-02-26 20:24:08 +00:00
|
|
|
case GB_IO_WY:
|
|
|
|
if (value == gb->current_line) {
|
|
|
|
gb->wy_triggered = true;
|
|
|
|
}
|
2017-08-19 22:34:12 +00:00
|
|
|
case GB_IO_WX:
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_IF:
|
2017-09-09 10:32:12 +00:00
|
|
|
case GB_IO_SCX:
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_SCY:
|
|
|
|
case GB_IO_BGP:
|
|
|
|
case GB_IO_OBP0:
|
|
|
|
case GB_IO_OBP1:
|
|
|
|
case GB_IO_SB:
|
2017-06-21 06:35:57 +00:00
|
|
|
case GB_IO_UNKNOWN2:
|
|
|
|
case GB_IO_UNKNOWN3:
|
|
|
|
case GB_IO_UNKNOWN4:
|
|
|
|
case GB_IO_UNKNOWN5:
|
2016-03-30 20:07:55 +00:00
|
|
|
gb->io_registers[addr & 0xFF] = value;
|
|
|
|
return;
|
2020-02-15 13:32:06 +00:00
|
|
|
case GB_IO_OBJECT_PRIORITY:
|
|
|
|
if ((!gb->boot_rom_finished || (gb->io_registers[GB_IO_MODE] & 8)) && GB_is_cgb(gb)) {
|
|
|
|
gb->io_registers[addr & 0xFF] = value;
|
|
|
|
gb->object_priority = (value & 1) ? GB_OBJECT_PRIORITY_X : GB_OBJECT_PRIORITY_INDEX;
|
|
|
|
}
|
|
|
|
else if (gb->cgb_mode) {
|
|
|
|
gb->io_registers[addr & 0xFF] = value;
|
|
|
|
|
|
|
|
}
|
|
|
|
return;
|
2018-02-24 22:48:45 +00:00
|
|
|
case GB_IO_LYC:
|
2018-05-11 09:38:55 +00:00
|
|
|
|
|
|
|
/* TODO: Probably completely wrong in double speed mode */
|
|
|
|
|
|
|
|
/* TODO: This hack is disgusting */
|
2018-06-16 10:59:33 +00:00
|
|
|
if (gb->display_state == 29 && GB_is_cgb(gb)) {
|
2018-05-11 09:38:55 +00:00
|
|
|
gb->ly_for_comparison = 153;
|
|
|
|
GB_STAT_update(gb);
|
|
|
|
gb->ly_for_comparison = 0;
|
|
|
|
}
|
|
|
|
|
2018-02-24 22:48:45 +00:00
|
|
|
gb->io_registers[addr & 0xFF] = value;
|
2018-05-11 09:38:55 +00:00
|
|
|
|
|
|
|
/* These are the states when LY changes, let the display routine call GB_STAT_update for use
|
|
|
|
so it correctly handles T-cycle accurate LYC writes */
|
2018-06-16 10:59:33 +00:00
|
|
|
if (!GB_is_cgb(gb) || (
|
2019-07-11 23:18:25 +00:00
|
|
|
gb->display_state != 35 &&
|
2018-05-11 09:38:55 +00:00
|
|
|
gb->display_state != 26 &&
|
|
|
|
gb->display_state != 15 &&
|
|
|
|
gb->display_state != 16)) {
|
|
|
|
|
|
|
|
/* More hacks to make LYC write conflicts work */
|
2018-06-16 10:59:33 +00:00
|
|
|
if (gb->display_state == 14 && GB_is_cgb(gb)) {
|
2018-05-11 09:38:55 +00:00
|
|
|
gb->ly_for_comparison = 153;
|
|
|
|
GB_STAT_update(gb);
|
|
|
|
gb->ly_for_comparison = -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
GB_STAT_update(gb);
|
|
|
|
}
|
|
|
|
}
|
2018-02-24 22:48:45 +00:00
|
|
|
return;
|
2016-08-06 10:56:29 +00:00
|
|
|
|
|
|
|
case GB_IO_TIMA:
|
|
|
|
if (gb->tima_reload_state != GB_TIMA_RELOADED) {
|
|
|
|
gb->io_registers[GB_IO_TIMA] = value;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case GB_IO_TMA:
|
|
|
|
gb->io_registers[GB_IO_TMA] = value;
|
2016-08-06 11:36:33 +00:00
|
|
|
if (gb->tima_reload_state != GB_TIMA_RUNNING) {
|
2016-08-06 10:56:29 +00:00
|
|
|
gb->io_registers[GB_IO_TIMA] = value;
|
|
|
|
}
|
|
|
|
return;
|
2016-03-30 20:07:55 +00:00
|
|
|
|
2016-08-05 14:22:12 +00:00
|
|
|
case GB_IO_TAC:
|
|
|
|
GB_emulate_timer_glitch(gb, gb->io_registers[GB_IO_TAC], value);
|
|
|
|
gb->io_registers[GB_IO_TAC] = value;
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_LCDC:
|
|
|
|
if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
2017-06-17 19:17:58 +00:00
|
|
|
gb->display_cycles = 0;
|
2018-02-24 22:48:45 +00:00
|
|
|
gb->display_state = 0;
|
2019-02-15 15:04:48 +00:00
|
|
|
if (GB_is_sgb(gb)) {
|
|
|
|
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
|
|
|
}
|
|
|
|
else if (gb->frame_skip_state == GB_FRAMESKIP_SECOND_FRAME_RENDERED) {
|
2017-06-03 13:42:42 +00:00
|
|
|
gb->frame_skip_state = GB_FRAMESKIP_LCD_TURNED_ON;
|
|
|
|
}
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
2017-04-21 13:00:53 +00:00
|
|
|
else if (!(value & 0x80) && (gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
|
|
|
/* Sync after turning off LCD */
|
|
|
|
GB_timing_sync(gb);
|
2018-02-24 22:48:45 +00:00
|
|
|
GB_lcd_off(gb);
|
2017-04-21 13:00:53 +00:00
|
|
|
}
|
2020-03-06 16:56:51 +00:00
|
|
|
/* Handle disabling objects while already fetching an object */
|
|
|
|
if ((gb->io_registers[GB_IO_LCDC] & 2) && !(value & 2)) {
|
|
|
|
if (gb->during_object_fetch) {
|
|
|
|
gb->cycles_for_line += gb->display_cycles;
|
|
|
|
gb->display_cycles = 0;
|
|
|
|
gb->object_fetch_aborted = true;
|
|
|
|
}
|
|
|
|
}
|
2020-02-22 22:38:47 +00:00
|
|
|
gb->io_registers[GB_IO_LCDC] = value;
|
2020-03-03 00:21:19 +00:00
|
|
|
if (!(value & 0x20)) {
|
|
|
|
gb->wx_triggered = false;
|
|
|
|
gb->wx166_glitch = false;
|
|
|
|
}
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
case GB_IO_STAT:
|
|
|
|
/* Delete previous R/W bits */
|
|
|
|
gb->io_registers[GB_IO_STAT] &= 7;
|
|
|
|
/* Set them by value */
|
|
|
|
gb->io_registers[GB_IO_STAT] |= value & ~7;
|
|
|
|
/* Set unused bit to 1 */
|
|
|
|
gb->io_registers[GB_IO_STAT] |= 0x80;
|
2018-02-24 22:48:45 +00:00
|
|
|
|
|
|
|
GB_STAT_update(gb);
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
case GB_IO_DIV:
|
2018-02-23 13:33:44 +00:00
|
|
|
/* Reset the div state machine */
|
|
|
|
gb->div_state = 0;
|
|
|
|
gb->div_cycles = 0;
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
case GB_IO_JOYP:
|
2019-11-09 12:45:38 +00:00
|
|
|
if (gb->joyp_write_callback) {
|
|
|
|
gb->joyp_write_callback(gb, value);
|
|
|
|
GB_update_joyp(gb);
|
|
|
|
}
|
|
|
|
else if ((gb->io_registers[GB_IO_JOYP] & 0x30) != (value & 0x30)) {
|
2019-09-13 10:13:28 +00:00
|
|
|
GB_sgb_write(gb, value);
|
2020-03-27 14:36:55 +00:00
|
|
|
gb->io_registers[GB_IO_JOYP] = (value & 0xF0) | (gb->io_registers[GB_IO_JOYP] & 0x0F);
|
2019-09-13 10:13:28 +00:00
|
|
|
GB_update_joyp(gb);
|
|
|
|
}
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
case GB_IO_BIOS:
|
2016-06-18 17:29:11 +00:00
|
|
|
gb->boot_rom_finished = true;
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
|
2020-02-15 13:32:06 +00:00
|
|
|
case GB_IO_MODE:
|
2018-06-16 10:59:33 +00:00
|
|
|
if (GB_is_cgb(gb) && !gb->boot_rom_finished) {
|
2018-07-20 20:23:47 +00:00
|
|
|
gb->cgb_mode = !(value & 0xC); /* The real "contents" of this register aren't quite known yet. */
|
2020-02-15 13:32:06 +00:00
|
|
|
gb->io_registers[GB_IO_MODE] = value;
|
2016-04-05 20:21:51 +00:00
|
|
|
}
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
case GB_IO_DMA:
|
2018-03-22 18:09:01 +00:00
|
|
|
if (gb->dma_steps_left) {
|
|
|
|
/* This is not correct emulation, since we're not really delaying the second DMA.
|
|
|
|
One write that should have happened in the first DMA will not happen. However,
|
|
|
|
since that byte will be overwritten by the second DMA before it can actually be
|
|
|
|
read, it doesn't actually matter. */
|
|
|
|
gb->is_dma_restarting = true;
|
|
|
|
}
|
|
|
|
gb->dma_cycles = -7;
|
|
|
|
gb->dma_current_dest = 0;
|
|
|
|
gb->dma_current_src = value << 8;
|
|
|
|
gb->dma_steps_left = 0xa0;
|
|
|
|
gb->io_registers[GB_IO_DMA] = value;
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
case GB_IO_SVBK:
|
|
|
|
if (!gb->cgb_mode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gb->cgb_ram_bank = value & 0x7;
|
|
|
|
if (!gb->cgb_ram_bank) {
|
|
|
|
gb->cgb_ram_bank++;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case GB_IO_VBK:
|
|
|
|
if (!gb->cgb_mode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gb->cgb_vram_bank = value & 0x1;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case GB_IO_BGPI:
|
|
|
|
case GB_IO_OBPI:
|
2018-06-16 10:59:33 +00:00
|
|
|
if (!GB_is_cgb(gb)) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
gb->io_registers[addr & 0xFF] = value;
|
|
|
|
return;
|
|
|
|
case GB_IO_BGPD:
|
|
|
|
case GB_IO_OBPD:
|
2016-10-01 19:10:44 +00:00
|
|
|
if (!gb->cgb_mode && gb->boot_rom_finished) {
|
|
|
|
/* Todo: Due to the behavior of a broken Game & Watch Gallery 2 ROM on a real CGB. A proper test ROM
|
|
|
|
is required. */
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-01-19 21:37:44 +00:00
|
|
|
|
|
|
|
uint8_t index_reg = (addr & 0xFF) - 1;
|
2019-01-19 17:32:26 +00:00
|
|
|
if (gb->cgb_palettes_blocked) {
|
2019-01-19 21:37:44 +00:00
|
|
|
if (gb->io_registers[index_reg] & 0x80) {
|
|
|
|
gb->io_registers[index_reg]++;
|
|
|
|
gb->io_registers[index_reg] |= 0x80;
|
|
|
|
}
|
2019-01-14 20:22:46 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-03-30 20:07:55 +00:00
|
|
|
((addr & 0xFF) == GB_IO_BGPD?
|
2017-04-19 20:26:39 +00:00
|
|
|
gb->background_palettes_data :
|
|
|
|
gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F] = value;
|
2016-06-18 17:29:11 +00:00
|
|
|
GB_palette_changed(gb, (addr & 0xFF) == GB_IO_BGPD, gb->io_registers[index_reg] & 0x3F);
|
2016-03-30 20:07:55 +00:00
|
|
|
if (gb->io_registers[index_reg] & 0x80) {
|
|
|
|
gb->io_registers[index_reg]++;
|
|
|
|
gb->io_registers[index_reg] |= 0x80;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case GB_IO_KEY1:
|
2017-08-12 18:42:47 +00:00
|
|
|
if (!gb->cgb_mode) {
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
gb->io_registers[GB_IO_KEY1] = value;
|
|
|
|
return;
|
2017-02-08 19:58:15 +00:00
|
|
|
case GB_IO_HDMA1:
|
2017-06-21 06:35:57 +00:00
|
|
|
if (gb->cgb_mode) {
|
2017-02-08 19:58:15 +00:00
|
|
|
gb->hdma_current_src &= 0xF0;
|
|
|
|
gb->hdma_current_src |= value << 8;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case GB_IO_HDMA2:
|
2017-06-21 06:35:57 +00:00
|
|
|
if (gb->cgb_mode) {
|
2017-02-08 19:58:15 +00:00
|
|
|
gb->hdma_current_src &= 0xFF00;
|
|
|
|
gb->hdma_current_src |= value & 0xF0;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case GB_IO_HDMA3:
|
2017-06-21 06:35:57 +00:00
|
|
|
if (gb->cgb_mode) {
|
2017-02-08 19:58:15 +00:00
|
|
|
gb->hdma_current_dest &= 0xF0;
|
|
|
|
gb->hdma_current_dest |= value << 8;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case GB_IO_HDMA4:
|
2017-06-21 06:35:57 +00:00
|
|
|
if (gb->cgb_mode) {
|
2017-02-08 19:58:15 +00:00
|
|
|
gb->hdma_current_dest &= 0x1F00;
|
|
|
|
gb->hdma_current_dest |= value & 0xF0;
|
|
|
|
}
|
|
|
|
return;
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_HDMA5:
|
2017-06-21 06:35:57 +00:00
|
|
|
if (!gb->cgb_mode) return;
|
2016-03-30 20:07:55 +00:00
|
|
|
if ((value & 0x80) == 0 && gb->hdma_on_hblank) {
|
|
|
|
gb->hdma_on_hblank = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gb->hdma_on = (value & 0x80) == 0;
|
|
|
|
gb->hdma_on_hblank = (value & 0x80) != 0;
|
2017-02-25 13:42:26 +00:00
|
|
|
if (gb->hdma_on_hblank && (gb->io_registers[GB_IO_STAT] & 3) == 0) {
|
|
|
|
gb->hdma_on = true;
|
|
|
|
}
|
2016-03-30 20:07:55 +00:00
|
|
|
gb->io_registers[GB_IO_HDMA5] = value;
|
|
|
|
gb->hdma_steps_left = (gb->io_registers[GB_IO_HDMA5] & 0x7F) + 1;
|
2016-06-18 13:51:25 +00:00
|
|
|
/* Todo: Verify this. Gambatte's DMA tests require this. */
|
|
|
|
if (gb->hdma_current_dest + (gb->hdma_steps_left << 4) > 0xFFFF) {
|
|
|
|
gb->hdma_steps_left = (0x10000 - gb->hdma_current_dest) >> 4;
|
|
|
|
}
|
2018-09-14 21:48:31 +00:00
|
|
|
gb->hdma_cycles = -12;
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
|
2016-09-05 19:13:28 +00:00
|
|
|
/* Todo: what happens when starting a transfer during a transfer?
|
|
|
|
What happens when starting a transfer during external clock?
|
|
|
|
*/
|
2016-03-30 20:07:55 +00:00
|
|
|
case GB_IO_SC:
|
2016-09-05 19:13:28 +00:00
|
|
|
if (!gb->cgb_mode) {
|
|
|
|
value |= 2;
|
|
|
|
}
|
|
|
|
gb->io_registers[GB_IO_SC] = value | (~0x83);
|
2016-03-30 20:07:55 +00:00
|
|
|
if ((value & 0x80) && (value & 0x1) ) {
|
2019-03-15 12:36:10 +00:00
|
|
|
gb->serial_length = gb->cgb_mode && (value & 2)? 16 : 512;
|
|
|
|
gb->serial_count = 0;
|
2017-06-23 14:58:04 +00:00
|
|
|
/* Todo: This is probably incorrect for CGB's faster clock mode. */
|
|
|
|
gb->serial_cycles &= 0xFF;
|
2019-03-15 12:36:10 +00:00
|
|
|
if (gb->serial_transfer_bit_start_callback) {
|
|
|
|
gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80);
|
2016-11-11 23:58:53 +00:00
|
|
|
}
|
2016-09-05 19:13:28 +00:00
|
|
|
}
|
|
|
|
else {
|
2017-06-23 14:58:04 +00:00
|
|
|
gb->serial_length = 0;
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
2016-07-18 19:05:11 +00:00
|
|
|
case GB_IO_RP: {
|
2018-06-16 10:59:33 +00:00
|
|
|
if (!GB_is_cgb(gb)) {
|
2016-07-18 19:05:11 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((value & 1) != (gb->io_registers[GB_IO_RP] & 1)) {
|
|
|
|
if (gb->infrared_callback) {
|
2016-07-20 22:03:13 +00:00
|
|
|
gb->infrared_callback(gb, value & 1, gb->cycles_since_ir_change);
|
|
|
|
gb->cycles_since_ir_change = 0;
|
2016-07-18 19:05:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
gb->io_registers[GB_IO_RP] = value;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-30 20:07:55 +00:00
|
|
|
default:
|
|
|
|
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) {
|
2016-06-18 17:29:11 +00:00
|
|
|
GB_apu_write(gb, addr & 0xFF, value);
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-09-03 00:39:32 +00:00
|
|
|
GB_log(gb, "Wrote %02x to %04x (HW Register)\n", value, addr);
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr == 0xFFFF) {
|
|
|
|
/* Interrupt mask */
|
2016-08-05 13:36:38 +00:00
|
|
|
gb->interrupt_enable = value;
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* HRAM */
|
|
|
|
gb->hram[addr - 0xFF80] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static GB_write_function_t * const write_map[] =
|
|
|
|
{
|
|
|
|
write_mbc, write_mbc, write_mbc, write_mbc, /* 0XXX, 1XXX, 2XXX, 3XXX */
|
|
|
|
write_mbc, write_mbc, write_mbc, write_mbc, /* 4XXX, 5XXX, 6XXX, 7XXX */
|
|
|
|
write_vram, write_vram, /* 8XXX, 9XXX */
|
|
|
|
write_mbc_ram, write_mbc_ram, /* AXXX, BXXX */
|
|
|
|
write_ram, write_banked_ram, /* CXXX, DXXX */
|
2018-12-12 21:44:00 +00:00
|
|
|
write_ram, write_high_memory, /* EXXX FXXX */
|
2016-03-30 20:07:55 +00:00
|
|
|
};
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
2017-10-12 16:05:27 +00:00
|
|
|
if (gb->n_watchpoints) {
|
2016-10-21 21:49:32 +00:00
|
|
|
GB_debugger_test_write_watchpoint(gb, addr, value);
|
2017-10-12 16:05:27 +00:00
|
|
|
}
|
2016-08-03 20:31:10 +00:00
|
|
|
if (is_addr_in_dma_use(gb, addr)) {
|
|
|
|
/* Todo: What should happen? Will this affect DMA? Will data be written? What and where? */
|
2016-03-30 20:07:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
write_map[addr >> 12](gb, addr, value);
|
|
|
|
}
|
|
|
|
|
2016-08-03 20:31:10 +00:00
|
|
|
void GB_dma_run(GB_gameboy_t *gb)
|
|
|
|
{
|
2016-08-06 10:57:38 +00:00
|
|
|
while (gb->dma_cycles >= 4 && gb->dma_steps_left) {
|
2016-08-03 20:31:10 +00:00
|
|
|
/* Todo: measure this value */
|
|
|
|
gb->dma_cycles -= 4;
|
|
|
|
gb->dma_steps_left--;
|
2018-04-13 11:41:39 +00:00
|
|
|
|
2018-03-22 18:09:01 +00:00
|
|
|
if (gb->dma_current_src < 0xe000) {
|
|
|
|
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Todo: Not correct on the CGB */
|
|
|
|
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src & ~0x2000);
|
|
|
|
}
|
|
|
|
|
2016-08-03 20:31:10 +00:00
|
|
|
/* dma_current_src must be the correct value during GB_read_memory */
|
|
|
|
gb->dma_current_src++;
|
2016-08-06 11:24:43 +00:00
|
|
|
if (!gb->dma_steps_left) {
|
|
|
|
gb->is_dma_restarting = false;
|
|
|
|
}
|
2016-08-03 20:31:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-18 17:29:11 +00:00
|
|
|
void GB_hdma_run(GB_gameboy_t *gb)
|
2016-03-30 20:07:55 +00:00
|
|
|
{
|
|
|
|
if (!gb->hdma_on) return;
|
2018-06-16 20:52:24 +00:00
|
|
|
|
2018-04-13 11:41:39 +00:00
|
|
|
while (gb->hdma_cycles >= 0x4) {
|
|
|
|
gb->hdma_cycles -= 0x4;
|
2017-02-08 19:58:15 +00:00
|
|
|
|
2018-04-13 11:41:39 +00:00
|
|
|
GB_write_memory(gb, 0x8000 | (gb->hdma_current_dest++ & 0x1FFF), GB_read_memory(gb, (gb->hdma_current_src++)));
|
2017-02-06 22:24:26 +00:00
|
|
|
|
2018-04-13 11:41:39 +00:00
|
|
|
if ((gb->hdma_current_dest & 0xf) == 0) {
|
2018-06-16 20:52:24 +00:00
|
|
|
if (--gb->hdma_steps_left == 0) {
|
2018-04-13 11:41:39 +00:00
|
|
|
gb->hdma_on = false;
|
|
|
|
gb->hdma_on_hblank = false;
|
2018-06-16 20:52:24 +00:00
|
|
|
gb->hdma_starting = false;
|
2018-04-13 11:41:39 +00:00
|
|
|
gb->io_registers[GB_IO_HDMA5] &= 0x7F;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (gb->hdma_on_hblank) {
|
|
|
|
gb->hdma_on = false;
|
|
|
|
break;
|
|
|
|
}
|
2016-03-30 20:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-06 22:24:26 +00:00
|
|
|
}
|