GBHawk: more C++ work

This commit is contained in:
alyosha-tas 2020-03-25 17:55:09 -04:00
parent 7aec03d788
commit 7662de47ca
11 changed files with 1819 additions and 662 deletions

View File

@ -5,7 +5,7 @@
#include "LR35902.h"
#include "GBAudio.h"
#include "TMS9918A.h"
#include "PPU_Base.h"
#include "Memory.h"
namespace GBHawk
@ -16,24 +16,35 @@ namespace GBHawk
GBCore()
{
MemMap.cpu_pntr = &cpu;
MemMap.vdp_pntr = &vdp;
MemMap.ppu_pntr = &ppu;
MemMap.psg_pntr = &psg;
cpu.mem_ctrl = &MemMap;
vdp.IRQ_PTR = &cpu.FlagI;
vdp.SHOW_BG = vdp.SHOW_SPRITES = true;
ppu.FlagI = &cpu.FlagI;
ppu.in_vblank = &MemMap.in_vblank;
ppu.cpu_LY = &cpu.LY;
ppu.REG_FFFF = &MemMap.REG_FFFF;
ppu.REG_FF0F = &MemMap.REG_FF0F;
ppu._scanlineCallbackLine = &MemMap._scanlineCallbackLine;
ppu.OAM = &MemMap.OAM[0];
ppu.VRAM = &MemMap.VRAM[0];
ppu.cpu_halted = &cpu.halted;
ppu._vidbuffer = &MemMap._vidbuffer[0];
ppu.color_palette = &MemMap.color_palette[0];
sl_case = 0;
};
TMS9918A vdp;
PPU ppu;
LR35902 cpu;
GBAudio psg;
MemoryManager MemMap;
uint8_t sl_case = 0;
void Load_BIOS(uint8_t* bios, uint8_t* basic)
void Load_BIOS(uint8_t* bios, bool GBC_console)
{
MemMap.Load_BIOS(bios, basic);
MemMap.Load_BIOS(bios, GBC_console);
}
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)
@ -51,14 +62,13 @@ namespace GBHawk
MemMap.lagged = true;
uint32_t scanlinesPerFrame = 262;
vdp.SpriteLimit = true;
return MemMap.lagged;
}
void GetVideo(uint32_t* dest)
{
uint32_t* src = vdp.FrameBuffer;
uint32_t* src = MemMap.FrameBuffer;
uint32_t* dst = dest;
std::memcpy(dst, src, sizeof uint32_t * 256 * 192);
@ -85,7 +95,7 @@ namespace GBHawk
void SaveState(uint8_t* saver)
{
saver = vdp.SaveState(saver);
saver = ppu.SaveState(saver);
saver = cpu.SaveState(saver);
saver = psg.SaveState(saver);
saver = MemMap.SaveState(saver);
@ -95,7 +105,7 @@ namespace GBHawk
void LoadState(uint8_t* loader)
{
loader = vdp.LoadState(loader);
loader = ppu.LoadState(loader);
loader = cpu.LoadState(loader);
loader = psg.LoadState(loader);
loader = MemMap.LoadState(loader);
@ -114,12 +124,12 @@ namespace GBHawk
uint8_t GetVRAM(uint32_t addr)
{
return vdp.VRAM[addr & 0x3FFF];
return MemMap.VRAM[addr & 0x3FFF];
}
uint8_t GetRAM(uint32_t addr)
{
return MemMap.ram[addr & 0xFFFF];
return MemMap.RAM[addr & 0xFFFF];
}
#pragma endregion

View File

@ -1347,8 +1347,6 @@ namespace GBHawk
return loader;
}
#pragma endregion
uint8_t* bool_saver(bool to_save, uint8_t* saver)
{
*saver = (uint8_t)(to_save ? 1 : 0); saver++;
@ -1363,7 +1361,7 @@ namespace GBHawk
return saver;
}
uint8_t* int_saver(uint32_t to_save, uint8_t* saver)
uint8_t* int_saver(uint32_t to_save, uint8_t* saver)
{
*saver = (uint8_t)(to_save & 0xFF); saver++; *saver = (uint8_t)((to_save >> 8) & 0xFF); saver++;
*saver = (uint8_t)((to_save >> 16) & 0xFF); saver++; *saver = (uint8_t)((to_save >> 24) & 0xFF); saver++;
@ -1392,5 +1390,7 @@ namespace GBHawk
return loader;
}
#pragma endregion
};
}

View File

@ -22,16 +22,15 @@ GBHawk_EXPORT GBCore* GB_create()
GBHawk_EXPORT void GB_destroy(GBCore* p)
{
delete p->MemMap.bios_rom;
delete p->MemMap.basic_rom;
delete p->MemMap.rom_1;
delete p->MemMap.rom_2;
std::free(p);
}
// load bios and basic into the core
GBHawk_EXPORT void GB_load_bios(GBCore* p, uint8_t* bios, uint8_t* basic)
GBHawk_EXPORT void GB_load_bios(GBCore* p, uint8_t* bios, bool GBC_console)
{
p->Load_BIOS(bios, basic);
p->Load_BIOS(bios, GBC_console);
}
// load a rom into the core

View File

@ -154,14 +154,16 @@
<ClInclude Include="GBAudio.h" />
<ClInclude Include="Core.h" />
<ClInclude Include="GBHawk.h" />
<ClInclude Include="GB_PPU.h" />
<ClInclude Include="Memory.h" />
<ClInclude Include="TMS9918A.h" />
<ClInclude Include="PPU_Base.h" />
<ClInclude Include="LR35902.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="GBHawk.cpp" />
<ClCompile Include="Memory.cpp" />
<ClCompile Include="LR35902.cpp" />
<ClCompile Include="PPU_Base.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

File diff suppressed because it is too large Load Diff

View File

@ -234,37 +234,47 @@ namespace GBHawk
I_use = false;
break;
case RD:
Read_Func(instr_table[instr_pntr++], instr_table[instr_pntr++], instr_table[instr_pntr++]);
Read_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2]);
instr_pntr += 3;
break;
case WR:
Write_Func(instr_table[instr_pntr++], instr_table[instr_pntr++], instr_table[instr_pntr++]);
Write_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2]);
instr_pntr += 3;
break;
case TR:
TR_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
TR_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case ADD16:
ADD16_Func(instr_table[instr_pntr++], instr_table[instr_pntr++], instr_table[instr_pntr++], instr_table[instr_pntr++]);
ADD16_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2], instr_table[instr_pntr + 3]);
instr_pntr += 4;
break;
case ADD8:
ADD8_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
ADD8_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case SUB8:
SUB8_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
SUB8_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case ADC8:
ADC8_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
ADC8_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case SBC8:
SBC8_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
SBC8_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case INC16:
INC16_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
INC16_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case INC8:
INC8_Func(instr_table[instr_pntr++]);
break;
case DEC16:
DEC16_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
DEC16_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case DEC8:
DEC8_Func(instr_table[instr_pntr++]);
@ -294,16 +304,20 @@ namespace GBHawk
CCF_Func(instr_table[instr_pntr++]);
break;
case AND8:
AND8_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
AND8_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case XOR8:
XOR8_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
XOR8_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case OR8:
OR8_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
OR8_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case CP8:
CP8_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
CP8_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case SLA:
SLA_Func(instr_table[instr_pntr++]);
@ -318,13 +332,16 @@ namespace GBHawk
SWAP_Func(instr_table[instr_pntr++]);
break;
case BIT:
BIT_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
BIT_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case RES:
RES_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
RES_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case SET:
SET_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
SET_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case EI:
if (EI_pending == 0) { EI_pending = 2; }
@ -502,10 +519,12 @@ namespace GBHawk
CB_prefix = true;
break;
case ASGN:
ASGN_Func(instr_table[instr_pntr++], instr_table[instr_pntr++]);
ASGN_Func(instr_table[instr_pntr++], instr_table[instr_pntr + 1]);
instr_pntr += 2;
break;
case ADDS:
ADDS_Func(instr_table[instr_pntr++], instr_table[instr_pntr++], instr_table[instr_pntr++], instr_table[instr_pntr++]);
ADDS_Func(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2], instr_table[instr_pntr + 3]);
instr_pntr += 2;
break;
case OP_G:
//OnExecFetch ? .Invoke(RegPC);
@ -519,7 +538,8 @@ namespace GBHawk
instr_pntr--;
break;
case RD_F:
Read_Func_F(instr_table[instr_pntr++], instr_table[instr_pntr++], instr_table[instr_pntr++]);
Read_Func_F(instr_table[instr_pntr], instr_table[instr_pntr + 1], instr_table[instr_pntr + 2]);
instr_pntr += 2;
break;
case EI_RETI:
EI_pending = 1;

View File

@ -5,7 +5,7 @@
#include "Memory.h"
#include "LR35902.h"
#include "TMS9918A.h"
#include "PPU_Base.h"
#include "GBAudio.h"
using namespace std;

View File

@ -8,20 +8,19 @@ using namespace std;
namespace GBHawk
{
class LR35902;
class TMS9918A;
class PPU;
class GBAudio;
class MemoryManager
{
public:
TMS9918A* vdp_pntr = nullptr;
PPU* ppu_pntr = nullptr;
GBAudio* psg_pntr = nullptr;
LR35902* cpu_pntr = nullptr;
uint8_t* rom_1 = nullptr;
uint8_t* rom_2 = nullptr;
uint8_t* bios_rom = nullptr;
uint8_t* basic_rom = nullptr;
// initialized by core loading, not savestated
uint32_t rom_size_1;
@ -41,9 +40,21 @@ namespace GBHawk
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 RAM[0x10000] = {};
uint8_t VRAM[0x10000] = {};
uint8_t OAM[0x10000] = {};
uint8_t cart_ram[0x8000] = {};
uint8_t unmapped[0x400] = {};
uint32_t _vidbuffer[160 * 144] = {};
uint32_t color_palette[4] = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 };
uint32_t FrameBuffer[0x400] = {};
// state shared amongst different components
bool in_vblank;
uint8_t REG_FFFF, REG_FF0F;
uint8_t _scanlineCallbackLine;
MemoryManager()
{
@ -55,13 +66,21 @@ namespace GBHawk
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, uint8_t* basic)
void Load_BIOS(uint8_t* bios, bool GBC_console)
{
bios_rom = new uint8_t[0x4000];
basic_rom = new uint8_t[0x4000];
if (GBC_console)
{
bios_rom = new uint8_t[2304];
memcpy(bios_rom, bios, 2304);
}
else
{
bios_rom = new uint8_t[256];
memcpy(bios_rom, bios, 256);
}
memcpy(bios_rom, bios, 0x4000);
memcpy(basic_rom, basic, 0x4000);
}
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)
@ -102,7 +121,7 @@ namespace GBHawk
*saver = reg_FFFE; saver++;
*saver = reg_FFFF; saver++;
std::memcpy(saver, &ram, 0x10000); saver += 0x10000;
std::memcpy(saver, &RAM, 0x10000); saver += 0x10000;
std::memcpy(saver, &cart_ram, 0x8000); saver += 0x8000;
return saver;
@ -121,7 +140,7 @@ namespace GBHawk
reg_FFFE = *loader; loader++;
reg_FFFF = *loader; loader++;
std::memcpy(&ram, loader, 0x10000); loader += 0x10000;
std::memcpy(&RAM, loader, 0x10000); loader += 0x10000;
std::memcpy(&cart_ram, loader, 0x8000); loader += 0x8000;
return loader;

View File

@ -0,0 +1,16 @@
#include <cstdint>
#include <iomanip>
#include <string>
#include "Memory.h"
#include "PPU_Base.h"
using namespace std;
namespace GBHawk
{
uint8_t PPU::ReadMemory(uint32_t addr)
{
return mem_ctrl->HardwareRead(addr);
}
}

View File

@ -0,0 +1,454 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace GBHawk
{
class MemoryManager;
class PPU
{
public:
#pragma region PPU
PPU()
{
}
MemoryManager* mem_ctrl;
// pointers not stated
bool* FlagI = nullptr;
bool* in_vblank = nullptr;
bool* cpu_halted = 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* _vidbuffer = nullptr;
uint32_t* color_palette = nullptr;
uint32_t BG_palette[32] = {};
uint32_t OBJ_palette[32] = {};
bool HDMA_active;
bool clear_screen;
// register variables
uint8_t LCDC;
uint8_t STAT;
uint8_t scroll_y;
uint8_t scroll_x;
uint8_t LY;
uint8_t LY_actual;
uint8_t LY_inc;
uint8_t LYC;
uint8_t DMA_addr;
uint8_t BGP;
uint8_t obj_pal_0;
uint8_t obj_pal_1;
uint8_t window_y;
uint8_t window_x;
bool DMA_start;
uint32_t DMA_clock;
uint32_t DMA_inc;
uint8_t DMA_uint8_t;
// state variables
uint32_t cycle;
bool LYC_INT;
bool HBL_INT;
bool VBL_INT;
bool OAM_INT;
bool LCD_was_off;
bool stat_line;
bool stat_line_old;
// OAM scan
bool DMA_OAM_access;
bool OAM_access_read;
bool OAM_access_write;
uint32_t OAM_scan_index;
uint32_t SL_sprites_index;
uint32_t SL_sprites[40] = {};
uint32_t write_sprite;
bool no_scan;
// render
bool VRAM_access_read;
bool VRAM_access_write;
uint32_t read_case;
uint32_t internal_cycle;
uint32_t y_tile;
uint32_t y_scroll_offset;
uint32_t x_tile;
uint32_t x_scroll_offset;
uint32_t tile_byte;
uint32_t sprite_fetch_cycles;
bool fetch_sprite;
bool going_to_fetch;
bool first_fetch;
uint32_t sprite_fetch_counter;
uint8_t sprite_attr_list[160] = {};
uint8_t sprite_pixel_list[160] = {};
uint8_t sprite_present_list[160] = {};
uint32_t temp_fetch;
uint32_t tile_inc;
bool pre_render;
bool pre_render_2;
uint8_t tile_data[3] = {};
uint8_t tile_data_latch[3] = {};
uint32_t latch_counter;
bool latch_new_data;
uint32_t render_counter;
uint32_t render_offset;
uint32_t pixel_counter;
uint32_t pixel;
uint8_t sprite_data[2] = {};
uint8_t sprite_sel[2] = {};
uint32_t sl_use_index;
bool no_sprites;
uint32_t SL_sprites_ordered[40] = {}; // (x_end, data_low, data_high, attr)
uint32_t evaled_sprites;
uint32_t sprite_ordered_index;
bool blank_frame;
bool window_latch;
uint32_t consecutive_sprite;
uint32_t last_eval;
uint32_t total_counter;
// windowing state
uint32_t window_counter;
bool window_pre_render;
bool window_started;
bool window_is_reset;
uint32_t window_tile_inc;
uint32_t window_y_tile;
uint32_t window_x_tile;
uint32_t window_y_tile_inc;
uint32_t window_x_latch;
uint32_t window_y_latch;
uint32_t hbl_countdown;
uint8_t ReadMemory(uint32_t);
virtual uint8_t ReadReg(int addr)
{
return 0;
}
virtual void WriteReg(int addr, uint8_t value)
{
}
virtual void tick()
{
}
// might be needed, not sure yet
virtual void latch_delay()
{
}
virtual void render(int render_cycle)
{
}
virtual void process_sprite()
{
}
// normal DMA moves twice as fast in double speed mode on GBC
// So give it it's own function so we can seperate it from PPU tick
virtual void DMA_tick()
{
}
virtual void OAM_scan(int OAM_cycle)
{
}
virtual void Reset()
{
}
// order sprites according to x coordinate
// note that for sprites of equal x coordinate, priority goes to first on the list
virtual void reorder_and_assemble_sprites()
{
}
#pragma endregion
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
{
for (int i = 0; i < 32; i++) { saver = int_saver(BG_palette[i], saver); }
for (int i = 0; i < 32; i++) { saver = int_saver(OBJ_palette[i], saver); }
for (int i = 0; i < 40; i++) { saver = int_saver(SL_sprites[i], saver); }
for (int i = 0; i < 160; i++) { saver = byte_saver(sprite_attr_list[i], saver); }
for (int i = 0; i < 160; i++) { saver = byte_saver(sprite_pixel_list[i], saver); }
for (int i = 0; i < 160; i++) { saver = byte_saver(sprite_present_list[i], saver); }
for (int i = 0; i < 3; i++) { saver = byte_saver(tile_data[i], saver); }
for (int i = 0; i < 3; i++) { saver = byte_saver(tile_data_latch[i], saver); }
for (int i = 0; i < 2; i++) { saver = byte_saver(sprite_data[i], saver); }
for (int i = 0; i < 2; i++) { saver = byte_saver(sprite_sel[i], saver); }
for (int i = 0; i < 40; i++) { saver = int_saver(SL_sprites_ordered[i], saver); }
saver = bool_saver(HDMA_active, saver);
saver = bool_saver(clear_screen, saver);
saver = byte_saver(LCDC, saver);
saver = byte_saver(STAT, saver);
saver = byte_saver(scroll_y, saver);
saver = byte_saver(scroll_x, saver);
saver = byte_saver(LY, saver);
saver = byte_saver(LY_actual, saver);
saver = byte_saver(LY_inc, saver);
saver = byte_saver(LYC, saver);
saver = byte_saver(DMA_addr, saver);
saver = byte_saver(BGP, saver);
saver = byte_saver(obj_pal_0, saver);
saver = byte_saver(obj_pal_1, saver);
saver = byte_saver(window_y, saver);
saver = byte_saver(window_x, saver);
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 = int_saver(cycle, saver);
saver = bool_saver(LYC_INT, saver);
saver = bool_saver(HBL_INT, saver);
saver = bool_saver(VBL_INT, saver);
saver = bool_saver(OAM_INT, saver);
saver = bool_saver(stat_line, saver);
saver = bool_saver(stat_line_old, saver);
saver = bool_saver(LCD_was_off, saver);
saver = int_saver(OAM_scan_index, saver);
saver = int_saver(SL_sprites_index, saver);
saver = int_saver(write_sprite, saver);
saver = bool_saver(no_scan, saver);
saver = bool_saver(DMA_OAM_access, saver);
saver = bool_saver(OAM_access_read, saver);
saver = bool_saver(OAM_access_write, saver);
saver = bool_saver(VRAM_access_read, saver);
saver = bool_saver(VRAM_access_write, saver);
saver = int_saver(read_case, saver);
saver = int_saver(internal_cycle, saver);
saver = int_saver(y_tile, saver);
saver = int_saver(y_scroll_offset, saver);
saver = int_saver(x_tile, saver);
saver = int_saver(x_scroll_offset, saver);
saver = int_saver(tile_byte, saver);
saver = int_saver(sprite_fetch_cycles, saver);
saver = bool_saver(fetch_sprite, saver);
saver = bool_saver(going_to_fetch, saver);
saver = bool_saver(first_fetch, saver);
saver = int_saver(sprite_fetch_counter, saver);
saver = int_saver(temp_fetch, saver);
saver = int_saver(tile_inc, saver);
saver = bool_saver(pre_render, saver);
saver = bool_saver(pre_render_2, saver);
saver = int_saver(latch_counter, saver);
saver = bool_saver(latch_new_data, saver);
saver = int_saver(render_counter, saver);
saver = int_saver(render_offset, saver);
saver = int_saver(pixel_counter, saver);
saver = int_saver(pixel, saver);
saver = int_saver(sl_use_index, saver);
saver = bool_saver(no_sprites, saver);
saver = int_saver(evaled_sprites, saver);
saver = int_saver(sprite_ordered_index, saver);
saver = bool_saver(blank_frame, saver);
saver = bool_saver(window_latch, saver);
saver = int_saver(consecutive_sprite, saver);
saver = int_saver(last_eval, saver);
saver = int_saver(window_counter, saver);
saver = bool_saver(window_pre_render, saver);
saver = bool_saver(window_started, saver);
saver = bool_saver(window_is_reset, saver);
saver = int_saver(window_tile_inc, saver);
saver = int_saver(window_y_tile, saver);
saver = int_saver(window_x_tile, saver);
saver = int_saver(window_y_tile_inc, saver);
saver = int_saver(window_x_latch, saver);
saver = int_saver(window_y_latch, saver);
saver = int_saver(hbl_countdown, saver);
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
for (int i = 0; i < 32; i++) { loader = int_loader(&BG_palette[i], loader); }
for (int i = 0; i < 32; i++) { loader = int_loader(&OBJ_palette[i], loader); }
for (int i = 0; i < 40; i++) { loader = int_loader(&SL_sprites[i], loader); }
for (int i = 0; i < 160; i++) { loader = byte_loader(&sprite_attr_list[i], loader); }
for (int i = 0; i < 160; i++) { loader = byte_loader(&sprite_pixel_list[i], loader); }
for (int i = 0; i < 160; i++) { loader = byte_loader(&sprite_present_list[i], loader); }
for (int i = 0; i < 3; i++) { loader = byte_loader(&tile_data[i], loader); }
for (int i = 0; i < 3; i++) { loader = byte_loader(&tile_data_latch[i], loader); }
for (int i = 0; i < 2; i++) { loader = byte_loader(&sprite_data[i], loader); }
for (int i = 0; i < 2; i++) { loader = byte_loader(&sprite_sel[i], loader); }
for (int i = 0; i < 40; i++) { loader = int_loader(&SL_sprites_ordered[i], loader); }
loader = bool_loader(&HDMA_active, loader);
loader = bool_loader(&clear_screen, loader);
loader = byte_loader(&LCDC, loader);
loader = byte_loader(&STAT, loader);
loader = byte_loader(&scroll_y, loader);
loader = byte_loader(&scroll_x, loader);
loader = byte_loader(&LY, loader);
loader = byte_loader(&LY_actual, loader);
loader = byte_loader(&LY_inc, loader);
loader = byte_loader(&LYC, loader);
loader = byte_loader(&DMA_addr, loader);
loader = byte_loader(&BGP, loader);
loader = byte_loader(&obj_pal_0, loader);
loader = byte_loader(&obj_pal_1, loader);
loader = byte_loader(&window_y, loader);
loader = byte_loader(&window_x, loader);
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 = int_loader(&cycle, loader);
loader = bool_loader(&LYC_INT, loader);
loader = bool_loader(&HBL_INT, loader);
loader = bool_loader(&VBL_INT, loader);
loader = bool_loader(&OAM_INT, loader);
loader = bool_loader(&stat_line, loader);
loader = bool_loader(&stat_line_old, loader);
loader = bool_loader(&LCD_was_off, loader);
loader = int_loader(&OAM_scan_index, loader);
loader = int_loader(&SL_sprites_index, loader);
loader = int_loader(&write_sprite, loader);
loader = bool_loader(&no_scan, loader);
loader = bool_loader(&DMA_OAM_access, loader);
loader = bool_loader(&OAM_access_read, loader);
loader = bool_loader(&OAM_access_write, loader);
loader = bool_loader(&VRAM_access_read, loader);
loader = bool_loader(&VRAM_access_write, loader);
loader = int_loader(&read_case, loader);
loader = int_loader(&internal_cycle, loader);
loader = int_loader(&y_tile, loader);
loader = int_loader(&y_scroll_offset, loader);
loader = int_loader(&x_tile, loader);
loader = int_loader(&x_scroll_offset, loader);
loader = int_loader(&tile_byte, loader);
loader = int_loader(&sprite_fetch_cycles, loader);
loader = bool_loader(&fetch_sprite, loader);
loader = bool_loader(&going_to_fetch, loader);
loader = bool_loader(&first_fetch, loader);
loader = int_loader(&sprite_fetch_counter, loader);
loader = int_loader(&temp_fetch, loader);
loader = int_loader(&tile_inc, loader);
loader = bool_loader(&pre_render, loader);
loader = bool_loader(&pre_render_2, loader);
loader = int_loader(&latch_counter, loader);
loader = bool_loader(&latch_new_data, loader);
loader = int_loader(&render_counter, loader);
loader = int_loader(&render_offset, loader);
loader = int_loader(&pixel_counter, loader);
loader = int_loader(&pixel, loader);
loader = int_loader(&sl_use_index, loader);
loader = bool_loader(&no_sprites, loader);
loader = int_loader(&evaled_sprites, loader);
loader = int_loader(&sprite_ordered_index, loader);
loader = bool_loader(&blank_frame, loader);
loader = bool_loader(&window_latch, loader);
loader = int_loader(&consecutive_sprite, loader);
loader = int_loader(&last_eval, loader);
loader = int_loader(&window_counter, loader);
loader = bool_loader(&window_pre_render, loader);
loader = bool_loader(&window_started, loader);
loader = bool_loader(&window_is_reset, loader);
loader = int_loader(&window_tile_inc, loader);
loader = int_loader(&window_y_tile, loader);
loader = int_loader(&window_x_tile, loader);
loader = int_loader(&window_y_tile_inc, loader);
loader = int_loader(&window_x_latch, loader);
loader = int_loader(&window_y_latch, loader);
loader = int_loader(&hbl_countdown, loader);
return loader;
}
uint8_t* bool_saver(bool to_save, uint8_t* saver)
{
*saver = (uint8_t)(to_save ? 1 : 0); saver++;
return saver;
}
uint8_t* byte_saver(uint8_t to_save, uint8_t* saver)
{
*saver = to_save; saver++;
return saver;
}
uint8_t* int_saver(uint32_t to_save, uint8_t* saver)
{
*saver = (uint8_t)(to_save & 0xFF); saver++; *saver = (uint8_t)((to_save >> 8) & 0xFF); saver++;
*saver = (uint8_t)((to_save >> 16) & 0xFF); saver++; *saver = (uint8_t)((to_save >> 24) & 0xFF); saver++;
return saver;
}
uint8_t* bool_loader(bool* to_load, uint8_t* loader)
{
to_load[0] = *to_load == 1; loader++;
return loader;
}
uint8_t* byte_loader(uint8_t* to_load, uint8_t* loader)
{
to_load[0] = *loader; loader++;
return loader;
}
uint8_t* int_loader(uint32_t* to_load, uint8_t* loader)
{
to_load[0] = *loader; loader++; to_load[0] |= (*loader << 8); loader++;
to_load[0] |= (*loader << 16); loader++; to_load[0] |= (*loader << 24); loader++;
return loader;
}
#pragma endregion
};
}

View File

@ -1,610 +0,0 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace GBHawk
{
class TMS9918A
{
public:
#pragma region VDP
TMS9918A()
{
}
bool* IRQ_PTR = nullptr;
// external flags to display background or sprites
bool SHOW_BG, SHOW_SPRITES;
bool SpriteLimit;
// VDP State
bool VdpWaitingForLatchInt = true;
bool VdpWaitingForLatchByte = true;
bool VIntPending;
bool HIntPending;
uint8_t StatusByte;
uint8_t VdpLatch;
uint8_t VdpBuffer;
uint8_t TmsMode;
uint8_t Registers[8] = {};
uint8_t VRAM[0x4000]; //16kb video RAM
int32_t ScanLine;
uint32_t VdpAddress;
uint32_t ColorTableBase;
uint32_t PatternGeneratorBase;
uint32_t SpritePatternGeneratorBase;
uint32_t TmsPatternNameTableBase;
uint32_t TmsSpriteAttributeBase;
uint32_t FrameBuffer[192 * 256] = {};
uint8_t ScanlinePriorityBuffer[256] = {};
uint8_t SpriteCollisionBuffer[256] = {};
// constants after load, not stated
uint32_t BackgroundColor = 0;
uint32_t IPeriod = 228;
// temporary variables not stated if on frame boundary
bool is_top;
uint32_t yc;
uint32_t yofs;
uint32_t FrameBufferOffset;
uint32_t PatternNameOffset;
uint32_t ScreenBGColor;
uint32_t yrow;
uint32_t PatternGeneratorOffset;
uint32_t ColorOffset;
uint32_t pn;
uint32_t pv;
uint32_t colorEntry;
uint32_t fgIndex;
uint32_t bgIndex;
uint32_t fgColor;
uint32_t bgColor;
uint32_t lColorIndex;
uint32_t rColorIndex;
uint32_t lColor;
uint32_t rColor;
uint32_t PaletteTMS9918[16] =
{
0xFF000000,
0xFF000000,
0xFF47B73B,
0xFF7CCF6F,
0xFF5D4EFF,
0xFF8072FF,
0xFFB66247,
0xFF5DC8ED,
0xFFD76B48,
0xFFFB8F6C,
0xFFC3CD41,
0xFFD3DA76,
0xFF3E9F2F,
0xFFB664C7,
0xFFCCCCCC,
0xFFFFFFFF
};
bool Mode1Bit() { return (Registers[1] & 16) > 0; }
bool Mode2Bit() { return (Registers[0] & 2) > 0; }
bool Mode3Bit() { return (Registers[1] & 8) > 0; }
bool EnableDoubledSprites() { return (Registers[1] & 1) > 0; }
bool EnableLargeSprites() { return (Registers[1] & 2) > 0; }
bool EnableInterrupts() { return (Registers[1] & 32) > 0; }
bool DisplayOn() { return (Registers[1] & 64) > 0; }
bool Mode16k() { return (Registers[1] & 128) > 0; }
bool InterruptPendingGet() { return (StatusByte & 0x80) != 0; }
void InterruptPendingSet(bool value) { StatusByte = (uint8_t)((StatusByte & ~0x02) | (value ? 0x80 : 0x00)); }
void WriteVdpControl(uint8_t value)
{
if (VdpWaitingForLatchByte)
{
VdpLatch = value;
VdpWaitingForLatchByte = false;
VdpAddress = (uint32_t)((VdpAddress & 0x3F00) | value);
return;
}
VdpWaitingForLatchByte = true;
VdpAddress = (uint32_t)(((value & 63) << 8) | VdpLatch);
VdpAddress &= 0x3FFF;
switch (value & 0xC0)
{
case 0x00: // read VRAM
VdpBuffer = VRAM[VdpAddress];
VdpAddress++;
VdpAddress &= 0x3FFF;
break;
case 0x40: // write VRAM
break;
case 0x80: // VDP register write
WriteRegister(value & 0x0F, VdpLatch);
break;
}
}
void WriteVdpData(uint8_t value)
{
VdpWaitingForLatchByte = true;
VdpBuffer = value;
VRAM[VdpAddress] = value;
//if (!Mode16k)
// Console.WriteLine("VRAM written while not in 16k addressing mode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
VdpAddress++;
VdpAddress &= 0x3FFF;
}
void WriteRegister(uint32_t reg, uint8_t data)
{
if (reg >= 8) return;
Registers[reg] = data;
switch (reg)
{
case 0: // Mode Control Register 1
CheckVideoMode();
break;
case 1: // Mode Control Register 2
CheckVideoMode();
IRQ_PTR[0] = (EnableInterrupts() && InterruptPendingGet());
break;
case 2: // Name Table Base Address
TmsPatternNameTableBase = (Registers[2] << 10) & 0x3C00;
break;
case 3: // Color Table Base Address
ColorTableBase = (Registers[3] << 6) & 0x3FC0;
break;
case 4: // Pattern Generator Base Address
PatternGeneratorBase = (Registers[4] << 11) & 0x3800;
break;
case 5: // Sprite Attribute Table Base Address
TmsSpriteAttributeBase = (Registers[5] << 7) & 0x3F80;
break;
case 6: // Sprite Pattern Generator Base Adderss
SpritePatternGeneratorBase = (Registers[6] << 11) & 0x3800;
break;
}
}
uint8_t ReadVdpStatus()
{
VdpWaitingForLatchByte = true;
uint8_t returnValue = StatusByte;
StatusByte &= 0x1F;
IRQ_PTR[0] = false;
return returnValue;
}
uint8_t ReadData()
{
VdpWaitingForLatchByte = true;
uint8_t value = VdpBuffer;
VdpBuffer = VRAM[VdpAddress];
VdpAddress++;
VdpAddress &= 0x3FFF;
return value;
}
void CheckVideoMode()
{
if (Mode1Bit()) TmsMode = 1;
else if (Mode2Bit()) TmsMode = 2;
else if (Mode3Bit()) TmsMode = 3;
else TmsMode = 0;
}
void RenderScanline(int32_t scanLine)
{
if (scanLine >= 192)
return;
if (TmsMode == 2)
{
RenderBackgroundM2(scanLine);
RenderTmsSprites(scanLine);
}
else if (TmsMode == 0)
{
RenderBackgroundM0(scanLine);
RenderTmsSprites(scanLine);
}
else if (TmsMode == 3)
{
RenderBackgroundM3(scanLine);
RenderTmsSprites(scanLine);
}
else if (TmsMode == 1)
{
RenderBackgroundM1(scanLine);
// no sprites (text mode)
}
}
void RenderBackgroundM0(uint32_t scanLine)
{
if (DisplayOn() == false)
{
for (int i = 0; i < 256; i++) { FrameBuffer[scanLine * 256 + i] = 0; };
return;
}
yc = scanLine / 8;
yofs = scanLine % 8;
FrameBufferOffset = scanLine * 256;
PatternNameOffset = TmsPatternNameTableBase + (yc * 32);
ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 32; xc++)
{
pn = VRAM[PatternNameOffset++];
pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs];
colorEntry = VRAM[ColorTableBase + (pn / 8)];
fgIndex = (colorEntry >> 4) & 0x0F;
bgIndex = colorEntry & 0x0F;
fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x20) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x10) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x08) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x04) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x02) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x01) > 0) ? fgColor : bgColor;
}
}
void RenderBackgroundM1(uint32_t scanLine)
{
if (DisplayOn() == false)
{
for (int i = 0; i < 256; i++) { FrameBuffer[scanLine * 256 + i] = 0; };
return;
}
yc = scanLine / 8;
yofs = scanLine % 8;
FrameBufferOffset = scanLine * 256;
PatternNameOffset = TmsPatternNameTableBase + (yc * 40);
ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 40; xc++)
{
pn = VRAM[PatternNameOffset++];
pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs];
colorEntry = Registers[7];
fgIndex = (colorEntry >> 4) & 0x0F;
bgIndex = colorEntry & 0x0F;
fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x20) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x10) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x08) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x04) > 0) ? fgColor : bgColor;
}
}
void RenderBackgroundM2(uint32_t scanLine)
{
if (DisplayOn() == false)
{
for (int i = 0; i < 256; i++) { FrameBuffer[scanLine * 256 + i] = 0; };
return;
}
yrow = scanLine / 8;
yofs = scanLine % 8;
FrameBufferOffset = scanLine * 256;
PatternNameOffset = TmsPatternNameTableBase + (yrow * 32);
PatternGeneratorOffset = (((Registers[4] & 4) << 11) & 0x2000);
ColorOffset = (ColorTableBase & 0x2000);
ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 32; xc++)
{
pn = VRAM[PatternNameOffset++] + ((yrow / 8) * 0x100);
pv = VRAM[PatternGeneratorOffset + (pn * 8) + yofs];
colorEntry = VRAM[ColorOffset + (pn * 8) + yofs];
fgIndex = (colorEntry >> 4) & 0x0F;
bgIndex = colorEntry & 0x0F;
fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x20) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x10) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x08) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x04) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x02) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x01) > 0) ? fgColor : bgColor;
}
}
void RenderBackgroundM3(uint32_t scanLine)
{
if (DisplayOn() == false)
{
for (int i = 0; i < 256; i++) { FrameBuffer[scanLine * 256 + i] = 0; };
return;
}
yc = scanLine / 8;
is_top = (scanLine & 4) == 0; // am I in the top 4 pixels of an 8-pixel character?
FrameBufferOffset = scanLine * 256;
PatternNameOffset = TmsPatternNameTableBase + (yc * 32);
ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 32; xc++)
{
pn = VRAM[PatternNameOffset++];
pv = VRAM[PatternGeneratorBase + (pn * 8) + ((yc & 3) * 2) + (is_top ? 0 : 1)];
lColorIndex = pv & 0xF;
rColorIndex = pv >> 4;
lColor = lColorIndex == 0 ? ScreenBGColor : PaletteTMS9918[lColorIndex];
rColor = rColorIndex == 0 ? ScreenBGColor : PaletteTMS9918[rColorIndex];
FrameBuffer[FrameBufferOffset++] = lColor;
FrameBuffer[FrameBufferOffset++] = lColor;
FrameBuffer[FrameBufferOffset++] = lColor;
FrameBuffer[FrameBufferOffset++] = lColor;
FrameBuffer[FrameBufferOffset++] = rColor;
FrameBuffer[FrameBufferOffset++] = rColor;
FrameBuffer[FrameBufferOffset++] = rColor;
FrameBuffer[FrameBufferOffset] = rColor;
}
}
inline void RenderTmsSprites(int32_t scanLine)
{
if (EnableDoubledSprites() == false)
{
RenderTmsSpritesStandard(scanLine);
}
else
{
RenderTmsSpritesDouble(scanLine);
}
}
void RenderTmsSpritesStandard(int32_t scanLine)
{
if (DisplayOn() == false) return;
for (uint32_t i = 0; i < 256; i++)
{
ScanlinePriorityBuffer[i] = 0;
SpriteCollisionBuffer[i] = 0;
};
bool LargeSprites = EnableLargeSprites();
int32_t SpriteSize = 8;
if (LargeSprites) SpriteSize *= 2;
const int32_t OneCellSize = 8;
int32_t NumSpritesOnScanline = 0;
for (int32_t i = 0; i < 32; i++)
{
int32_t SpriteBase = TmsSpriteAttributeBase + (i * 4);
int32_t y = VRAM[SpriteBase++];
int32_t x = VRAM[SpriteBase++];
int32_t Pattern = VRAM[SpriteBase++];
int32_t Color = VRAM[SpriteBase];
if (y == 208) break; // terminator sprite
if (y > 224) y -= 256; // sprite Y wrap
y++; // inexplicably, sprites start on Y+1
if (y > scanLine || y + SpriteSize <= scanLine) continue; // sprite is not on this scanline
if ((Color & 0x80) > 0) x -= 32; // Early Clock adjustment
if (++NumSpritesOnScanline == 5)
{
StatusByte &= 0xE0; // Clear FS0-FS4 bits
StatusByte |= (uint8_t)i; // set 5th sprite index
StatusByte |= 0x40; // set overflow bit
break;
}
if (LargeSprites) Pattern &= 0xFC; // 16x16 sprites forced to 4-uint8_t alignment
int32_t SpriteLine = scanLine - y;
// pv contains the VRAM uint8_t holding the pattern data for this character at this scanline.
// each uint8_t contains the pattern data for each the 8 pixels on this line.
// the bit-shift further down on PV pulls out the relevant horizontal pixel.
int8_t pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine];
for (int32_t xp = 0; xp < SpriteSize && x + xp < 256; xp++)
{
if (x + xp < 0) continue;
if (LargeSprites && xp == OneCellSize)
pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine + 16];
if (Color != 0 && (pv & (1 << (7 - (xp & 7)))) > 0)
{
if (SpriteCollisionBuffer[x + xp] != 0)
StatusByte |= 0x20; // Set sprite collision flag
if (ScanlinePriorityBuffer[x + xp] == 0)
{
ScanlinePriorityBuffer[x + xp] = 1;
SpriteCollisionBuffer[x + xp] = 1;
FrameBuffer[(scanLine * 256) + x + xp] = PaletteTMS9918[Color & 0x0F];
}
}
}
}
}
void RenderTmsSpritesDouble(int32_t scanLine)
{
if (DisplayOn() == false) return;
for (uint32_t i = 0; i < 256; i++)
{
ScanlinePriorityBuffer[i] = 0;
SpriteCollisionBuffer[i] = 0;
};
bool LargeSprites = EnableLargeSprites();
int32_t SpriteSize = 8;
if (LargeSprites) SpriteSize *= 2;
SpriteSize *= 2; // because sprite magnification
const int32_t OneCellSize = 16; // once 8-pixel cell, doubled, will take 16 pixels
int32_t NumSpritesOnScanline = 0;
for (int32_t i = 0; i < 32; i++)
{
int32_t SpriteBase = TmsSpriteAttributeBase + (i * 4);
int32_t y = VRAM[SpriteBase++];
int32_t x = VRAM[SpriteBase++];
int32_t Pattern = VRAM[SpriteBase++];
int32_t Color = VRAM[SpriteBase];
if (y == 208) break; // terminator sprite
if (y > 224) y -= 256; // sprite Y wrap
y++; // inexplicably, sprites start on Y+1
if (y > scanLine || y + SpriteSize <= scanLine) continue; // sprite is not on this scanline
if ((Color & 0x80) > 0) x -= 32; // Early Clock adjustment
if (++NumSpritesOnScanline == 5)
{
StatusByte &= 0xE0; // Clear FS0-FS4 bits
StatusByte |= (uint8_t)i; // set 5th sprite index
StatusByte |= 0x40; // set overflow bit
break;
}
if (LargeSprites) Pattern &= 0xFC; // 16x16 sprites forced to 4-byte alignment
int32_t SpriteLine = scanLine - y;
SpriteLine /= 2; // because of sprite magnification
int8_t pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine];
for (int32_t xp = 0; xp < SpriteSize && x + xp < 256; xp++)
{
if (x + xp < 0) continue;
if (LargeSprites && xp == OneCellSize)
pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine + 16];
if (Color != 0 && (pv & (1 << (7 - ((xp / 2) & 7)))) > 0) // xp/2 is due to sprite magnification
{
if (SpriteCollisionBuffer[x + xp] != 0)
StatusByte |= 0x20; // Set sprite collision flag
if (ScanlinePriorityBuffer[x + xp] == 0)
{
ScanlinePriorityBuffer[x + xp] = 1;
SpriteCollisionBuffer[x + xp] = 1;
FrameBuffer[(scanLine * 256) + x + xp] = PaletteTMS9918[Color & 0x0F];
}
}
}
}
}
#pragma endregion
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
{
*saver = (uint8_t)(VdpWaitingForLatchInt ? 1 : 0); saver++;
*saver = (uint8_t)(VdpWaitingForLatchByte ? 1 : 0); saver++;
*saver = (uint8_t)(VIntPending ? 1 : 0); saver++;
*saver = (uint8_t)(HIntPending ? 1 : 0); saver++;
*saver = StatusByte; saver++;
*saver = VdpLatch; saver++;
*saver = VdpBuffer; saver++;
*saver = TmsMode; saver++;
std::memcpy(saver, &Registers, 8); saver += 8;
std::memcpy(saver, &VRAM, 0x4000); saver += 0x4000;
*saver = (uint8_t)(ScanLine & 0xFF); saver++; *saver = (uint8_t)((ScanLine >> 8) & 0xFF); saver++;
*saver = (uint8_t)((ScanLine >> 16) & 0xFF); saver++; *saver = (uint8_t)((ScanLine >> 24) & 0xFF); saver++;
*saver = (uint8_t)(VdpAddress & 0xFF); saver++; *saver = (uint8_t)((VdpAddress >> 8) & 0xFF); saver++;
*saver = (uint8_t)((VdpAddress >> 16) & 0xFF); saver++; *saver = (uint8_t)((VdpAddress >> 24) & 0xFF); saver++;
*saver = (uint8_t)(ColorTableBase & 0xFF); saver++; *saver = (uint8_t)((ColorTableBase >> 8) & 0xFF); saver++;
*saver = (uint8_t)((ColorTableBase >> 16) & 0xFF); saver++; *saver = (uint8_t)((ColorTableBase >> 24) & 0xFF); saver++;
*saver = (uint8_t)(PatternGeneratorBase & 0xFF); saver++; *saver = (uint8_t)((PatternGeneratorBase >> 8) & 0xFF); saver++;
*saver = (uint8_t)((PatternGeneratorBase >> 16) & 0xFF); saver++; *saver = (uint8_t)((PatternGeneratorBase >> 24) & 0xFF); saver++;
*saver = (uint8_t)(SpritePatternGeneratorBase & 0xFF); saver++; *saver = (uint8_t)((SpritePatternGeneratorBase >> 8) & 0xFF); saver++;
*saver = (uint8_t)((SpritePatternGeneratorBase >> 16) & 0xFF); saver++; *saver = (uint8_t)((SpritePatternGeneratorBase >> 24) & 0xFF); saver++;
*saver = (uint8_t)(TmsPatternNameTableBase & 0xFF); saver++; *saver = (uint8_t)((TmsPatternNameTableBase >> 8) & 0xFF); saver++;
*saver = (uint8_t)((TmsPatternNameTableBase >> 16) & 0xFF); saver++; *saver = (uint8_t)((TmsPatternNameTableBase >> 24) & 0xFF); saver++;
*saver = (uint8_t)(TmsSpriteAttributeBase & 0xFF); saver++; *saver = (uint8_t)((TmsSpriteAttributeBase >> 8) & 0xFF); saver++;
*saver = (uint8_t)((TmsSpriteAttributeBase >> 16) & 0xFF); saver++; *saver = (uint8_t)((TmsSpriteAttributeBase >> 24) & 0xFF); saver++;
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
VdpWaitingForLatchInt = *loader == 1; loader++;
VdpWaitingForLatchByte = *loader == 1; loader++;
VIntPending = *loader == 1; loader++;
HIntPending = *loader == 1; loader++;
StatusByte = *loader; loader++;
VdpLatch = *loader; loader++;
VdpBuffer = *loader; loader++;
TmsMode = *loader; loader++;
std::memcpy(&Registers, loader, 8); loader += 8;
std::memcpy(&VRAM, loader, 0x4000); loader += 0x4000;
ScanLine = *loader; loader++; ScanLine |= (*loader << 8); loader++;
ScanLine |= (*loader << 16); loader++; ScanLine |= (*loader << 24); loader++;
VdpAddress = *loader; loader++; VdpAddress |= (*loader << 8); loader++;
VdpAddress |= (*loader << 16); loader++; VdpAddress |= (*loader << 24); loader++;
ColorTableBase = *loader; loader++; ColorTableBase |= (*loader << 8); loader++;
ColorTableBase |= (*loader << 16); loader++; ColorTableBase |= (*loader << 24); loader++;
PatternGeneratorBase = *loader; loader++; PatternGeneratorBase |= (*loader << 8); loader++;
PatternGeneratorBase |= (*loader << 16); loader++; PatternGeneratorBase |= (*loader << 24); loader++;
SpritePatternGeneratorBase = *loader; loader++; SpritePatternGeneratorBase |= (*loader << 8); loader++;
SpritePatternGeneratorBase |= (*loader << 16); loader++; SpritePatternGeneratorBase |= (*loader << 24); loader++;
TmsPatternNameTableBase = *loader; loader++; TmsPatternNameTableBase |= (*loader << 8); loader++;
TmsPatternNameTableBase |= (*loader << 16); loader++; TmsPatternNameTableBase |= (*loader << 24); loader++;
TmsSpriteAttributeBase = *loader; loader++; TmsSpriteAttributeBase |= (*loader << 8); loader++;
TmsSpriteAttributeBase |= (*loader << 16); loader++; TmsSpriteAttributeBase |= (*loader << 24); loader++;
return loader;
}
#pragma endregion
};
}