GBHawk: start C++'ification
This commit is contained in:
parent
26619d367b
commit
d0cf220391
|
@ -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
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
#ifdef _WIN32
|
||||
#define GBHawk_EXPORT extern "C" __declspec(dllexport)
|
||||
#elif __linux__
|
||||
#define GBHawk_EXPORT extern "C"
|
||||
#endif
|
|
@ -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>
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
#define GBHawk_API __declspec(dllexport)
|
||||
#define GBHawk_API __declspec(dllimport)
|
Loading…
Reference in New Issue