MSXHawk: start converting to actual msx machine

This commit is contained in:
alyosha-tas 2020-01-22 19:26:08 -05:00
parent d70afbb643
commit c171505d64
12 changed files with 790 additions and 1577 deletions

View File

@ -12,23 +12,36 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
{
# region Core
/// <returns>opaque state pointer</returns>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr MSX_create();
/// <param name="core">opaque state pointer</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void MSX_destroy(IntPtr core);
/// <summary>
/// Load BIOS and BASIC image. each must be 16K in size
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="bios">the rom data, can be disposed of once this function returns</param>
/// <param name="basic">length of romdata in bytes</param>
/// <returns>0 on success, negative value on failure.</returns>
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int MSX_load_bios(IntPtr core, byte[] bios, byte[] basic);
/// <summary>
/// Load ROM image.
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="romdata">the rom data, can be disposed of once this function returns</param>
/// <param name="length">length of romdata in bytes</param>
/// <param name="mapper">Mapper number to load core with</param>
/// <param name="romdata_1">the rom data, can be disposed of once this function returns</param>
/// <param name="length_1">length of romdata in bytes</param>
/// <param name="mapper_1">Mapper number to load core with</param>
/// <param name="romdata_2">the rom data, can be disposed of once this function returns</param>
/// <param name="length_2">length of romdata in bytes</param>
/// <param name="mapper_2">Mapper number to load core with</param>
/// <returns>0 on success, negative value on failure.</returns>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int MSX_load(IntPtr core, byte[] romdata, uint length, int mapper);
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int MSX_load(IntPtr core, byte[] romdata_1, uint length_1, int mapper_1, byte[] romdata_2, uint length_2, int mapper_2);
/// <summary>
/// Advance a frame and send controller data.
@ -39,7 +52,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// <param name="render">length of romdata in bytes</param>
/// <param name="sound">Mapper number to load core with</param>
/// <returns>0 on success, negative value on failure.</returns>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool MSX_frame_advance(IntPtr core, byte ctrl1, byte ctrl2, bool render, bool sound);
/// <summary>
@ -47,7 +60,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="videobuf">where to send video to</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void MSX_get_video(IntPtr core, int[] videobuf);
/// <summary>
@ -58,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// <param name="aud_buf_R">where to send right audio to</param>
/// <param name="n_samp_L">number of left samples</param>
/// <param name="n_samp_R">number of right samples</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint MSX_get_audio(IntPtr core, uint[] aud_buf_L, uint[] aud_buf_R, ref uint n_samp_L, ref uint n_samp_R);
#endregion
@ -70,7 +83,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="saver">save buffer</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void MSX_save_state(IntPtr core, byte[] saver);
/// <summary>
@ -90,7 +103,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">system bus address</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte MSX_getsysbus(IntPtr core, int addr);
/// <summary>
@ -98,7 +111,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">vram address</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte MSX_getvram(IntPtr core, int addr);
@ -117,28 +130,28 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="callback">null to clear</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void MSX_settracecallback(IntPtr core, TraceCallback callback);
/// <summary>
/// get the trace logger header length
/// </summary>
/// <param name="core">opaque state pointer</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int MSX_getheaderlength(IntPtr core);
/// <summary>
/// get the trace logger disassembly length, a constant
/// </summary>
/// <param name="core">opaque state pointer</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int MSX_getdisasmlength(IntPtr core);
/// <summary>
/// get the trace logger register string length, a constant
/// </summary>
/// <param name="core">opaque state pointer</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int MSX_getregstringlength(IntPtr core);
/// <summary>
@ -147,7 +160,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// <param name="core">opaque state pointer</param>
/// <param name="h">pointer to const char *</param>
/// <param name="callback">null to clear</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void MSX_getheader(IntPtr core, StringBuilder h, int l);
/// <summary>
@ -157,7 +170,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// <param name="r">pointer to const char *</param>
/// <param name="t">call type</param>
/// <param name="l">copy length, must be obtained from appropriate get legnth function</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void MSX_getregisterstate(IntPtr core, StringBuilder h, int t, int l);
/// <summary>
@ -167,7 +180,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// <param name="d">pointer to const char *</param>
/// <param name="t">call type</param>
/// <param name="l">copy length, must be obtained from appropriate get legnth function</param>
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void MSX_getdisassembly(IntPtr core, StringBuilder h, int t, int l);
#endregion
}

View File

@ -149,7 +149,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
#region Video
public int _frameHz = 60;
public int[] _vidbuffer = new int[160 * 144];
public int[] _vidbuffer = new int[192 * 256];
public int[] GetVideoBuffer()
{

View File

@ -27,8 +27,15 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
Array.Resize(ref RomData, ((RomData.Length / BankSize) + 1) * BankSize);
}
byte[] Bios = null;
byte[] Basic = null;
Bios = comm.CoreFileProvider.GetFirmware("MSX", "bios", true, "BIOS Not Found, Cannot Load");
Basic = comm.CoreFileProvider.GetFirmware("MSX", "basic", true, "BIOS Not Found, Cannot Load");
MSX_Pntr = LibMSX.MSX_create();
LibMSX.MSX_load(MSX_Pntr, RomData, (uint)RomData.Length, 0);
LibMSX.MSX_load_bios(MSX_Pntr, Bios, Basic);
LibMSX.MSX_load(MSX_Pntr, RomData, (uint)RomData.Length, 0, RomData, (uint)RomData.Length, 0);
blip_L.SetRates(3579545, 44100);
blip_R.SetRates(3579545, 44100);
@ -61,6 +68,8 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
IntPtr MSX_Pntr { get; set; } = IntPtr.Zero;
byte[] MSX_core = new byte[0x20000];
public byte[] _bios;
public byte[] _basic;
// Constants
private const int BankSize = 16384;

View File

@ -5,7 +5,7 @@
#include "Z80A.h"
#include "PSG.h"
#include "VDP.h"
#include "TMS9918A.h"
#include "Memory.h"
namespace MSXHawk
@ -24,14 +24,19 @@ namespace MSXHawk
psg.Clock_Divider = 16;
};
VDP vdp;
TMS9918A vdp;
Z80A cpu;
SN76489sms psg;
MemoryManager MemMap;
void Load_ROM(uint8_t* ext_rom, uint32_t ext_rom_size, uint32_t ext_rom_mapper)
void Load_BIOS(uint8_t* bios, uint8_t* basic)
{
MemMap.Load_ROM(ext_rom, ext_rom_size, ext_rom_mapper);
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, bool render, bool rendersound)
@ -41,23 +46,28 @@ namespace MSXHawk
MemMap.start_pressed = (controller_1 & 0x80) > 0;
MemMap.lagged = true;
int scanlinesPerFrame = 262;
uint32_t scanlinesPerFrame = 262;
vdp.SpriteLimit = true;
psg.num_samples_L = 0;
psg.num_samples_R = 0;
psg.sampleclock = 0;
for (int i = 0; i < scanlinesPerFrame; i++)
for (uint32_t i = 0; i < scanlinesPerFrame; i++)
{
vdp.ScanLine = i;
vdp.RenderCurrentScanline(render);
vdp.RenderScanline(i);
vdp.ProcessFrameInterrupt();
vdp.ProcessLineInterrupt();
if (vdp.ScanLine == 192)
{
vdp.InterruptPendingSet(true);
for (int j = 0; j < vdp.IPeriod; j++)
if (vdp.EnableInterrupts())
cpu.NonMaskableInterruptset(true);
}
for (uint32_t j = 0; j < vdp.IPeriod; j++)
{
cpu.ExecuteOne();
@ -69,27 +79,16 @@ namespace MSXHawk
}
psg.sampleclock++;
}
if (vdp.ScanLine == scanlinesPerFrame - 1)
{
vdp.ProcessGGScreen();
//vdp.ProcessOverscan();
}
}
return MemMap.lagged;
}
void GetVideo(uint32_t* dest) {
uint32_t* src = vdp.GameGearFrameBuffer;
uint32_t* src = vdp.FrameBuffer;
uint32_t* dst = dest;
for (int i = 0; i < 144; i++)
{
std::memcpy(dst, src, sizeof uint32_t * 160);
src += 160;
dst += 160;
}
std::memcpy(dst, src, sizeof uint32_t * 256 * 192);
}
uint32_t GetAudio(uint32_t* dest_L, uint32_t* dest_R, uint32_t* n_samp_L, uint32_t* n_samp_R)

View File

@ -13,37 +13,43 @@ using namespace MSXHawk;
#pragma region Core
// Create pointer to a core instance
MSXHAWK_EXPORT MSXCore* MSX_create()
MSXHawk_EXPORT MSXCore* MSX_create()
{
return new MSXCore();
}
// free the memory from the core pointer
MSXHAWK_EXPORT void MSX_destroy(MSXCore* p)
MSXHawk_EXPORT void MSX_destroy(MSXCore* p)
{
std::free(p);
}
// load a rom into the core
MSXHAWK_EXPORT void MSX_load(MSXCore* p, uint8_t* rom, unsigned int size, int mapper)
// load bios and basic into the core
MSXHawk_EXPORT void MSX_load_bios(MSXCore* p, uint8_t* bios, uint8_t* basic)
{
p->Load_ROM(rom, size, mapper);
p->Load_BIOS(bios, basic);
}
// load a rom into the core
MSXHawk_EXPORT void MSX_load(MSXCore* 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
MSXHAWK_EXPORT void MSX_frame_advance(MSXCore* p, uint8_t ctrl1, uint8_t ctrl2, bool render, bool sound)
MSXHawk_EXPORT void MSX_frame_advance(MSXCore* p, uint8_t ctrl1, uint8_t ctrl2, bool render, bool sound)
{
p->FrameAdvance(ctrl1, ctrl2, render, sound);
}
// send video data to external video provider
MSXHAWK_EXPORT void MSX_get_video(MSXCore* p, uint32_t* dest)
MSXHawk_EXPORT void MSX_get_video(MSXCore* p, uint32_t* dest)
{
p->GetVideo(dest);
}
// send audio data to external audio provider
MSXHAWK_EXPORT uint32_t MSX_get_audio(MSXCore* p, uint32_t* dest_L, uint32_t* dest_R, uint32_t* n_samp_L, uint32_t* n_samp_R)
MSXHawk_EXPORT uint32_t MSX_get_audio(MSXCore* p, uint32_t* dest_L, uint32_t* dest_R, uint32_t* n_samp_L, uint32_t* n_samp_R)
{
return p->GetAudio(dest_L, dest_R, n_samp_L, n_samp_R);
}
@ -51,13 +57,13 @@ MSXHAWK_EXPORT uint32_t MSX_get_audio(MSXCore* p, uint32_t* dest_L, uint32_t* de
#pragma region State Save / Load
// save state
MSXHAWK_EXPORT void MSX_save_state(MSXCore* p, uint8_t* saver)
MSXHawk_EXPORT void MSX_save_state(MSXCore* p, uint8_t* saver)
{
p->SaveState(saver);
}
// load state
MSXHAWK_EXPORT void MSX_load_state(MSXCore* p, uint8_t* loader)
MSXHawk_EXPORT void MSX_load_state(MSXCore* p, uint8_t* loader)
{
p->LoadState(loader);
}
@ -66,11 +72,11 @@ MSXHAWK_EXPORT void MSX_load_state(MSXCore* p, uint8_t* loader)
#pragma region Memory Domain Functions
MSXHAWK_EXPORT uint8_t MSX_getsysbus(MSXCore* p, uint32_t addr) {
MSXHawk_EXPORT uint8_t MSX_getsysbus(MSXCore* p, uint32_t addr) {
return p->GetSysBus(addr);
}
MSXHAWK_EXPORT uint8_t MSX_getvram(MSXCore* p, uint32_t addr) {
MSXHawk_EXPORT uint8_t MSX_getvram(MSXCore* p, uint32_t addr) {
return p->GetVRAM(addr);
}
#pragma endregion
@ -79,37 +85,37 @@ MSXHAWK_EXPORT uint8_t MSX_getvram(MSXCore* p, uint32_t addr) {
#pragma region Tracer
// set tracer callback
MSXHAWK_EXPORT void MSX_settracecallback(MSXCore* p, void (*callback)(int)) {
MSXHawk_EXPORT void MSX_settracecallback(MSXCore* p, void (*callback)(int)) {
p->SetTraceCallback(callback);
}
// return the cpu trace header length
MSXHAWK_EXPORT int MSX_getheaderlength(MSXCore* p) {
MSXHawk_EXPORT int MSX_getheaderlength(MSXCore* p) {
return p->GetHeaderLength();
}
// return the cpu disassembly length
MSXHAWK_EXPORT int MSX_getdisasmlength(MSXCore* p) {
MSXHawk_EXPORT int MSX_getdisasmlength(MSXCore* p) {
return p->GetDisasmLength();
}
// return the cpu register string length
MSXHAWK_EXPORT int MSX_getregstringlength(MSXCore* p) {
MSXHawk_EXPORT int MSX_getregstringlength(MSXCore* p) {
return p->GetRegStringLength();
}
// return the cpu trace header
MSXHAWK_EXPORT void MSX_getheader(MSXCore* p, char* h, int l) {
MSXHawk_EXPORT void MSX_getheader(MSXCore* p, char* h, int l) {
p->GetHeader(h, l);
}
// return the cpu register state
MSXHAWK_EXPORT void MSX_getregisterstate(MSXCore* p, char* r, int t, int l) {
MSXHawk_EXPORT void MSX_getregisterstate(MSXCore* p, char* r, int t, int l) {
p->GetRegisterState(r, t, l);
}
// return the cpu disassembly
MSXHAWK_EXPORT void MSX_getdisassembly(MSXCore* p, char* d, int t, int l) {
MSXHawk_EXPORT void MSX_getdisassembly(MSXCore* p, char* d, int t, int l) {
p->GetDisassembly(d, t, l);
}

View File

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

View File

@ -71,19 +71,19 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<TargetName>MSXHAWK</TargetName>
<TargetName>MSXHawk</TargetName>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TargetName>MSXHAWK</TargetName>
<TargetName>MSXHawk</TargetName>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetName>MSXHAWK</TargetName>
<TargetName>MSXHawk</TargetName>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetName>MSXHAWK</TargetName>
<TargetName>MSXHawk</TargetName>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@ -91,7 +91,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;MSXHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;MSXHawk_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
@ -106,7 +106,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;MSXHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_DEBUG;MSXHawk_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
@ -123,7 +123,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;MSXHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;NDEBUG;MSXHawk_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
@ -142,7 +142,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;MSXHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NDEBUG;MSXHawk_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
@ -160,7 +160,7 @@
<ItemGroup>
<ClInclude Include="Core.h" />
<ClInclude Include="Memory.h" />
<ClInclude Include="VDP.h" />
<ClInclude Include="TMS9918A.h" />
<ClInclude Include="PSG.h" />
<ClInclude Include="Z80A.h" />
<ClInclude Include="MSXHawk.h" />

View File

@ -5,7 +5,7 @@
#include "Memory.h"
#include "Z80A.h"
#include "VDP.h"
#include "TMS9918A.h"
#include "PSG.h"
using namespace std;
@ -15,143 +15,172 @@ namespace MSXHawk
uint8_t MemoryManager::HardwareRead(uint32_t port)
{
port &= 0xFF;
if (port < 0x40) // General IO ports
{
switch (port)
{
case 0x00: return ReadPort0();
case 0x01: return Port01;
case 0x02: return Port02;
case 0x03: return Port03;
case 0x04: return Port04;
case 0x05: return Port05;
case 0x06: return 0xFF;
case 0x3E: return Port3E;
default: return 0xFF;
}
}
if (port < 0x80) // VDP Vcounter/HCounter
{
if ((port & 1) == 0)
return vdp_pntr->ReadVLineCounter();
else
return vdp_pntr->ReadHLineCounter();
}
if (port < 0xC0) // VDP data/control ports
if (port >= 0xA0 && port < 0xC0) // VDP data/control ports
{
if ((port & 1) == 0)
return vdp_pntr->ReadData();
else
return vdp_pntr->ReadVdpStatus();
}
switch (port)
{
case 0xC0:
case 0xDC: return ReadControls1();
case 0xC1:
case 0xDD: return ReadControls2();
case 0xDE: return PortDEEnabled ? PortDE : 0xFF;
case 0xF2: return 0xFF;
default: return 0xFF;
}
return 0xFF;
}
void MemoryManager::HardwareWrite(uint32_t port, uint8_t value)
{
port &= 0xFF;
if (port < 0x40) // general IO ports
if (port == 0x98) // VDP
{
switch (port & 0xFF)
{
case 0x01: Port01 = value; break;
case 0x02: Port02 = value; break;
case 0x03: Port03 = value; break;
case 0x04: /*Port04 = value*/; break; // receive port, not sure what writing does
case 0x05: Port05 = (uint8_t)(value & 0xF8); break;
case 0x06: psg_pntr->Set_Panning(value); break;
case 0x3E: Port3E = value; break;
case 0x3F: Port3F = value; break;
}
}
else if (port < 0x80) // PSG
vdp_pntr->WriteVdpData(value);
}
else if(port == 0x99) // VDP
{
vdp_pntr->WriteVdpControl(value);
}
else if (port == 0xA1)
{
psg_pntr->WriteReg(value);
}
else if (port < 0xC0) // VDP
else if (port == 0xA8)
{
if ((port & 1) == 0)
{
vdp_pntr->WriteVdpData(value);
}
else
{
vdp_pntr->WriteVdpControl(value);
}
PortA8 = value;
remap();
}
else if (port == 0xDE && PortDEEnabled) PortDE = value;
}
void MemoryManager::remap_ROM_0()
void MemoryManager::remap()
{
// 0x0000 - 0x03FF always maps to start of ROM
cpu_pntr->MemoryMap[0] = &rom[0];
cpu_pntr->MemoryMapMask[0] = 0;
for (uint32_t i = 1; i < 16; i++)
{
cpu_pntr->MemoryMap[i] = &rom[(reg_FFFD % rom_size) * 0x4000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i] = 0;
}
}
void MemoryManager::remap_ROM_1()
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 16] = &rom[(reg_FFFE % rom_size) * 0x4000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 16] = 0;
}
}
void MemoryManager::remap_ROM_2()
{
if ((reg_FFFC & 0x8) > 0)
if ((PortA8 & 3) == 0)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 32] = &cart_ram[((reg_FFFC >> 2) & 0x1) * 0x4000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 32] = 0xFF;
cpu_pntr->MemoryMap[i] = &bios_rom[(0x400 * i)];
cpu_pntr->MemoryMapMask[i] = 0;
}
}
else
else if ((PortA8 & 3) == 1)
{
for (int i = 0; i < 16; i++)
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 32] = &rom[(reg_FFFF % rom_size) * 0x4000 + (0x400 * 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[0xC000 + (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[04000 + (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[04000 + (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[0x8000 + (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;
}
}
}
void MemoryManager::remap_RAM()
{
if ((reg_FFFC & 0x10) > 0)
else if (((PortA8 >> 4) & 3) == 1)
{
for (uint32_t i = 0; i < 16; i++)
{
cpu_pntr->MemoryMap[i + 48] = &cart_ram[(0x400 * i)];
cpu_pntr->MemoryMapMask[i + 48] = 0xFF;
cpu_pntr->MemoryMap[i + 32] = &rom_1[0x8000 + (0x400 * i)];
cpu_pntr->MemoryMapMask[i + 32] = 0;
}
}
else
else if (((PortA8 >> 4) & 3) == 2)
{
for (uint32_t i = 0; i < 8; i++)
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[0x4000 + (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[(0x400 * i)];
cpu_pntr->MemoryMap[i + 48 + 8] = &ram[(0x400 * i)];
cpu_pntr->MemoryMapMask[i + 48] = 0xFF;
cpu_pntr->MemoryMapMask[i + 48 + 8] = 0xFF;
}
}
}

View File

@ -8,21 +8,26 @@ using namespace std;
namespace MSXHawk
{
class Z80A;
class VDP;
class TMS9918A;
class SN76489sms;
class MemoryManager
{
public:
VDP* vdp_pntr = nullptr;
TMS9918A* vdp_pntr = nullptr;
SN76489sms* psg_pntr = nullptr;
Z80A* cpu_pntr = nullptr;
uint8_t* rom = 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;
uint32_t rom_mapper;
uint32_t rom_size_1;
uint32_t rom_mapper_1;
uint32_t rom_size_2;
uint32_t rom_mapper_2;
// State
bool PortDEEnabled = false;
@ -30,17 +35,11 @@ namespace MSXHawk
bool start_pressed;
uint8_t controller_byte_1, controller_byte_2;
uint8_t Port01 = 0xFF;
uint8_t Port02 = 0xFF;
uint8_t Port03 = 0x00;
uint8_t Port04 = 0xFF;
uint8_t Port05 = 0x00;
uint8_t Port3E = 0xAF;
uint8_t Port3F = 0xFF;
uint8_t PortDE = 0x00;
uint8_t PortA8 = 0x00;
uint8_t reg_FFFC, reg_FFFD, reg_FFFE, reg_FFFF;
uint8_t ram[0x2000] = {};
uint8_t ram[0x10000] = {};
uint8_t cart_ram[0x8000] = {};
uint8_t unmapped[0x400] = {};
MemoryManager()
{
@ -51,53 +50,32 @@ namespace MSXHawk
void HardwareWrite(uint32_t addr, uint8_t value);
void remap_ROM_0();
void remap();
void remap_ROM_1();
void remap_ROM_2();
void remap_RAM();
void Load_ROM(uint8_t* ext_rom, uint32_t ext_rom_size, uint32_t ext_rom_mapper)
void Load_BIOS(uint8_t* bios, uint8_t* basic)
{
rom = ext_rom;
rom_size = ext_rom_size / 0x4000;
rom_mapper = ext_rom_mapper;
bios_rom = bios;
basic_rom = 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)
{
rom_1 = ext_rom_1;
rom_size_1 = ext_rom_size_1 / 0x4000;
rom_mapper_1 = ext_rom_mapper_1;
rom_2 = ext_rom_2;
rom_size_2 = ext_rom_size_2 / 0x4000;
rom_mapper_2 = ext_rom_mapper_2;
// default memory map setup
reg_FFFC = 0;
reg_FFFD = 0;
reg_FFFE = 1;
reg_FFFF = 2;
remap_ROM_0();
remap_ROM_1();
remap_ROM_2();
remap_RAM();
PortA8 = 0;
remap();
}
void MemoryWrite(uint32_t addr, uint8_t value)
{
switch (addr)
{
case 0xFFFC:
reg_FFFC = value;
remap_ROM_2();
remap_RAM();
break;
case 0xFFFD:
reg_FFFD = value;
remap_ROM_0();
break;
case 0xFFFE:
reg_FFFE = value;
remap_ROM_1();
break;
case 0xFFFF:
reg_FFFF = value;
remap_ROM_2();
break;
}
}
uint8_t ReadPort0()
@ -131,19 +109,6 @@ namespace MSXHawk
value &= ~(controller_byte_2 & 0xF);
if ((Port3F & 0x0F) == 5)
{
if (Port3F >> 4 == 0x0F)
{
value |= 0xC0;
}
else
{
value &= 0x3F;
}
}
return value;
}
@ -157,14 +122,7 @@ namespace MSXHawk
*saver = controller_byte_1; saver++;
*saver = controller_byte_2; saver++;
*saver = Port01; saver++;
*saver = Port02; saver++;
*saver = Port03; saver++;
*saver = Port04; saver++;
*saver = Port05; saver++;
*saver = Port3E; saver++;
*saver = Port3F; saver++;
*saver = PortDE; saver++;
*saver = PortA8; saver++;
*saver = reg_FFFC; saver++;
*saver = reg_FFFD; saver++;
*saver = reg_FFFE; saver++;
@ -184,26 +142,16 @@ namespace MSXHawk
controller_byte_1 = *loader; loader++;
controller_byte_2 = *loader; loader++;
Port01 = *loader; loader++;
Port02 = *loader; loader++;
Port03 = *loader; loader++;
Port04 = *loader; loader++;
Port05 = *loader; loader++;
Port3E = *loader; loader++;
Port3F = *loader; loader++;
PortDE = *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, 0x2000); loader += 0x2000;
std::memcpy(&cart_ram, loader, 0x2000); loader += 0x2000;
std::memcpy(&ram, loader, 0x10000); loader += 0x10000;
std::memcpy(&cart_ram, loader, 0x8000); loader += 0x8000;
remap_ROM_0();
remap_ROM_1();
remap_ROM_2();
remap_RAM();
remap();
return loader;
}

View File

@ -0,0 +1,523 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace MSXHawk
{
class TMS9918A
{
public:
#pragma region VDP
TMS9918A()
{
}
// external pointers to CPU
bool* INT_FLAG = 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 Registers[8] = {};
uint8_t VRAM[0x4000]; //16kb video RAM
int32_t ScanLine;
uint32_t IPeriod = 228;
uint32_t VdpAddress;
uint32_t TmsMode;
uint32_t ColorTableBase;
uint32_t PatternGeneratorBase;
uint32_t SpritePatternGeneratorBase;
uint32_t TmsPatternNameTableBase;
uint32_t TmsSpriteAttributeBase;
uint32_t FrameBuffer[192 * 256] = {};
uint32_t BackgroundColor = 0;
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
uint32_t reg = value & 0x0F;
WriteRegister(reg, 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();
INT_FLAG[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;
INT_FLAG[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;
}
uint32_t yc = scanLine / 8;
uint32_t yofs = scanLine % 8;
uint32_t FrameBufferOffset = scanLine * 256;
uint32_t PatternNameOffset = TmsPatternNameTableBase + (yc * 32);
uint32_t ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 32; xc++)
{
uint32_t pn = VRAM[PatternNameOffset++];
uint32_t pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs];
uint32_t colorEntry = VRAM[ColorTableBase + (pn / 8)];
uint32_t fgIndex = (colorEntry >> 4) & 0x0F;
uint32_t bgIndex = colorEntry & 0x0F;
uint32_t fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
uint32_t 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;
}
uint32_t yc = scanLine / 8;
uint32_t yofs = scanLine % 8;
uint32_t FrameBufferOffset = scanLine * 256;
uint32_t PatternNameOffset = TmsPatternNameTableBase + (yc * 40);
uint32_t ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 40; xc++)
{
uint32_t pn = VRAM[PatternNameOffset++];
uint32_t pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs];
uint32_t colorEntry = Registers[7];
uint32_t fgIndex = (colorEntry >> 4) & 0x0F;
uint32_t bgIndex = colorEntry & 0x0F;
uint32_t fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
uint32_t 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;
}
uint32_t yrow = scanLine / 8;
uint32_t yofs = scanLine % 8;
uint32_t FrameBufferOffset = scanLine * 256;
uint32_t PatternNameOffset = TmsPatternNameTableBase + (yrow * 32);
uint32_t PatternGeneratorOffset = (((Registers[4] & 4) << 11) & 0x2000);
uint32_t ColorOffset = (ColorTableBase & 0x2000);
uint32_t ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 32; xc++)
{
uint32_t pn = VRAM[PatternNameOffset++] + ((yrow / 8) * 0x100);
uint32_t pv = VRAM[PatternGeneratorOffset + (pn * 8) + yofs];
uint32_t colorEntry = VRAM[ColorOffset + (pn * 8) + yofs];
uint32_t fgIndex = (colorEntry >> 4) & 0x0F;
uint32_t bgIndex = colorEntry & 0x0F;
uint32_t fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
uint32_t 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;
}
uint32_t yc = scanLine / 8;
bool top = (scanLine & 4) == 0; // am I in the top 4 pixels of an 8-pixel character?
uint32_t FrameBufferOffset = scanLine * 256;
uint32_t PatternNameOffset = TmsPatternNameTableBase + (yc * 32);
uint32_t ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 32; xc++)
{
uint32_t pn = VRAM[PatternNameOffset++];
uint32_t pv = VRAM[PatternGeneratorBase + (pn * 8) + ((yc & 3) * 2) + (top ? 0 : 1)];
uint32_t lColorIndex = pv & 0xF;
uint32_t rColorIndex = pv >> 4;
uint32_t lColor = lColorIndex == 0 ? ScreenBGColor : PaletteTMS9918[lColorIndex];
uint32_t 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;
}
}
uint8_t ScanlinePriorityBuffer[256] = {};
uint8_t SpriteCollisionBuffer[256] = {};
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)
{
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
return loader;
}
#pragma endregion
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
#define MSXHAWK_API __declspec(dllexport)
#define MSXHAWK_API __declspec(dllimport)
#define MSXHawk_API __declspec(dllexport)
#define MSXHawk_API __declspec(dllimport)