MSXHawk: start converting to actual msx machine
This commit is contained in:
parent
d70afbb643
commit
c171505d64
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
@ -1,2 +1,2 @@
|
|||
#define MSXHAWK_API __declspec(dllexport)
|
||||
#define MSXHAWK_API __declspec(dllimport)
|
||||
#define MSXHawk_API __declspec(dllexport)
|
||||
#define MSXHawk_API __declspec(dllimport)
|
||||
|
|
Loading…
Reference in New Issue