GBHawk: start C++'ification

This commit is contained in:
alyosha-tas 2020-03-24 12:05:49 -04:00
parent 26619d367b
commit d0cf220391
12 changed files with 5491 additions and 0 deletions

31
libHawk/GBHawk/GBHawk.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29709.97
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GBHawk", "GBHawk\GBHawk.vcxproj", "{FA59603F-32AB-429A-9186-B46114851290}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FA59603F-32AB-429A-9186-B46114851290}.Debug|x64.ActiveCfg = Debug|x64
{FA59603F-32AB-429A-9186-B46114851290}.Debug|x64.Build.0 = Debug|x64
{FA59603F-32AB-429A-9186-B46114851290}.Debug|x86.ActiveCfg = Debug|Win32
{FA59603F-32AB-429A-9186-B46114851290}.Debug|x86.Build.0 = Debug|Win32
{FA59603F-32AB-429A-9186-B46114851290}.Release|x64.ActiveCfg = Release|x64
{FA59603F-32AB-429A-9186-B46114851290}.Release|x64.Build.0 = Release|x64
{FA59603F-32AB-429A-9186-B46114851290}.Release|x86.ActiveCfg = Release|Win32
{FA59603F-32AB-429A-9186-B46114851290}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ED770D9B-8735-46CA-B51E-F85B00A9C744}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,442 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace GBHawk
{
class AY_3_8910
{
public:
#pragma region AY_3_8910
AY_3_8910()
{
Reset();
}
bool A_on, B_on, C_on;
bool A_up, B_up, C_up;
bool A_noise, B_noise, C_noise;
bool env_vol_A, env_vol_B, env_vol_C;
uint8_t env_shape;
uint8_t port_sel;
uint8_t vol_A, vol_B, vol_C;
uint8_t Register[16] = {};
uint32_t psg_clock;
uint32_t sq_per_A, sq_per_B, sq_per_C;
uint32_t clock_A, clock_B, clock_C;
uint32_t env_per;
uint32_t env_clock;
int32_t env_E;
int32_t E_up_down;
uint32_t noise_clock;
uint32_t noise_per;
uint32_t noise = 0x1;
int32_t old_sample;
// non stated if only on frame boundaries
bool sound_out_A;
bool sound_out_B;
bool sound_out_C;
uint8_t Clock_Divider;
int32_t current_sample;
uint32_t sampleclock;
uint32_t num_samples;
int32_t samples[9000] = {};
void Reset()
{
clock_A = clock_B = clock_C = 0x1000;
noise_clock = 0x20;
port_sel = 0;
for (int i = 0; i < 16; i++)
{
Register[i] = 0x0;
}
sync_psg_state();
}
short Sample()
{
return current_sample;
}
const uint32_t VolumeTable[16] =
{
0x0000, 0x0055, 0x0079, 0x00AB, 0x00F1, 0x0155, 0x01E3, 0x02AA,
0x03C5, 0x0555, 0x078B, 0x0AAB, 0x0F16, 0x1555, 0x1E2B, 0x2AAA
};
uint8_t ReadReg()
{
return Register[port_sel];
}
void sync_psg_state()
{
sq_per_A = (Register[0] & 0xFF) | (((Register[1] & 0xF) << 8));
if (sq_per_A == 0)
{
sq_per_A = 0x1000;
}
sq_per_B = (Register[2] & 0xFF) | (((Register[3] & 0xF) << 8));
if (sq_per_B == 0)
{
sq_per_B = 0x1000;
}
sq_per_C = (Register[4] & 0xFF) | (((Register[5] & 0xF) << 8));
if (sq_per_C == 0)
{
sq_per_C = 0x1000;
}
env_per = (Register[11] & 0xFF) | (((Register[12] & 0xFF) << 8));
if (env_per == 0)
{
env_per = 0x10000;
}
env_per *= 2;
A_on = (Register[7] & 0x1) > 0;
B_on = (Register[7] & 0x2) > 0;
C_on = (Register[7] & 0x4) > 0;
A_noise = (Register[7] & 0x8) > 0;
B_noise = (Register[7] & 0x10) > 0;
C_noise = (Register[7] & 0x20) > 0;
noise_per = Register[6] & 0x1F;
if (noise_per == 0)
{
noise_per = 0x20;
}
uint8_t shape_select = Register[13] & 0xF;
if (shape_select < 4) { env_shape = 0; }
else if (shape_select < 8) { env_shape = 1; }
else { env_shape = 2 + (shape_select - 8); }
vol_A = Register[8] & 0xF;
env_vol_A = ((Register[8] >> 4) & 0x1) > 0;
vol_B = Register[9] & 0xF;
env_vol_B = ((Register[9] >> 4) & 0x1) > 0;
vol_C = Register[10] & 0xF;
env_vol_C = ((Register[10] >> 4) & 0x1) > 0;
}
void WriteReg(uint8_t value)
{
value &= 0xFF;
if (port_sel != 0xE) { Register[port_sel] = value; }
sync_psg_state();
if (port_sel == 13)
{
env_clock = env_per;
if (env_shape == 0 || env_shape == 2 || env_shape == 3 || env_shape == 4 || env_shape == 5)
{
env_E = 15;
E_up_down = -1;
}
else
{
env_E = 0;
E_up_down = 1;
}
}
}
void generate_sound()
{
// there are 8 cpu cycles for every psg cycle
clock_A--;
clock_B--;
clock_C--;
noise_clock--;
env_clock--;
// clock noise
if (noise_clock == 0)
{
noise = (noise >> 1) ^ (((noise &0x1) > 0) ? 0x10004 : 0);
noise_clock = noise_per;
}
if (env_clock == 0)
{
env_clock = env_per;
env_E += E_up_down;
if (env_E == 16 || env_E == -1)
{
// we just completed a period of the envelope, determine what to do now based on the envelope shape
if (env_shape == 0 || env_shape == 1 || env_shape == 3 || env_shape == 9)
{
E_up_down = 0;
env_E = 0;
}
else if (env_shape == 5 || env_shape == 7)
{
E_up_down = 0;
env_E = 15;
}
else if (env_shape == 4 || env_shape == 8)
{
if (env_E == 16)
{
env_E = 15;
E_up_down = -1;
}
else
{
env_E = 0;
E_up_down = 1;
}
}
else if (env_shape == 2)
{
env_E = 15;
}
else
{
env_E = 0;
}
}
}
if (clock_A == 0)
{
A_up = !A_up;
clock_A = sq_per_A;
}
if (clock_B == 0)
{
B_up = !B_up;
clock_B = sq_per_B;
}
if (clock_C == 0)
{
C_up = !C_up;
clock_C = sq_per_C;
}
sound_out_A = (((noise & 0x1) > 0) | A_noise) & (A_on | A_up);
sound_out_B = (((noise & 0x1) > 0) | B_noise) & (B_on | B_up);
sound_out_C = (((noise & 0x1) > 0) | C_noise) & (C_on | C_up);
// now calculate the volume of each channel and add them together
current_sample = 0;
if (env_vol_A)
{
current_sample = (sound_out_A ? VolumeTable[env_E] : 0);
}
else
{
current_sample = (sound_out_A ? VolumeTable[vol_A] : 0);
}
if (env_vol_B)
{
current_sample += (sound_out_B ? VolumeTable[env_E] : 0);
}
else
{
current_sample += (sound_out_B ? VolumeTable[vol_B] : 0);
}
if (env_vol_C)
{
current_sample += (sound_out_C ? VolumeTable[env_E] : 0);
}
else
{
current_sample += (sound_out_C ? VolumeTable[vol_C] : 0);
}
current_sample *= 2;
if ((current_sample != old_sample) && (num_samples < 4500))
{
samples[num_samples * 2] = sampleclock;
samples[num_samples * 2 + 1] = current_sample - old_sample;
num_samples++;
old_sample = current_sample;
}
}
#pragma endregion
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
{
*saver = (uint8_t)(A_on ? 1 : 0); saver++;
*saver = (uint8_t)(B_on ? 1 : 0); saver++;
*saver = (uint8_t)(C_on ? 1 : 0); saver++;
*saver = (uint8_t)(A_up ? 1 : 0); saver++;
*saver = (uint8_t)(B_up ? 1 : 0); saver++;
*saver = (uint8_t)(C_up ? 1 : 0); saver++;
*saver = (uint8_t)(A_noise ? 1 : 0); saver++;
*saver = (uint8_t)(B_noise ? 1 : 0); saver++;
*saver = (uint8_t)(C_noise ? 1 : 0); saver++;
*saver = (uint8_t)(env_vol_A ? 1 : 0); saver++;
*saver = (uint8_t)(env_vol_B ? 1 : 0); saver++;
*saver = (uint8_t)(env_vol_C ? 1 : 0); saver++;
*saver = env_shape; saver++;
*saver = port_sel; saver++;
*saver = vol_A; saver++;
*saver = vol_B; saver++;
*saver = vol_C; saver++;
for (int i = 0; i < 16; i++) { *saver = Register[i]; saver++; }
*saver = (uint8_t)(psg_clock & 0xFF); saver++; *saver = (uint8_t)((psg_clock >> 8) & 0xFF); saver++;
*saver = (uint8_t)((psg_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((psg_clock >> 24) & 0xFF); saver++;
*saver = (uint8_t)(sq_per_A & 0xFF); saver++; *saver = (uint8_t)((sq_per_A >> 8) & 0xFF); saver++;
*saver = (uint8_t)((sq_per_A >> 16) & 0xFF); saver++; *saver = (uint8_t)((sq_per_A >> 24) & 0xFF); saver++;
*saver = (uint8_t)(sq_per_B & 0xFF); saver++; *saver = (uint8_t)((sq_per_B >> 8) & 0xFF); saver++;
*saver = (uint8_t)((sq_per_B >> 16) & 0xFF); saver++; *saver = (uint8_t)((sq_per_B >> 24) & 0xFF); saver++;
*saver = (uint8_t)(sq_per_C & 0xFF); saver++; *saver = (uint8_t)((sq_per_C >> 8) & 0xFF); saver++;
*saver = (uint8_t)((sq_per_C >> 16) & 0xFF); saver++; *saver = (uint8_t)((sq_per_C >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clock_A & 0xFF); saver++; *saver = (uint8_t)((clock_A >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clock_A >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_A >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clock_B & 0xFF); saver++; *saver = (uint8_t)((clock_B >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clock_B >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_B >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clock_C & 0xFF); saver++; *saver = (uint8_t)((clock_C >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clock_C >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_C >> 24) & 0xFF); saver++;
*saver = (uint8_t)(env_per & 0xFF); saver++; *saver = (uint8_t)((env_per >> 8) & 0xFF); saver++;
*saver = (uint8_t)((env_per >> 16) & 0xFF); saver++; *saver = (uint8_t)((env_per >> 24) & 0xFF); saver++;
*saver = (uint8_t)(env_clock & 0xFF); saver++; *saver = (uint8_t)((env_clock >> 8) & 0xFF); saver++;
*saver = (uint8_t)((env_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((env_clock >> 24) & 0xFF); saver++;
*saver = (uint8_t)(env_E & 0xFF); saver++; *saver = (uint8_t)((env_E >> 8) & 0xFF); saver++;
*saver = (uint8_t)((env_E >> 16) & 0xFF); saver++; *saver = (uint8_t)((env_E >> 24) & 0xFF); saver++;
*saver = (uint8_t)(E_up_down & 0xFF); saver++; *saver = (uint8_t)((E_up_down >> 8) & 0xFF); saver++;
*saver = (uint8_t)((E_up_down >> 16) & 0xFF); saver++; *saver = (uint8_t)((E_up_down >> 24) & 0xFF); saver++;
*saver = (uint8_t)(noise_clock & 0xFF); saver++; *saver = (uint8_t)((noise_clock >> 8) & 0xFF); saver++;
*saver = (uint8_t)((noise_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise_clock >> 24) & 0xFF); saver++;
*saver = (uint8_t)(noise_per & 0xFF); saver++; *saver = (uint8_t)((noise_per >> 8) & 0xFF); saver++;
*saver = (uint8_t)((noise_per >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise_per >> 24) & 0xFF); saver++;
*saver = (uint8_t)(noise & 0xFF); saver++; *saver = (uint8_t)((noise >> 8) & 0xFF); saver++;
*saver = (uint8_t)((noise >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise >> 24) & 0xFF); saver++;
*saver = (uint8_t)(old_sample & 0xFF); saver++; *saver = (uint8_t)((old_sample >> 8) & 0xFF); saver++;
*saver = (uint8_t)((old_sample >> 16) & 0xFF); saver++; *saver = (uint8_t)((old_sample >> 24) & 0xFF); saver++;
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
A_on = *loader == 1; loader++;
B_on = *loader == 1; loader++;
C_on = *loader == 1; loader++;
A_up = *loader == 1; loader++;
B_up = *loader == 1; loader++;
C_up = *loader == 1; loader++;
A_noise = *loader == 1; loader++;
B_noise = *loader == 1; loader++;
C_noise = *loader == 1; loader++;
env_vol_A = *loader == 1; loader++;
env_vol_B = *loader == 1; loader++;
env_vol_C = *loader == 1; loader++;
env_shape = *loader; loader++;
port_sel = *loader; loader++;
vol_A = *loader; loader++;
vol_B = *loader; loader++;
vol_C = *loader; loader++;
for (int i = 0; i < 16; i++) { Register[i] = *loader; loader++; }
psg_clock = *loader; loader++; psg_clock |= (*loader << 8); loader++;
psg_clock |= (*loader << 16); loader++; psg_clock |= (*loader << 24); loader++;
sq_per_A = *loader; loader++; sq_per_A |= (*loader << 8); loader++;
sq_per_A |= (*loader << 16); loader++; sq_per_A |= (*loader << 24); loader++;
sq_per_B = *loader; loader++; sq_per_B |= (*loader << 8); loader++;
sq_per_B |= (*loader << 16); loader++; sq_per_B |= (*loader << 24); loader++;
sq_per_C = *loader; loader++; sq_per_C |= (*loader << 8); loader++;
sq_per_C |= (*loader << 16); loader++; sq_per_C |= (*loader << 24); loader++;
clock_A = *loader; loader++; clock_A |= (*loader << 8); loader++;
clock_A |= (*loader << 16); loader++; clock_A |= (*loader << 24); loader++;
clock_B = *loader; loader++; clock_B |= (*loader << 8); loader++;
clock_B |= (*loader << 16); loader++; clock_B |= (*loader << 24); loader++;
clock_C = *loader; loader++; clock_C |= (*loader << 8); loader++;
clock_C |= (*loader << 16); loader++; clock_C |= (*loader << 24); loader++;
env_per = *loader; loader++; env_per |= (*loader << 8); loader++;
env_per |= (*loader << 16); loader++; env_per |= (*loader << 24); loader++;
env_clock = *loader; loader++; env_clock |= (*loader << 8); loader++;
env_clock |= (*loader << 16); loader++; env_clock |= (*loader << 24); loader++;
env_E = *loader; loader++; env_E |= (*loader << 8); loader++;
env_E |= (*loader << 16); loader++; env_E |= (*loader << 24); loader++;
E_up_down = *loader; loader++; E_up_down |= (*loader << 8); loader++;
E_up_down |= (*loader << 16); loader++; E_up_down |= (*loader << 24); loader++;
noise_clock = *loader; loader++; noise_clock |= (*loader << 8); loader++;
noise_clock |= (*loader << 16); loader++; noise_clock |= (*loader << 24); loader++;
noise_per = *loader; loader++; noise_per |= (*loader << 8); loader++;
noise_per |= (*loader << 16); loader++; noise_per |= (*loader << 24); loader++;
noise = *loader; loader++; noise |= (*loader << 8); loader++;
noise |= (*loader << 16); loader++; noise |= (*loader << 24); loader++;
old_sample = *loader; loader++; old_sample |= (*loader << 8); loader++;
old_sample |= (*loader << 16); loader++; old_sample |= (*loader << 24); loader++;
return loader;
}
#pragma endregion
};
}

View File

@ -0,0 +1,271 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
#include "LR35902.h"
#include "AY_3_8910.h"
#include "TMS9918A.h"
#include "Memory.h"
namespace GBHawk
{
class GBCore
{
public:
GBCore()
{
MemMap.cpu_pntr = &cpu;
MemMap.vdp_pntr = &vdp;
MemMap.psg_pntr = &psg;
cpu.mem_ctrl = &MemMap;
vdp.IRQ_PTR = &cpu.FlagI;
vdp.SHOW_BG = vdp.SHOW_SPRITES = true;
psg.Clock_Divider = 16;
sl_case = 0;
};
TMS9918A vdp;
Z80A cpu;
AY_3_8910 psg;
MemoryManager MemMap;
uint8_t sl_case = 0;
void Load_BIOS(uint8_t* bios, uint8_t* basic)
{
MemMap.Load_BIOS(bios, basic);
}
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)
{
MemMap.Load_ROM(ext_rom_1, ext_rom_size_1, ext_rom_mapper_1, ext_rom_2, ext_rom_size_2, ext_rom_mapper_2);
}
bool FrameAdvance(uint8_t controller_1, uint8_t controller_2, uint8_t* kb_rows_ptr, bool render, bool rendersound)
{
if ((MemMap.psg_pntr->Register[0xF] & 0x40) > 0)
{
MemMap.psg_pntr->Register[0xE] = controller_2;
}
else
{
MemMap.psg_pntr->Register[0xE] = controller_1;
}
MemMap.controller_byte_1 = controller_1;
MemMap.controller_byte_2 = controller_2;
MemMap.kb_rows = kb_rows_ptr;
MemMap.start_pressed = (controller_1 & 0x80) > 0;
MemMap.lagged = true;
uint32_t scanlinesPerFrame = 262;
vdp.SpriteLimit = true;
psg.num_samples = 0;
psg.sampleclock = 0;
for (uint32_t i = 0; i < scanlinesPerFrame; i++)
{
vdp.ScanLine = i;
vdp.RenderScanline(i);
if (vdp.ScanLine == 192)
{
vdp.InterruptPendingSet(true);
if (vdp.EnableInterrupts()) { cpu.FlagI = true; }
}
switch (sl_case)
{
case 0:
for (int i = 0; i < 14; i++)
{
cpu.ExecuteOne(16);
psg.sampleclock+=16;
psg.generate_sound();
}
cpu.ExecuteOne(4);
psg.sampleclock += 4;
sl_case = 1;
break;
case 1:
cpu.ExecuteOne(12);
psg.sampleclock += 12;
psg.generate_sound();
for (int i = 0; i < 13; i++)
{
cpu.ExecuteOne(16);
psg.sampleclock += 16;
psg.generate_sound();
}
cpu.ExecuteOne(8);
psg.sampleclock += 8;
sl_case = 2;
break;
case 2:
cpu.ExecuteOne(8);
psg.sampleclock += 8;
psg.generate_sound();
for (int i = 0; i < 13; i++)
{
cpu.ExecuteOne(16);
psg.sampleclock += 16;
psg.generate_sound();
}
cpu.ExecuteOne(12);
psg.sampleclock += 12;
sl_case = 3;
break;
case 3:
cpu.ExecuteOne(4);
psg.sampleclock += 4;
psg.generate_sound();
for (int i = 0; i < 14; i++)
{
cpu.ExecuteOne(16);
psg.sampleclock += 16;
psg.generate_sound();
}
sl_case = 0;
break;
}
}
return MemMap.lagged;
}
void GetVideo(uint32_t* dest)
{
uint32_t* src = vdp.FrameBuffer;
uint32_t* dst = dest;
std::memcpy(dst, src, sizeof uint32_t * 256 * 192);
}
uint32_t GetAudio(int32_t* dest, int32_t* n_samp)
{
int32_t* src = psg.samples;
int32_t* dst = dest;
std::memcpy(dst, src, sizeof int32_t * psg.num_samples * 2);
n_samp[0] = psg.num_samples;
return psg.sampleclock;
}
#pragma region State Save / Load
void SaveState(uint8_t* saver)
{
saver = vdp.SaveState(saver);
saver = cpu.SaveState(saver);
saver = psg.SaveState(saver);
saver = MemMap.SaveState(saver);
*saver = sl_case; saver++;
}
void LoadState(uint8_t* loader)
{
loader = vdp.LoadState(loader);
loader = cpu.LoadState(loader);
loader = psg.LoadState(loader);
loader = MemMap.LoadState(loader);
sl_case = *loader; loader++;
}
#pragma endregion
#pragma region Memory Domain Functions
uint8_t GetSysBus(uint32_t addr)
{
cpu.bank_num = cpu.bank_offset = addr & 0xFFFF;
cpu.bank_offset &= cpu.low_mask;
cpu.bank_num = (cpu.bank_num >> cpu.bank_shift)& cpu.high_mask;
return cpu.MemoryMap[cpu.bank_num][cpu.bank_offset];
}
uint8_t GetVRAM(uint32_t addr)
{
return vdp.VRAM[addr & 0x3FFF];
}
uint8_t GetRAM(uint32_t addr)
{
return MemMap.ram[addr & 0xFFFF];
}
#pragma endregion
#pragma region Tracer
void SetTraceCallback(void (*callback)(int))
{
cpu.TraceCallback = callback;
}
int GetHeaderLength()
{
return 105 + 1;
}
int GetDisasmLength()
{
return 48 + 1;
}
int GetRegStringLength()
{
return 86 + 1;
}
void GetHeader(char* h, int l)
{
memcpy(h, cpu.TraceHeader, l);
}
// the copy length l must be supplied ahead of time from GetRegStrngLength
void GetRegisterState(char* r, int t, int l)
{
if (t == 0)
{
memcpy(r, cpu.CPURegisterState().c_str(), l);
}
else
{
memcpy(r, cpu.No_Reg, l);
}
}
// the copy length l must be supplied ahead of time from GetDisasmLength
void GetDisassembly(char* d, int t, int l)
{
if (t == 0)
{
memcpy(d, cpu.CPUDisassembly().c_str(), l);
}
else if (t == 1)
{
memcpy(d, cpu.NMI_event, l);
}
else
{
memcpy(d, cpu.IRQ_event, l);
}
}
#pragma endregion
};
}

View File

@ -0,0 +1,131 @@
// GBHawk.cpp : Defines the exported functions for the DLL.
//
#include "GBHawk.h"
#include "Core.h"
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace GBHawk;
#pragma region Core
// Create pointer to a core instance
GBHawk_EXPORT GBCore* GB_create()
{
return new GBCore();
}
// free the memory from the core pointer
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)
{
p->Load_BIOS(bios, basic);
}
// load a rom into the core
GBHawk_EXPORT void GB_load(GBCore* p, uint8_t* rom_1, uint32_t size_1, uint32_t mapper_1, uint8_t* rom_2, uint32_t size_2, uint8_t mapper_2)
{
p->Load_ROM(rom_1, size_1, mapper_1, rom_2, size_2, mapper_2);
}
// advance a frame
GBHawk_EXPORT bool GB_frame_advance(GBCore* p, uint8_t ctrl1, uint8_t ctrl2, uint8_t* kbrows, bool render, bool sound)
{
return p->FrameAdvance(ctrl1, ctrl2, kbrows, render, sound);
}
// send video data to external video provider
GBHawk_EXPORT void GB_get_video(GBCore* p, uint32_t* dest)
{
p->GetVideo(dest);
}
// send audio data to external audio provider
GBHawk_EXPORT uint32_t GB_get_audio(GBCore* p, int32_t* dest, int32_t* n_samp)
{
return p->GetAudio(dest, n_samp);
}
#pragma region State Save / Load
// save state
GBHawk_EXPORT void GB_save_state(GBCore* p, uint8_t* saver)
{
p->SaveState(saver);
}
// load state
GBHawk_EXPORT void GB_load_state(GBCore* p, uint8_t* loader)
{
p->LoadState(loader);
}
#pragma endregion
#pragma region Memory Domain Functions
GBHawk_EXPORT uint8_t GB_getsysbus(GBCore* p, uint32_t addr) {
return p->GetSysBus(addr);
}
GBHawk_EXPORT uint8_t GB_getvram(GBCore* p, uint32_t addr) {
return p->GetVRAM(addr);
}
GBHawk_EXPORT uint8_t GB_getram(GBCore* p, uint32_t addr) {
return p->GetRAM(addr);
}
#pragma endregion
#pragma region Tracer
// set tracer callback
GBHawk_EXPORT void GB_settracecallback(GBCore* p, void (*callback)(int)) {
p->SetTraceCallback(callback);
}
// return the cpu trace header length
GBHawk_EXPORT int GB_getheaderlength(GBCore* p) {
return p->GetHeaderLength();
}
// return the cpu disassembly length
GBHawk_EXPORT int GB_getdisasmlength(GBCore* p) {
return p->GetDisasmLength();
}
// return the cpu register string length
GBHawk_EXPORT int GB_getregstringlength(GBCore* p) {
return p->GetRegStringLength();
}
// return the cpu trace header
GBHawk_EXPORT void GB_getheader(GBCore* p, char* h, int l) {
p->GetHeader(h, l);
}
// return the cpu register state
GBHawk_EXPORT void GB_getregisterstate(GBCore* p, char* r, int t, int l) {
p->GetRegisterState(r, t, l);
}
// return the cpu disassembly
GBHawk_EXPORT void GB_getdisassembly(GBCore* p, char* d, int t, int l) {
p->GetDisassembly(d, t, l);
}
#pragma endregion

View File

@ -0,0 +1,5 @@
#ifdef _WIN32
#define GBHawk_EXPORT extern "C" __declspec(dllexport)
#elif __linux__
#define GBHawk_EXPORT extern "C"
#endif

View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{FA59603F-32AB-429A-9186-B46114851290}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>GBHawk</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;GBHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;GBHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;GBHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;GBHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AY_3_8910.h" />
<ClInclude Include="Core.h" />
<ClInclude Include="GBHawk.h" />
<ClInclude Include="Memory.h" />
<ClInclude Include="TMS9918A.h" />
<ClInclude Include="LR35902.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="GBHawk.cpp" />
<ClCompile Include="Memory.cpp" />
<ClCompile Include="LR35902.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,29 @@
#include <cstdint>
#include <iomanip>
#include <string>
#include "Memory.h"
#include "LR35902.h"
using namespace std;
namespace GBHawk
{
void LR35902::WriteMemory(uint32_t addr, uint8_t value)
{
if ((addr & 0xFFFF) >= 0xFFFC)
{
mem_ctrl->MemoryWrite(addr, value);
}
}
uint8_t LR35902::ReadMemory(uint32_t addr)
{
return mem_ctrl->HardwareRead(addr);
}
uint8_t LR35902::SpeedFunc(uint32_t addr)
{
return mem_ctrl->HardwareRead(addr);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,229 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
#include "Memory.h"
#include "LR35902.h"
#include "TMS9918A.h"
#include "AY_3_8910.h"
using namespace std;
namespace GBHawk
{
uint8_t MemoryManager::HardwareRead(uint32_t port)
{
port &= 0xFF;
if (port == 0x98) // VDP
{
return vdp_pntr->ReadData();
}
else if (port == 0x99) // VDP
{
return vdp_pntr->ReadVdpStatus();
}
else if (port == 0xA2)
{
if (psg_pntr->port_sel == 0xE) { lagged = false; }
return psg_pntr->ReadReg();
}
else if (port == 0xA8)
{
return PortA8;
}
else if (port == 0xA9)
{
lagged = false;
return ~kb_rows[kb_rows_sel];
}
else if (port == 0xAA)
{
// TODO: casette, caps lamp, keyboard sound click
return kb_rows_sel;
}
return 0xFF;
}
void MemoryManager::HardwareWrite(uint32_t port, uint8_t value)
{
port &= 0xFF;
if (port == 0x98) // VDP
{
vdp_pntr->WriteVdpData(value);
}
else if(port == 0x99) // VDP
{
vdp_pntr->WriteVdpControl(value);
}
else if (port == 0xA0)
{
psg_pntr->port_sel = (value & 0xF);
}
else if (port == 0xA1)
{
psg_pntr->WriteReg(value);
// update controller port data if port F is written to
if (psg_pntr->port_sel == 0xF)
{
if ((psg_pntr->Register[0xF] & 0x40) > 0)
{
psg_pntr->Register[0xE] = controller_byte_2;
}
else
{
psg_pntr->Register[0xE] = controller_byte_1;
}
}
}
else if (port == 0xA8)
{
PortA8 = value;
remap();
}
else if (port == 0xAA)
{
kb_rows_sel = value & 0xF;
remap();
}
}
void MemoryManager::remap()
{
if ((PortA8 & 3) == 0)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i] = &bios_rom[(0x400 * i)];
cpu_pntr->MemoryMapMask[i] = 0;
}
}
else if ((PortA8 & 3) == 1)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i] = &rom_1[(0x400 * i)];
cpu_pntr->MemoryMapMask[i] = 0;
}
}
else if ((PortA8 & 3) == 2)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i] = &rom_2[(0x400 * i)];
cpu_pntr->MemoryMapMask[i] = 0;
}
}
else if ((PortA8 & 3) == 3)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i] = &ram[(0x400 * i)];
cpu_pntr->MemoryMapMask[i] = 0xFF;
}
}
if (((PortA8 >> 2) & 3) == 0)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 16] = &basic_rom[(0x400 * i)];
cpu_pntr->MemoryMapMask[i + 16] = 0;
}
}
else if (((PortA8 >> 2) & 3) == 1)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 16] = &rom_1[0x4000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 16] = 0;
}
}
else if (((PortA8 >> 2) & 3) == 2)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 16] = &rom_2[0x4000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 16] = 0;
}
}
else if (((PortA8 >> 2) & 3) == 3)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 16] = &ram[0x4000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 16] = 0xFF;
}
}
if (((PortA8 >> 4) & 3) == 0)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 32] = &unmapped[0];
cpu_pntr->MemoryMapMask[i + 32] = 0;
}
}
else if (((PortA8 >> 4) & 3) == 1)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 32] = &rom_1[0x8000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 32] = 0;
}
}
else if (((PortA8 >> 4) & 3) == 2)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 32] = &rom_2[0x8000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 32] = 0;
}
}
else if (((PortA8 >> 4) & 3) == 3)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 32] = &ram[0x8000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 32] = 0xFF;
}
}
if (((PortA8 >> 6) & 3) == 0)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 48] = &unmapped[0];
cpu_pntr->MemoryMapMask[i + 48] = 0;
}
}
else if (((PortA8 >> 6) & 3) == 1)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 48] = &rom_1[0xC000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 48] = 0;
}
}
else if (((PortA8 >> 6) & 3) == 2)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 48] = &rom_2[0xC000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 48] = 0;
}
}
else if (((PortA8 >> 6) & 3) == 3)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 48] = &ram[0xC000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 48] = 0xFF;
}
}
}
}

View File

@ -0,0 +1,138 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace GBHawk
{
class Z80A;
class TMS9918A;
class AY_3_8910;
class MemoryManager
{
public:
TMS9918A* vdp_pntr = nullptr;
AY_3_8910* psg_pntr = nullptr;
Z80A* 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;
uint32_t rom_mapper_1;
uint32_t rom_size_2;
uint32_t rom_mapper_2;
// controls are not stated
uint8_t controller_byte_1, controller_byte_2;
uint8_t* kb_rows;
// State
bool PortDEEnabled = false;
bool lagged;
bool start_pressed;
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 cart_ram[0x8000] = {};
uint8_t unmapped[0x400] = {};
MemoryManager()
{
};
uint8_t HardwareRead(uint32_t value);
void HardwareWrite(uint32_t addr, uint8_t value);
void remap();
// NOTE: only called from source when both are available and of correct size (0x4000)
void Load_BIOS(uint8_t* bios, uint8_t* basic)
{
bios_rom = new uint8_t[0x4000];
basic_rom = new uint8_t[0x4000];
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)
{
rom_1 = new uint8_t[ext_rom_size_1];
rom_2 = new uint8_t[ext_rom_size_2];
memcpy(rom_1, ext_rom_1, ext_rom_size_1);
memcpy(rom_2, ext_rom_2, ext_rom_size_2);
rom_size_1 = ext_rom_size_1 / 0x4000;
rom_mapper_1 = ext_rom_mapper_1;
rom_size_2 = ext_rom_size_2 / 0x4000;
rom_mapper_2 = ext_rom_mapper_2;
// default memory map setup
PortA8 = 0;
remap();
}
void MemoryWrite(uint32_t addr, uint8_t value)
{
}
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
{
*saver = (uint8_t)(PortDEEnabled ? 1 : 0); saver++;
*saver = (uint8_t)(lagged ? 1 : 0); saver++;
*saver = (uint8_t)(start_pressed ? 1 : 0); saver++;
*saver = kb_rows_sel; saver++;
*saver = PortA8; saver++;
*saver = reg_FFFC; saver++;
*saver = reg_FFFD; saver++;
*saver = reg_FFFE; saver++;
*saver = reg_FFFF; saver++;
std::memcpy(saver, &ram, 0x10000); saver += 0x10000;
std::memcpy(saver, &cart_ram, 0x8000); saver += 0x8000;
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
PortDEEnabled = *loader == 1; loader++;
lagged = *loader == 1; loader++;
start_pressed = *loader == 1; loader++;
kb_rows_sel = *loader; loader++;
PortA8 = *loader; loader++;
reg_FFFC = *loader; loader++;
reg_FFFD = *loader; loader++;
reg_FFFE = *loader; loader++;
reg_FFFF = *loader; loader++;
std::memcpy(&ram, loader, 0x10000); loader += 0x10000;
std::memcpy(&cart_ram, loader, 0x8000); loader += 0x8000;
remap();
return loader;
}
#pragma endregion
};
}

View File

@ -0,0 +1,610 @@
#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
};
}

View File

@ -0,0 +1,2 @@
#define GBHawk_API __declspec(dllexport)
#define GBHawk_API __declspec(dllimport)