GBHawk: finish PPU ports

This commit is contained in:
alyosha-tas 2020-03-25 20:52:16 -04:00
parent 6ee8d6d312
commit 37e8b29056
10 changed files with 3782 additions and 71 deletions

View File

@ -28,9 +28,12 @@ namespace GBHawk
ppu._scanlineCallbackLine = &MemMap._scanlineCallbackLine;
ppu.OAM = &MemMap.OAM[0];
ppu.VRAM = &MemMap.VRAM[0];
ppu.VRAM_Bank = &MemMap.VRAM_Bank;
ppu.cpu_halted = &cpu.halted;
ppu._vidbuffer = &MemMap._vidbuffer[0];
ppu.color_palette = &MemMap.color_palette[0];
ppu.HDMA_transfer = &MemMap.HDMA_transfer;
ppu.GBC_compat = &MemMap.GBC_compat;
sl_case = 0;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -153,6 +153,8 @@
<ItemGroup>
<ClInclude Include="GBAudio.h" />
<ClInclude Include="Core.h" />
<ClInclude Include="GBC_GB_PPU.h" />
<ClInclude Include="GBC_PPU.h" />
<ClInclude Include="GBHawk.h" />
<ClInclude Include="GB_PPU.h" />
<ClInclude Include="Memory.h" />

View File

@ -13,8 +13,6 @@ namespace GBHawk
class GB_PPU : public PPU
{
public:
#pragma region PPU
uint8_t ReadReg(uint32_t addr)
{
uint8_t ret = 0;
@ -559,8 +557,8 @@ namespace GBHawk
if (render_counter >= (render_offset + 8))
{
pixel = tile_data_latch[0] & (1 << (7 - (render_counter % 8))) > 0 ? 1 : 0;
pixel |= tile_data_latch[1] & (1 << (7 - (render_counter % 8))) > 0 ? 2 : 0;
pixel = (tile_data_latch[0] & (1 << (7 - (render_counter % 8))) > 0) ? 1 : 0;
pixel |= (tile_data_latch[1] & (1 << (7 - (render_counter % 8))) > 0) ? 2 : 0;
uint32_t ref_pixel = pixel;
if (((LCDC & 0x1) > 0))
@ -665,7 +663,7 @@ namespace GBHawk
if ((internal_cycle % 2) == 1)
{
// calculate the row number of the tiles to be fetched
y_tile = ((uint32_t)floor((float)(scroll_y + LY) / 8.0)) % 32;
y_tile = ((uint32_t)floor((float)((uint32_t)scroll_y + LY) / 8.0)) % 32;
temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32;
tile_byte = VRAM[0x1800 + (((LCDC & 0x8) > 0) ? 1 : 0) * 0x400 + temp_fetch];
@ -945,7 +943,7 @@ namespace GBHawk
else if (((last_eval + render_offset) % 8) == 6) { sprite_fetch_counter += 0; }
else if (((last_eval + render_offset) % 8) == 7) { sprite_fetch_counter += 0; }
consecutive_sprite = (uint32_t)floor((double)(last_eval + render_offset) / 8.0) * 8 + 8 - render_offset;
consecutive_sprite = (uint32_t)floor((double)((uint32_t)last_eval + render_offset) / 8.0) * 8 + 8 - render_offset;
// special case exists here for sprites at zero with non-zero x-scroll. Not sure exactly the reason for it.
if (last_eval == 0 && render_offset != 0)
@ -1043,12 +1041,12 @@ namespace GBHawk
// So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF)
uint8_t DMA_actual = DMA_addr;
if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; }
DMA_uint8_t = ReadMemory((uint32_t)((DMA_actual << 8) + DMA_inc));
DMA_byte = ReadMemory((uint32_t)((DMA_actual << 8) + DMA_inc));
DMA_start = true;
}
else if ((DMA_clock % 4) == 3)
{
OAM[DMA_inc] = DMA_uint8_t;
OAM[DMA_inc] = DMA_byte;
if (DMA_inc < (0xA0 - 1)) { DMA_inc++; }
}

View File

@ -11,21 +11,21 @@ namespace GBHawk
{
void LR35902::WriteMemory(uint32_t addr, uint8_t value)
{
mem_ctrl->MemoryWrite(addr, value);
mem_ctrl->WriteMemory(addr, value);
}
uint8_t LR35902::ReadMemory(uint32_t addr)
{
return mem_ctrl->HardwareRead(addr);
return mem_ctrl->ReadMemory(addr);
}
uint8_t LR35902::PeekMemory(uint32_t addr)
{
return mem_ctrl->HardwareRead(addr);
return mem_ctrl->PeekMemory(addr);
}
uint8_t LR35902::SpeedFunc(uint32_t addr)
uint8_t LR35902::SpeedFunc(uint32_t val)
{
return mem_ctrl->HardwareRead(addr);
return mem_ctrl->SpeedFunc(val);
}
}

View File

@ -12,14 +12,397 @@ using namespace std;
namespace GBHawk
{
uint8_t MemoryManager::HardwareRead(uint32_t port)
{
/*
$FFFF Interrupt Enable Flag
$FF80-$FFFE Zero Page - 127 bytes
$FF00-$FF7F Hardware I/O Registers
$FEA0-$FEFF Unusable Memory
$FE00-$FE9F OAM - Object Attribute Memory
$E000-$FDFF Echo RAM - Reserved, Do Not Use
$D000-$DFFF Internal RAM - Bank 1-7 (switchable - CGB only)
$C000-$CFFF Internal RAM - Bank 0 (fixed)
$A000-$BFFF Cartridge RAM (If Available)
$9C00-$9FFF BG Map Data 2
$9800-$9BFF BG Map Data 1
$8000-$97FF Character RAM
$4000-$7FFF Cartridge ROM - Switchable Banks 1-xx
$0150-$3FFF Cartridge ROM - Bank 0 (fixed)
$0100-$014F Cartridge Header Area
$0000-$00FF Restart and Interrupt Vectors
*/
/*
* VRAM is arranged as:
* 0x1800 Tiles
* 0x400 BG Map 1
* 0x400 BG Map 2
* 0x1800 Tiles
* 0x400 CA Map 1
* 0x400 CA Map 2
* Only the top set is available in GB (i.e. VRAM_Bank = 0)
*/
uint8_t MemoryManager::ReadMemory(uint32_t addr)
{
//uint flags = (uint)(MemoryCallbackFlags.AccessRead);
//MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "System Bus");
addr_access = addr;
if (ppu_pntr->DMA_start)
{
// some of gekkio's tests require these to be accessible during DMA
if (addr < 0x8000)
{
if (ppu_pntr->DMA_addr < 0x80)
{
return 0xFF;
}
else
{
return mapper.ReadMemory(addr);
}
}
else if ((addr >= 0xE000) && (addr < 0xF000))
{
return RAM[addr - 0xE000];
}
else if ((addr >= 0xF000) && (addr < 0xFE00))
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)];
}
else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access)
{
return OAM[addr - 0xFE00];
}
else if ((addr >= 0xFF00) && (addr < 0xFF80)) // The game GOAL! Requires Hardware Regs to be accessible
{
return Read_Registers(addr);
}
else if ((addr >= 0xFF80))
{
return ZP_RAM[addr - 0xFF80];
}
return 0xFF;
}
if (addr < 0x900)
{
if (addr < 0x100)
{
// return Either BIOS ROM or Game ROM
if ((GB_bios_register & 0x1) == 0)
{
return bios_rom[addr]; // Return BIOS
}
else
{
return mapper.ReadMemory(addr);
}
}
else if (addr >= 0x200)
{
// return Either BIOS ROM or Game ROM
if (((GB_bios_register & 0x1) == 0) && is_GBC)
{
return bios_rom[addr]; // Return BIOS
}
else
{
return mapper.ReadMemory(addr);
}
}
else
{
return mapper.ReadMemory(addr);
}
}
else if (addr < 0x8000)
{
return mapper.ReadMemory(addr);
}
else if (addr < 0xA000)
{
if (ppu_pntr->VRAM_access_read) { return VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)]; }
else { return 0xFF; }
}
else if (addr < 0xC000)
{
return mapper.ReadMemory(addr);
}
else if (addr < 0xD000)
{
return RAM[addr - 0xC000];
}
else if (addr < 0xE000)
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)];
}
else if (addr < 0xF000)
{
return RAM[addr - 0xE000];
}
else if (addr < 0xFE00)
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)];
}
else if (addr < 0xFEA0)
{
if (ppu_pntr->OAM_access_read) { return OAM[addr - 0xFE00]; }
else { return 0xFF; }
}
else if (addr < 0xFF00)
{
// unmapped memory, returns 0xFF
return 0xFF;
}
else if (addr < 0xFF80)
{
return Read_Registers(addr);
}
else if (addr < 0xFFFF)
{
return ZP_RAM[addr - 0xFF80];
}
else
{
return Read_Registers(addr);
}
return 0xFF;
}
void MemoryManager::HardwareWrite(uint32_t port, uint8_t value)
void MemoryManager::WriteMemory(uint32_t addr, uint8_t value)
{
//uint flags = (uint)(MemoryCallbackFlags.AccessWrite);
//MemoryCallbacks.CallMemoryCallbacks(addr, value, flags, "System Bus");
addr_access = addr;
if (ppu_pntr->DMA_start)
{
// some of gekkio's tests require this to be accessible during DMA
if ((addr >= 0xE000) && (addr < 0xF000))
{
RAM[addr - 0xE000] = value;
}
else if ((addr >= 0xF000) && (addr < 0xFE00))
{
RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)] = value;
}
else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access)
{
OAM[addr - 0xFE00] = value;
}
else if ((addr >= 0xFF00) && (addr < 0xFF80)) // The game GOAL! Requires Hardware Regs to be accessible
{
Write_Registers(addr, value);
}
else if ((addr >= 0xFF80))
{
ZP_RAM[addr - 0xFF80] = value;
}
return;
}
if (addr < 0x900)
{
if (addr < 0x100)
{
if ((GB_bios_register & 0x1) == 0)
{
// No Writing to BIOS
}
else
{
mapper.WriteMemory(addr, value);
}
}
else if (addr >= 0x200)
{
if (((GB_bios_register & 0x1) == 0) && is_GBC)
{
// No Writing to BIOS
}
else
{
mapper.WriteMemory(addr, value);
}
}
else
{
mapper.WriteMemory(addr, value);
}
}
else if (addr < 0x8000)
{
mapper.WriteMemory(addr, value);
}
else if (addr < 0xA000)
{
if (ppu_pntr->VRAM_access_write) { VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)] = value; }
}
else if (addr < 0xC000)
{
mapper.WriteMemory(addr, value);
}
else if (addr < 0xD000)
{
RAM[addr - 0xC000] = value;
}
else if (addr < 0xE000)
{
RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)] = value;
}
else if (addr < 0xF000)
{
RAM[addr - 0xE000] = value;
}
else if (addr < 0xFE00)
{
RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)] = value;
}
else if (addr < 0xFEA0)
{
if (ppu_pntr->OAM_access_write) { OAM[addr - 0xFE00] = value; }
}
else if (addr < 0xFF00)
{
// unmapped, writing has no effect
}
else if (addr < 0xFF80)
{
Write_Registers(addr, value);
}
else if (addr < 0xFFFF)
{
ZP_RAM[addr - 0xFF80] = value;
}
else
{
Write_Registers(addr, value);
}
}
uint8_t MemoryManager::PeekMemory(uint32_t addr)
{
if (ppu_pntr->DMA_start)
{
// some of gekkio's tests require these to be accessible during DMA
if (addr < 0x8000)
{
if (ppu.DMA_addr < 0x80)
{
return 0xFF;
}
else
{
return mapper.ReadMemory(addr);
}
}
else if ((addr >= 0xE000) && (addr < 0xF000))
{
return RAM[addr - 0xE000];
}
else if ((addr >= 0xF000) && (addr < 0xFE00))
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)];
}
else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access)
{
return OAM[addr - 0xFE00];
}
else if ((addr >= 0xFF00) && (addr < 0xFF80)) // The game GOAL! Requires Hardware Regs to be accessible
{
return Read_Registers(addr);
}
else if ((addr >= 0xFF80))
{
return ZP_RAM[addr - 0xFF80];
}
return 0xFF;
}
if (addr < 0x900)
{
if (addr < 0x100)
{
// return Either BIOS ROM or Game ROM
if ((GB_bios_register & 0x1) == 0)
{
return bios_rom[addr]; // Return BIOS
}
else
{
return mapper.ReadMemory(addr);
}
}
else if (addr >= 0x200)
{
// return Either BIOS ROM or Game ROM
if (((GB_bios_register & 0x1) == 0) && is_GBC)
{
return bios_rom[addr]; // Return BIOS
}
else
{
return mapper.ReadMemory(addr);
}
}
else
{
return mapper.ReadMemory(addr);
}
}
else if (addr < 0x8000)
{
return mapper.PeekMemory(addr);
}
else if (addr < 0xA000)
{
if (ppu_pntr->VRAM_access_read) { return VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)]; }
else { return 0xFF; }
}
else if (addr < 0xC000)
{
return mapper.PeekMemory(addr);
}
else if (addr < 0xD000)
{
return RAM[addr - 0xC000];
}
else if (addr < 0xE000)
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)];
}
else if (addr < 0xF000)
{
return RAM[addr - 0xE000];
}
else if (addr < 0xFE00)
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)];
}
else if (addr < 0xFEA0)
{
if (ppu_pntr->OAM_access_read) { return OAM[addr - 0xFE00]; }
else { return 0xFF; }
}
else if (addr < 0xFF00)
{
// unmapped memory, returns 0xFF
return 0xFF;
}
else if (addr < 0xFF80)
{
return Read_Registers(addr);
}
else if (addr < 0xFFFF)
{
return ZP_RAM[addr - 0xFF80];
}
else
{
return Read_Registers(addr);
}
}
}

View File

@ -15,6 +15,17 @@ namespace GBHawk
{
public:
MemoryManager()
{
};
uint8_t ReadMemory(uint32_t addr);
uint8_t PeekMemory(uint32_t addr);
void WriteMemory(uint32_t addr, uint8_t value);
#pragma region Declarations
PPU* ppu_pntr = nullptr;
GBAudio* psg_pntr = nullptr;
LR35902* cpu_pntr = nullptr;
@ -36,11 +47,20 @@ namespace GBHawk
bool PortDEEnabled = false;
bool lagged;
bool start_pressed;
bool is_GBC;
bool GBC_compat;
bool speed_switch, double_speed;
bool in_vblank;
bool GB_bios_register;
bool HDMA_transfer;
uint8_t addr_access;
uint8_t REG_FFFF, REG_FF0F;
uint8_t _scanlineCallbackLine;
uint32_t RAM_Bank;
uint32_t VRAM_Bank;
uint8_t kb_rows_sel;
uint8_t PortA8 = 0x00;
uint8_t reg_FFFC, reg_FFFD, reg_FFFE, reg_FFFF;
uint8_t RAM[0x10000] = {};
uint8_t ZP_RAM[0x80] = {};
uint8_t RAM[0x8000] = {};
uint8_t VRAM[0x10000] = {};
uint8_t OAM[0x10000] = {};
uint8_t cart_ram[0x8000] = {};
@ -48,39 +68,24 @@ namespace GBHawk
uint32_t _vidbuffer[160 * 144] = {};
uint32_t color_palette[4] = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 };
uint32_t FrameBuffer[0x400] = {};
uint32_t FrameBuffer[160 * 144] = {};
// state shared amongst different components
bool in_vblank;
uint8_t REG_FFFF, REG_FF0F;
uint8_t _scanlineCallbackLine;
#pragma endregion
MemoryManager()
#pragma region Functions
// NOTE: only called when checks pass that the files are correct
void Load_BIOS(uint8_t* bios, bool GBC_console)
{
};
uint8_t HardwareRead(uint32_t value);
void HardwareWrite(uint32_t addr, uint8_t value);
// NOTE: only called from source when both are available and of correct size (0x4000)
void Load_BIOS(uint8_t* bios, bool GBC_console)
{
if (GBC_console)
if (GBC_console)
{
bios_rom = new uint8_t[2304];
memcpy(bios_rom, bios, 2304);
}
else
else
{
bios_rom = new uint8_t[256];
memcpy(bios_rom, bios, 256);
}
}
void Load_ROM(uint8_t* ext_rom_1, uint32_t ext_rom_size_1, uint32_t ext_rom_mapper_1, uint8_t* ext_rom_2, uint32_t ext_rom_size_2, uint32_t ext_rom_mapper_2)
@ -96,16 +101,33 @@ namespace GBHawk
rom_size_2 = ext_rom_size_2 / 0x4000;
rom_mapper_2 = ext_rom_mapper_2;
// default memory map setup
PortA8 = 0;
}
void MemoryWrite(uint32_t addr, uint8_t value)
// Switch Speed (GBC only)
uint32_t SpeedFunc(uint32_t temp)
{
if (is_GBC)
{
if (speed_switch)
{
speed_switch = false;
uint32_t ret = double_speed ? 70224 * 2 : 70224 * 2; // actual time needs checking
double_speed = !double_speed;
return ret;
}
// if we are not switching speed, return 0
return 0;
}
// if we are in GB mode, return 0 indicating not switching speed
return 0;
}
#pragma endregion
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
@ -114,13 +136,6 @@ namespace GBHawk
*saver = (uint8_t)(lagged ? 1 : 0); saver++;
*saver = (uint8_t)(start_pressed ? 1 : 0); saver++;
*saver = kb_rows_sel; saver++;
*saver = PortA8; saver++;
*saver = reg_FFFC; saver++;
*saver = reg_FFFD; saver++;
*saver = reg_FFFE; saver++;
*saver = reg_FFFF; saver++;
std::memcpy(saver, &RAM, 0x10000); saver += 0x10000;
std::memcpy(saver, &cart_ram, 0x8000); saver += 0x8000;
@ -133,13 +148,6 @@ namespace GBHawk
lagged = *loader == 1; loader++;
start_pressed = *loader == 1; loader++;
kb_rows_sel = *loader; loader++;
PortA8 = *loader; loader++;
reg_FFFC = *loader; loader++;
reg_FFFD = *loader; loader++;
reg_FFFE = *loader; loader++;
reg_FFFF = *loader; loader++;
std::memcpy(&RAM, loader, 0x10000); loader += 0x10000;
std::memcpy(&cart_ram, loader, 0x8000); loader += 0x8000;

View File

@ -11,6 +11,6 @@ namespace GBHawk
{
uint8_t PPU::ReadMemory(uint32_t addr)
{
return mem_ctrl->HardwareRead(addr);
return mem_ctrl->ReadMemory(addr);
}
}

View File

@ -19,21 +19,26 @@ namespace GBHawk
}
uint8_t ReadMemory(uint32_t);
MemoryManager* mem_ctrl;
// pointers not stated
bool* FlagI = nullptr;
bool* in_vblank = nullptr;
bool* cpu_halted = nullptr;
bool* HDMA_transfer = nullptr;
bool* GBC_compat = nullptr;
uint8_t* cpu_LY = nullptr;
uint8_t* REG_FFFF = nullptr;
uint8_t* REG_FF0F = nullptr;
uint8_t* _scanlineCallbackLine = nullptr;
uint8_t* OAM = nullptr;
uint8_t* VRAM = nullptr;
uint32_t* VRAM_Bank = nullptr;
uint32_t* _vidbuffer = nullptr;
uint32_t* color_palette = nullptr;
uint32_t BG_palette[32] = {};
uint32_t OBJ_palette[32] = {};
@ -59,7 +64,7 @@ namespace GBHawk
bool DMA_start;
uint32_t DMA_clock;
uint32_t DMA_inc;
uint8_t DMA_uint8_t;
uint8_t DMA_byte;
// state variables
uint32_t cycle;
@ -136,14 +141,54 @@ namespace GBHawk
uint32_t hbl_countdown;
uint8_t ReadMemory(uint32_t);
// The following are GBC specific variables
// individual uint8_t used in palette colors
uint8_t BG_bytes[64] = {};
uint8_t OBJ_bytes[64] = {};
bool BG_bytes_inc;
bool OBJ_bytes_inc;
uint8_t BG_bytes_index;
uint8_t OBJ_bytes_index;
uint8_t BG_transfer_byte;
uint8_t OBJ_transfer_byte;
virtual uint8_t ReadReg(int addr)
// HDMA is unique to GBC, do it as part of the PPU tick
uint8_t HDMA_src_hi;
uint8_t HDMA_src_lo;
uint8_t HDMA_dest_hi;
uint8_t HDMA_dest_lo;
uint32_t HDMA_tick;
uint8_t HDMA_byte;
// controls for tile attributes
uint32_t VRAM_sel;
bool BG_V_flip;
bool HDMA_mode;
bool HDMA_run_once;
uint32_t cur_DMA_src;
uint32_t cur_DMA_dest;
uint32_t HDMA_length;
uint32_t HDMA_countdown;
uint32_t HBL_HDMA_count;
uint32_t last_HBL;
bool HBL_HDMA_go;
bool HBL_test;
uint8_t LYC_t;
uint32_t LYC_cd;
// accessors for derived values (GBC only)
uint8_t BG_pal_ret() { return (uint8_t)(((BG_bytes_inc ? 1 : 0) << 7) | (BG_bytes_index & 0x3F)); }
uint8_t OBJ_pal_ret() { return (uint8_t)(((OBJ_bytes_inc ? 1 : 0) << 7) | (OBJ_bytes_index & 0x3F)); }
uint8_t HDMA_ctrl() { return (uint8_t)(((HDMA_active ? 0 : 1) << 7) | ((HDMA_length >> 4) - 1)); }
virtual uint8_t ReadReg(uint32_t addr)
{
return 0;
}
virtual void WriteReg(int addr, uint8_t value)
virtual void WriteReg(uint32_t addr, uint8_t value)
{
}
@ -159,7 +204,7 @@ namespace GBHawk
}
virtual void render(int render_cycle)
virtual void render(uint32_t render_cycle)
{
}
@ -176,7 +221,7 @@ namespace GBHawk
}
virtual void OAM_scan(int OAM_cycle)
virtual void OAM_scan(uint32_t OAM_cycle)
{
}
@ -193,6 +238,16 @@ namespace GBHawk
}
virtual void color_compute_BG()
{
}
void color_compute_OBJ()
{
}
#pragma endregion
#pragma region State Save / Load
@ -232,7 +287,7 @@ namespace GBHawk
saver = bool_saver(DMA_start, saver);
saver = int_saver(DMA_clock, saver);
saver = int_saver(DMA_inc, saver);
saver = byte_saver(DMA_uint8_t, saver);
saver = byte_saver(DMA_byte, saver);
saver = int_saver(cycle, saver);
saver = bool_saver(LYC_INT, saver);
@ -298,6 +353,40 @@ namespace GBHawk
saver = int_saver(hbl_countdown, saver);
// The following are GBC specific variables
for (int i = 0; i < 64; i++) { saver = byte_saver(BG_bytes[i], saver); }
for (int i = 0; i < 64; i++) { saver = byte_saver(OBJ_bytes[i], saver); }
saver = byte_saver(BG_transfer_byte, saver);
saver = byte_saver(OBJ_transfer_byte, saver);
saver = byte_saver(HDMA_src_hi, saver);
saver = byte_saver(HDMA_src_lo, saver);
saver = byte_saver(HDMA_dest_hi, saver);
saver = byte_saver(HDMA_dest_lo, saver);
saver = int_saver(HDMA_tick, saver);
saver = byte_saver(HDMA_byte, saver);
saver = int_saver(VRAM_sel, saver);
saver = bool_saver(BG_V_flip, saver);
saver = bool_saver(HDMA_mode, saver);
saver = bool_saver(HDMA_run_once, saver);
saver = int_saver(cur_DMA_src, saver);
saver = int_saver(cur_DMA_dest, saver);
saver = int_saver(HDMA_length, saver);
saver = int_saver(HDMA_countdown, saver);
saver = int_saver(HBL_HDMA_count, saver);
saver = int_saver(last_HBL, saver);
saver = bool_saver(HBL_HDMA_go, saver);
saver = bool_saver(HBL_test, saver);
saver = bool_saver(BG_bytes_inc, saver);
saver = bool_saver(OBJ_bytes_inc, saver);
saver = byte_saver(BG_bytes_index, saver);
saver = byte_saver(OBJ_bytes_index, saver);
saver = byte_saver(LYC_t, saver);
saver = int_saver(LYC_cd, saver);
return saver;
}
@ -336,7 +425,7 @@ namespace GBHawk
loader = bool_loader(&DMA_start, loader);
loader = int_loader(&DMA_clock, loader);
loader = int_loader(&DMA_inc, loader);
loader = byte_loader(&DMA_uint8_t, loader);
loader = byte_loader(&DMA_byte, loader);
loader = int_loader(&cycle, loader);
loader = bool_loader(&LYC_INT, loader);
@ -401,6 +490,40 @@ namespace GBHawk
loader = int_loader(&window_y_latch, loader);
loader = int_loader(&hbl_countdown, loader);
// The following are GBC specific variables
for (int i = 0; i < 64; i++) { loader = byte_loader(&BG_bytes[i], loader); }
for (int i = 0; i < 64; i++) { loader = byte_loader(&OBJ_bytes[i], loader); }
loader = byte_loader(&BG_transfer_byte, loader);
loader = byte_loader(&OBJ_transfer_byte, loader);
loader = byte_loader(&HDMA_src_hi, loader);
loader = byte_loader(&HDMA_src_lo, loader);
loader = byte_loader(&HDMA_dest_hi, loader);
loader = byte_loader(&HDMA_dest_lo, loader);
loader = int_loader(&HDMA_tick, loader);
loader = byte_loader(&HDMA_byte, loader);
loader = int_loader(&VRAM_sel, loader);
loader = bool_loader(&BG_V_flip, loader);
loader = bool_loader(&HDMA_mode, loader);
loader = bool_loader(&HDMA_run_once, loader);
loader = int_loader(&cur_DMA_src, loader);
loader = int_loader(&cur_DMA_dest, loader);
loader = int_loader(&HDMA_length, loader);
loader = int_loader(&HDMA_countdown, loader);
loader = int_loader(&HBL_HDMA_count, loader);
loader = int_loader(&last_HBL, loader);
loader = bool_loader(&HBL_HDMA_go, loader);
loader = bool_loader(&HBL_test, loader);
loader = bool_loader(&BG_bytes_inc, loader);
loader = bool_loader(&OBJ_bytes_inc, loader);
loader = byte_loader(&BG_bytes_index, loader);
loader = byte_loader(&OBJ_bytes_index, loader);
loader = byte_loader(&LYC_t, loader);
loader = int_loader(&LYC_cd, loader);
return loader;
}