GBHawk: more C++ work
This commit is contained in:
parent
7aec03d788
commit
7662de47ca
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "Memory.h"
|
||||
#include "LR35902.h"
|
||||
#include "TMS9918A.h"
|
||||
#include "PPU_Base.h"
|
||||
#include "GBAudio.h"
|
||||
|
||||
using namespace std;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue