MSXHawk: Tracer Support
This commit is contained in:
parent
1e195243be
commit
ce7d6cdcf5
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Computers.MSX
|
namespace BizHawk.Emulation.Cores.Computers.MSX
|
||||||
{
|
{
|
||||||
|
@ -39,5 +40,68 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
|
||||||
/// <returns>0 on success, negative value on failure.</returns>
|
/// <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);
|
public static extern bool MSX_frame_advance(IntPtr core, byte ctrl1, byte ctrl2, bool render, bool sound);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get Video data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="core">opaque state pointer</param>
|
||||||
|
/// <param name="ctrl1">controller data for player 1</param>
|
||||||
|
/// <param name="ctrl2">controller data for player 2</param>
|
||||||
|
/// <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)]
|
||||||
|
public static extern void MSX_get_video(IntPtr core, int[] videobuf);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// type of the cpu trace callback
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">type of event</param>
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate void TraceCallback(int t);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// set a callback for trace logging
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="core">opaque state pointer</param>
|
||||||
|
/// <param name="callback">null to clear</param>
|
||||||
|
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern void MSX_settracecallback(IntPtr core, TraceCallback callback);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// get the trace logger header
|
||||||
|
/// </summary>
|
||||||
|
/// <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)]
|
||||||
|
public static extern void MSX_getheader(IntPtr core, StringBuilder h);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// get the trace logger header length
|
||||||
|
/// </summary>
|
||||||
|
/// <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)]
|
||||||
|
public static extern int MSX_getheaderlength(IntPtr core);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// get the register state from the cpu
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="core">opaque state pointer</param>
|
||||||
|
/// <param name="r">pointer to const char *</param>
|
||||||
|
/// <param name="t">call type</param>
|
||||||
|
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern void MSX_getregisterstate(IntPtr core, StringBuilder h, int t);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// get the register state from the cpu
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="core">opaque state pointer</param>
|
||||||
|
/// <param name="d">pointer to const char *</param>
|
||||||
|
/// <param name="t">call type</param>
|
||||||
|
[DllImport("MSXHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern void MSX_getdisassembly(IntPtr core, StringBuilder h, int t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,21 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
|
||||||
_controller = controller;
|
_controller = controller;
|
||||||
|
|
||||||
_frame++;
|
_frame++;
|
||||||
|
|
||||||
|
if (Tracer.Enabled)
|
||||||
|
{
|
||||||
|
tracecb = MakeTrace;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tracecb = null;
|
||||||
|
}
|
||||||
|
|
||||||
return LibMSX.MSX_frame_advance(MSX_Pntr, 0, 0, true, true);
|
LibMSX.MSX_settracecallback(MSX_Pntr, tracecb);
|
||||||
|
|
||||||
|
LibMSX.MSX_frame_advance(MSX_Pntr, 0, 0, true, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Frame => _frame;
|
public int Frame => _frame;
|
||||||
|
@ -119,13 +132,12 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
|
||||||
#region Video
|
#region Video
|
||||||
public int _frameHz = 60;
|
public int _frameHz = 60;
|
||||||
|
|
||||||
public int[] _vidbuffer;
|
public int[] _vidbuffer = new int[160 * 144];
|
||||||
|
|
||||||
public int[] frame_buffer = new int[160 * 144];
|
|
||||||
|
|
||||||
public int[] GetVideoBuffer()
|
public int[] GetVideoBuffer()
|
||||||
{
|
{
|
||||||
return frame_buffer;
|
LibMSX.MSX_get_video(MSX_Pntr, _vidbuffer);
|
||||||
|
return _vidbuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int VirtualWidth => 160;
|
public int VirtualWidth => 160;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Computers.MSX
|
namespace BizHawk.Emulation.Cores.Computers.MSX
|
||||||
|
@ -39,8 +39,14 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
|
||||||
|
|
||||||
InputCallbacks = new InputCallbackSystem();
|
InputCallbacks = new InputCallbackSystem();
|
||||||
|
|
||||||
//var serviceProvider = ServiceProvider as BasicServiceProvider;
|
int new_header_size = LibMSX.MSX_getheaderlength(MSX_Pntr);
|
||||||
//serviceProvider.Register<ITraceable>(Tracer);
|
StringBuilder new_header = new StringBuilder(new_header_size);
|
||||||
|
LibMSX.MSX_getheader(MSX_Pntr, new_header);
|
||||||
|
|
||||||
|
Tracer = new TraceBuffer { Header = new_header.ToString() };
|
||||||
|
|
||||||
|
var serviceProvider = ServiceProvider as BasicServiceProvider;
|
||||||
|
serviceProvider.Register<ITraceable>(Tracer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HardReset()
|
public void HardReset()
|
||||||
|
@ -63,7 +69,29 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
|
||||||
|
|
||||||
public DisplayType Region => DisplayType.NTSC;
|
public DisplayType Region => DisplayType.NTSC;
|
||||||
|
|
||||||
private readonly ITraceable Tracer;
|
#region Trace Logger
|
||||||
|
private ITraceable Tracer;
|
||||||
|
|
||||||
|
private LibMSX.TraceCallback tracecb;
|
||||||
|
|
||||||
|
private void MakeTrace(int t)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
StringBuilder new_d = new StringBuilder(500);
|
||||||
|
StringBuilder new_r = new StringBuilder(500);
|
||||||
|
|
||||||
|
LibMSX.MSX_getdisassembly(MSX_Pntr, new_d, t);
|
||||||
|
LibMSX.MSX_getregisterstate(MSX_Pntr, new_r, t);
|
||||||
|
|
||||||
|
Tracer.Put(new TraceInfo
|
||||||
|
{
|
||||||
|
Disassembly = new_d.ToString().PadRight(36),
|
||||||
|
RegisterInfo = new_r.ToString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { "System Bus" });
|
private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { "System Bus" });
|
||||||
public IMemoryCallbackSystem MemoryCallbacks => _memorycallbacks;
|
public IMemoryCallbackSystem MemoryCallbacks => _memorycallbacks;
|
||||||
|
|
|
@ -21,6 +21,11 @@ namespace MSXHawk
|
||||||
cpu.mem_ctrl = &MemMap;
|
cpu.mem_ctrl = &MemMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VDP 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_ROM(uint8_t* ext_rom, uint32_t ext_rom_size, uint32_t ext_rom_mapper)
|
||||||
{
|
{
|
||||||
MemMap.Load_ROM(ext_rom, ext_rom_size, ext_rom_mapper);
|
MemMap.Load_ROM(ext_rom, ext_rom_size, ext_rom_mapper);
|
||||||
|
@ -78,10 +83,60 @@ namespace MSXHawk
|
||||||
return MemMap.lagged;
|
return MemMap.lagged;
|
||||||
}
|
}
|
||||||
|
|
||||||
VDP vdp;
|
void GetVideo(uint32_t* dest) {
|
||||||
Z80A cpu;
|
uint32_t* src = vdp.GameGearFrameBuffer;
|
||||||
SN76489sms psg;
|
uint32_t* dst = dest;
|
||||||
MemoryManager MemMap;
|
|
||||||
|
for (int i = 0; i < 144; i++)
|
||||||
|
{
|
||||||
|
std::memcpy(dst, src, sizeof uint32_t * 160);
|
||||||
|
src += 160;
|
||||||
|
dst += 160;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTraceCallback(void (*callback)(int))
|
||||||
|
{
|
||||||
|
cpu.TraceCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetHeader(char* h)
|
||||||
|
{
|
||||||
|
memcpy(h, cpu.TraceHeader, *(&cpu.TraceHeader + 1) - cpu.TraceHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetHeaderLength()
|
||||||
|
{
|
||||||
|
return *(&cpu.TraceHeader + 1) - cpu.TraceHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetRegisterState(char* r, int t)
|
||||||
|
{
|
||||||
|
if (t == 0)
|
||||||
|
{
|
||||||
|
memcpy(r, cpu.CPURegisterState().c_str(), cpu.CPURegisterState().length() + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(r, cpu.No_Reg, *(&cpu.No_Reg + 1) - cpu.No_Reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetDisassembly(char* d, int t)
|
||||||
|
{
|
||||||
|
if (t == 0)
|
||||||
|
{
|
||||||
|
memcpy(d, cpu.CPUDisassembly().c_str(), cpu.CPUDisassembly().length() + 1);
|
||||||
|
}
|
||||||
|
else if (t == 1)
|
||||||
|
{
|
||||||
|
memcpy(d, cpu.NMI_event, *(&cpu.NMI_event + 1) - cpu.NMI_event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(d, cpu.IRQ_event, *(&cpu.IRQ_event + 1) - cpu.IRQ_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,3 +35,34 @@ MSXHAWK_EXPORT void MSX_frame_advance(MSXCore* p, uint8_t ctrl1, uint8_t ctrl2,
|
||||||
p->FrameAdvance(ctrl1, ctrl2, render, 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)
|
||||||
|
{
|
||||||
|
p->GetVideo(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set tracer callback
|
||||||
|
MSXHAWK_EXPORT void MSX_settracecallback(MSXCore* p, void (*callback)(int)) {
|
||||||
|
p->SetTraceCallback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the cpu trace header
|
||||||
|
MSXHAWK_EXPORT void MSX_getheader(MSXCore* p, char* h) {
|
||||||
|
p->GetHeader(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the cpu trace header length
|
||||||
|
MSXHAWK_EXPORT int MSX_getheaderlength(MSXCore* p) {
|
||||||
|
return p->GetHeaderLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the cpu register state
|
||||||
|
MSXHAWK_EXPORT void MSX_getregisterstate(MSXCore* p, char* r, int t) {
|
||||||
|
p->GetRegisterState(r, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the cpu disassembly
|
||||||
|
MSXHAWK_EXPORT void MSX_getdisassembly(MSXCore* p, char* d, int t) {
|
||||||
|
p->GetDisassembly(d, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,8 +59,8 @@ namespace MSXHawk
|
||||||
|
|
||||||
int32_t IPeriod = 228;
|
int32_t IPeriod = 228;
|
||||||
int32_t FrameHeight = 192;
|
int32_t FrameHeight = 192;
|
||||||
int32_t FrameBuffer[256 * 244];
|
uint32_t FrameBuffer[256 * 244];
|
||||||
int32_t GameGearFrameBuffer[160 * 144];
|
uint32_t GameGearFrameBuffer[160 * 144];
|
||||||
int32_t OverscanFrameBuffer[1];
|
int32_t OverscanFrameBuffer[1];
|
||||||
int32_t ScanLine;
|
int32_t ScanLine;
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,8 @@ namespace MSXHawk
|
||||||
interruptMode = value;
|
interruptMode = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char replacer[32] = {};
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Constant Declarations
|
#pragma region Constant Declarations
|
||||||
|
@ -263,8 +265,8 @@ namespace MSXHawk
|
||||||
|
|
||||||
Z80A()
|
Z80A()
|
||||||
{
|
{
|
||||||
//Reset();
|
Reset();
|
||||||
//InitTableParity();
|
InitTableParity();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
|
@ -308,7 +310,9 @@ namespace MSXHawk
|
||||||
case OP_F:
|
case OP_F:
|
||||||
// Read the opcode of the next instruction
|
// Read the opcode of the next instruction
|
||||||
//if (OnExecFetch != null) OnExecFetch(RegPC);
|
//if (OnExecFetch != null) OnExecFetch(RegPC);
|
||||||
//if (TraceCallback != null) TraceCallback(State());
|
|
||||||
|
if (TraceCallback) { TraceCallback(0); }
|
||||||
|
|
||||||
bank_num = bank_offset = RegPCget();
|
bank_num = bank_offset = RegPCget();
|
||||||
bank_offset &= low_mask;
|
bank_offset &= low_mask;
|
||||||
bank_num = (bank_num >> bank_shift)& high_mask;
|
bank_num = (bank_num >> bank_shift)& high_mask;
|
||||||
|
@ -786,12 +790,9 @@ namespace MSXHawk
|
||||||
if (nonMaskableInterruptPending)
|
if (nonMaskableInterruptPending)
|
||||||
{
|
{
|
||||||
nonMaskableInterruptPending = false;
|
nonMaskableInterruptPending = false;
|
||||||
/*
|
|
||||||
if (TraceCallback != null)
|
if (TraceCallback) { TraceCallback(1); }
|
||||||
{
|
|
||||||
TraceCallback(new TraceInfo{ Disassembly = "====NMI====", RegisterInfo = "" });
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
IFF2 = IFF1;
|
IFF2 = IFF1;
|
||||||
IFF1 = false;
|
IFF1 = false;
|
||||||
NMI_();
|
NMI_();
|
||||||
|
@ -810,12 +811,9 @@ namespace MSXHawk
|
||||||
{
|
{
|
||||||
IFF1 = IFF2 = false;
|
IFF1 = IFF2 = false;
|
||||||
EI_pending = 0;
|
EI_pending = 0;
|
||||||
/*
|
|
||||||
if (TraceCallback != null)
|
if (TraceCallback) { TraceCallback(2); }
|
||||||
{
|
|
||||||
TraceCallback(new TraceInfo{ Disassembly = "====IRQ====", RegisterInfo = "" });
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
switch (interruptMode)
|
switch (interruptMode)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -864,64 +862,6 @@ namespace MSXHawk
|
||||||
TotalExecutedCycles++;
|
TotalExecutedCycles++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string TraceHeader = "Z80A: PC, machine code, mnemonic, operands, registers (AF, BC, DE, HL, IX, IY, SP, Cy), flags (CNP3H5ZS)";
|
|
||||||
|
|
||||||
string CPUDisassembly()
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
|
|
||||||
/*
|
|
||||||
uint32_t bytes_read = 0;
|
|
||||||
|
|
||||||
int* bytes_read_ptr = &bytes_read;
|
|
||||||
|
|
||||||
string disasm = disassemble ? Disassemble(RegPCget(), bytes_read_ptr) : "---";
|
|
||||||
string byte_code = "";
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < bytes_read; i++)
|
|
||||||
{
|
|
||||||
byte_code += $"{ReadMemory((uint32_t)(RegPC + i)):X2}";
|
|
||||||
if (i < (bytes_read - 1))
|
|
||||||
{
|
|
||||||
byte_code += " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TraceInfo
|
|
||||||
{
|
|
||||||
Disassembly = $"{RegPC:X4}: {byte_code.PadRight(12)} {disasm.PadRight(26)}",
|
|
||||||
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
string CPURegisterState()
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
|
|
||||||
/*
|
|
||||||
RegisterInfo = string.Join(" ",
|
|
||||||
$"AF:{(Regs[A] << 8) + Regs[F]:X4}",
|
|
||||||
$"BC:{(Regs[B] << 8) + Regs[C]:X4}",
|
|
||||||
$"DE:{(Regs[D] << 8) + Regs[E]:X4}",
|
|
||||||
$"HL:{(Regs[H] << 8) + Regs[L]:X4}",
|
|
||||||
$"IX:{(Regs[Ixh] << 8) + Regs[Ixl]:X4}",
|
|
||||||
$"IY:{(Regs[Iyh] << 8) + Regs[Iyl]:X4}",
|
|
||||||
$"SP:{Regs[SPl] | (Regs[SPh] << 8):X4}",
|
|
||||||
$"Cy:{TotalExecutedCycles}",
|
|
||||||
string.Concat(
|
|
||||||
FlagC ? "C" : "c",
|
|
||||||
FlagN ? "N" : "n",
|
|
||||||
FlagP ? "P" : "p",
|
|
||||||
Flag3 ? "3" : "-",
|
|
||||||
FlagH ? "H" : "h",
|
|
||||||
Flag5 ? "5" : "-",
|
|
||||||
FlagZ ? "Z" : "z",
|
|
||||||
FlagS ? "S" : "s",
|
|
||||||
FlagI ? "E" : "e"))
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optimization method to set BUSRQ
|
/// Optimization method to set BUSRQ
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -4509,25 +4449,302 @@ namespace MSXHawk
|
||||||
|
|
||||||
#pragma region Disassemble
|
#pragma region Disassemble
|
||||||
|
|
||||||
static string Result(string format, uint32_t addr)
|
const char* TraceHeader = "Z80A: PC, machine code, mnemonic, operands, registers (AF, BC, DE, HL, IX, IY, SP, Cy), flags (CNP3H5ZS)";
|
||||||
|
const char* NMI_event = "====NMI====";
|
||||||
|
const char* IRQ_event = "====IRQ====";
|
||||||
|
const char* No_Reg = "Q";
|
||||||
|
|
||||||
|
void (*TraceCallback)(int);
|
||||||
|
|
||||||
|
string CPURegisterState()
|
||||||
|
{
|
||||||
|
string reg_state = " ";
|
||||||
|
|
||||||
|
char* val_char = replacer;
|
||||||
|
|
||||||
|
int temp_reg = (Regs[A] << 8) + Regs[F];
|
||||||
|
sprintf_s(val_char, 5, "%04X", temp_reg);
|
||||||
|
reg_state.append(val_char, 4);
|
||||||
|
reg_state.append(" ");
|
||||||
|
|
||||||
|
temp_reg = (Regs[B] << 8) + Regs[C];
|
||||||
|
sprintf_s(val_char, 5, "%04X", temp_reg);
|
||||||
|
reg_state.append(val_char, 4);
|
||||||
|
reg_state.append(" ");
|
||||||
|
|
||||||
|
temp_reg = (Regs[D] << 8) + Regs[E];
|
||||||
|
sprintf_s(val_char, 5, "%04X", temp_reg);
|
||||||
|
reg_state.append(val_char, 4);
|
||||||
|
reg_state.append(" ");
|
||||||
|
|
||||||
|
temp_reg = (Regs[H] << 8) + Regs[L];
|
||||||
|
sprintf_s(val_char, 5, "%04X", temp_reg);
|
||||||
|
reg_state.append(val_char, 4);
|
||||||
|
reg_state.append(" ");
|
||||||
|
|
||||||
|
temp_reg = (Regs[Ixh] << 8) + Regs[Ixl];
|
||||||
|
sprintf_s(val_char, 5, "%04X", temp_reg);
|
||||||
|
reg_state.append(val_char, 4);
|
||||||
|
reg_state.append(" ");
|
||||||
|
|
||||||
|
temp_reg = (Regs[Iyh] << 8) + Regs[Iyl];
|
||||||
|
sprintf_s(val_char, 5, "%04X", temp_reg);
|
||||||
|
reg_state.append(val_char, 4);
|
||||||
|
reg_state.append(" ");
|
||||||
|
|
||||||
|
temp_reg = (Regs[SPh] << 8) + Regs[SPl];
|
||||||
|
sprintf_s(val_char, 5, "%04X", temp_reg);
|
||||||
|
reg_state.append(val_char, 4);
|
||||||
|
reg_state.append(" ");
|
||||||
|
|
||||||
|
int tot_long = sprintf_s(val_char, 32, "%16u", TotalExecutedCycles);
|
||||||
|
reg_state.append(val_char, tot_long);
|
||||||
|
reg_state.append(" ");
|
||||||
|
|
||||||
|
reg_state.append(FlagCget() ? "C" : "c");
|
||||||
|
reg_state.append(FlagNget() ? "N" : "n");
|
||||||
|
reg_state.append(FlagPget() ? "P" : "p");
|
||||||
|
reg_state.append(Flag3get() ? "3" : "-");
|
||||||
|
reg_state.append(FlagHget() ? "H" : "h");
|
||||||
|
reg_state.append(Flag5get() ? "5" : "-");
|
||||||
|
reg_state.append(FlagZget() ? "Z" : "z");
|
||||||
|
reg_state.append(FlagSget() ? "S" : "s");
|
||||||
|
reg_state.append(FlagI ? "E" : "e");
|
||||||
|
|
||||||
|
return reg_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
string CPUDisassembly()
|
||||||
|
{
|
||||||
|
uint32_t bytes_read = 0;
|
||||||
|
|
||||||
|
uint32_t* bytes_read_ptr = &bytes_read;
|
||||||
|
|
||||||
|
string disasm = Disassemble(RegPCget(), bytes_read_ptr);
|
||||||
|
string byte_code = "";
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < bytes_read; i++)
|
||||||
|
{
|
||||||
|
bank_num = bank_offset = (RegPCget() + i) & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift)& high_mask;
|
||||||
|
|
||||||
|
char* val_char_1 = replacer;
|
||||||
|
sprintf_s(val_char_1, 5, "%02X", MemoryMap[bank_num][bank_offset]);
|
||||||
|
string val1(val_char_1, 2);
|
||||||
|
|
||||||
|
byte_code.append(val1);
|
||||||
|
if (i < (bytes_read - 1))
|
||||||
|
{
|
||||||
|
byte_code.append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte_code.append(disasm);
|
||||||
|
|
||||||
|
while (byte_code.length() < 32)
|
||||||
|
{
|
||||||
|
byte_code.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return byte_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Result(string format, uint32_t addr)
|
||||||
{
|
{
|
||||||
//d immediately succeeds the opcode
|
//d immediately succeeds the opcode
|
||||||
//n immediate succeeds the opcode and the displacement (if present)
|
//n immediate succeeds the opcode and the displacement (if present)
|
||||||
//nn immediately succeeds the opcode and the displacement (if present)
|
//nn immediately succeeds the opcode and the displacement (if present)
|
||||||
/*
|
|
||||||
if (format.IndexOf("nn") != -1) format = format.Replace("nn", $"{read(addr++) + (read(addr++) << 8):X4}h"); // LSB is read first
|
|
||||||
if (format.IndexOf("n") != -1) format = format.Replace("n", $"{read(addr++):X2}h");
|
|
||||||
|
|
||||||
if (format.IndexOf("+d") != -1) format = format.Replace("+d", "d");
|
if (format.find("nn") != string::npos)
|
||||||
if (format.IndexOf("d") != -1)
|
|
||||||
{
|
{
|
||||||
var b = unchecked((sbyte)read(addr++));
|
size_t str_loc = format.find("nn");
|
||||||
format = format.Replace("d", $"{(b < 0 ? '-' : '+')}{(b < 0 ? -b : b):X2}h");
|
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift) & high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
char* val_char_1 = replacer;
|
||||||
|
sprintf_s(val_char_1, 5, "%02X", MemoryMap[bank_num][bank_offset]);
|
||||||
|
string val1(val_char_1, 2);
|
||||||
|
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift)& high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
char* val_char_2 = replacer;
|
||||||
|
sprintf_s(val_char_2, 5, "%02X", MemoryMap[bank_num][bank_offset]);
|
||||||
|
string val2(val_char_2, 2);
|
||||||
|
|
||||||
|
format.erase(str_loc, 2);
|
||||||
|
format.insert(str_loc, val1);
|
||||||
|
format.insert(str_loc, val2);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
if (format.find("n") != string::npos)
|
||||||
|
{
|
||||||
|
size_t str_loc = format.find("n");
|
||||||
|
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift)& high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
char* val_char_1 = replacer;
|
||||||
|
sprintf_s(val_char_1, 5, "%02X", MemoryMap[bank_num][bank_offset]);
|
||||||
|
string val1(val_char_1, 2);
|
||||||
|
|
||||||
|
format.erase(str_loc, 1);
|
||||||
|
format.insert(str_loc, val1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format.find("+d") != string::npos)
|
||||||
|
{
|
||||||
|
size_t str_loc = format.find("+d");
|
||||||
|
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift)& high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
char* val_char_1 = replacer;
|
||||||
|
sprintf_s(val_char_1, 5, "%u", MemoryMap[bank_num][bank_offset]);
|
||||||
|
string val1(val_char_1, 2);
|
||||||
|
|
||||||
|
format.erase(str_loc + 1, 1);
|
||||||
|
format.insert(str_loc + 1, val1);
|
||||||
|
}
|
||||||
|
if (format.find("d") != string::npos)
|
||||||
|
{
|
||||||
|
size_t str_loc = format.find("d");
|
||||||
|
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift)& high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
int8_t temp = (int8_t)MemoryMap[bank_num][bank_offset];
|
||||||
|
|
||||||
|
char* val_char_1 = replacer;
|
||||||
|
sprintf_s(val_char_1, 5, "%d", temp);
|
||||||
|
string val1(val_char_1, 3);
|
||||||
|
|
||||||
|
format.erase(str_loc, 1);
|
||||||
|
format.insert(str_loc, val1);
|
||||||
|
}
|
||||||
|
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string Disassemble(uint32_t addr, uint32_t* size)
|
||||||
|
{
|
||||||
|
uint32_t start_addr = addr;
|
||||||
|
uint32_t extra_inc = 0;
|
||||||
|
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift) & high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
uint32_t A = MemoryMap[bank_num][bank_offset];
|
||||||
|
string format;
|
||||||
|
switch (A)
|
||||||
|
{
|
||||||
|
case 0xCB:
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift) & high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
A = MemoryMap[bank_num][bank_offset];
|
||||||
|
format = mnemonicsCB[A];
|
||||||
|
break;
|
||||||
|
case 0xDD:
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift) & high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
A = MemoryMap[bank_num][bank_offset];
|
||||||
|
switch (A)
|
||||||
|
{
|
||||||
|
case 0xCB:
|
||||||
|
bank_num = bank_offset = (addr + 1) & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift) & high_mask;
|
||||||
|
|
||||||
|
format = mnemonicsDDCB[MemoryMap[bank_num][bank_offset]];
|
||||||
|
extra_inc = 1;
|
||||||
|
break;
|
||||||
|
case 0xED:
|
||||||
|
format = mnemonicsED[A];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
format = mnemonicsDD[A];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xED:
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift) & high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
A = MemoryMap[bank_num][bank_offset];
|
||||||
|
format = mnemonicsED[A];
|
||||||
|
break;
|
||||||
|
case 0xFD:
|
||||||
|
bank_num = bank_offset = addr & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift) & high_mask;
|
||||||
|
addr++;
|
||||||
|
|
||||||
|
A = MemoryMap[bank_num][bank_offset];
|
||||||
|
switch (A)
|
||||||
|
{
|
||||||
|
case 0xCB:
|
||||||
|
bank_num = bank_offset = (addr + 1) & 0xFFFF;
|
||||||
|
bank_offset &= low_mask;
|
||||||
|
bank_num = (bank_num >> bank_shift)& high_mask;
|
||||||
|
|
||||||
|
format = mnemonicsFDCB[MemoryMap[bank_num][bank_offset]];
|
||||||
|
extra_inc = 1;
|
||||||
|
break;
|
||||||
|
case 0xED:
|
||||||
|
format = mnemonicsED[A];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
format = mnemonicsFD[A];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: format = mnemonics[A]; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string temp = Result(format, addr);
|
||||||
|
|
||||||
|
addr += extra_inc;
|
||||||
|
|
||||||
|
size[0] = addr - start_addr;
|
||||||
|
// handle case of addr wrapping around at 16 bit boundary
|
||||||
|
if (addr < start_addr)
|
||||||
|
{
|
||||||
|
size[0] = (0x10000 + addr) - start_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
string Disassemble(MemoryDomain m, uuint32_t addr, out uint32_t length)
|
||||||
|
{
|
||||||
|
string ret = Disassemble((uint32_t)addr, a = > m.PeekByte(a), out length);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
const string mnemonics[256] =
|
const string mnemonics[256] =
|
||||||
{
|
{
|
||||||
"NOP", "LD BC, nn", "LD (BC), A", "INC BC", //0x04
|
"NOP", "LD BC, nn", "LD (BC), A", "INC BC", //0x04
|
||||||
|
@ -4881,112 +5098,6 @@ namespace MSXHawk
|
||||||
"NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", //0xF0
|
"NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", //0xF0
|
||||||
"NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", //0x100
|
"NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", //0x100
|
||||||
};
|
};
|
||||||
|
|
||||||
string Disassemble(uint32_t addr, int* size)
|
|
||||||
{
|
|
||||||
uint32_t start_addr = addr;
|
|
||||||
uint32_t extra_inc = 0;
|
|
||||||
|
|
||||||
bank_num = bank_offset = addr & 0xFFFF;
|
|
||||||
bank_offset &= low_mask;
|
|
||||||
bank_num = (bank_num >> bank_shift)& high_mask;
|
|
||||||
addr++;
|
|
||||||
|
|
||||||
uint32_t A = MemoryMap[bank_num][bank_offset];
|
|
||||||
string format;
|
|
||||||
switch (A)
|
|
||||||
{
|
|
||||||
case 0xCB:
|
|
||||||
bank_num = bank_offset = addr & 0xFFFF;
|
|
||||||
bank_offset &= low_mask;
|
|
||||||
bank_num = (bank_num >> bank_shift)& high_mask;
|
|
||||||
addr++;
|
|
||||||
|
|
||||||
A = MemoryMap[bank_num][bank_offset];
|
|
||||||
format = mnemonicsCB[A];
|
|
||||||
break;
|
|
||||||
case 0xDD:
|
|
||||||
bank_num = bank_offset = addr & 0xFFFF;
|
|
||||||
bank_offset &= low_mask;
|
|
||||||
bank_num = (bank_num >> bank_shift)& high_mask;
|
|
||||||
addr++;
|
|
||||||
|
|
||||||
A = MemoryMap[bank_num][bank_offset];
|
|
||||||
switch (A)
|
|
||||||
{
|
|
||||||
case 0xCB:
|
|
||||||
bank_num = bank_offset = (addr + 1) & 0xFFFF;
|
|
||||||
bank_offset &= low_mask;
|
|
||||||
bank_num = (bank_num >> bank_shift)& high_mask;
|
|
||||||
|
|
||||||
format = mnemonicsDDCB[MemoryMap[bank_num][bank_offset]];
|
|
||||||
extra_inc = 1;
|
|
||||||
break;
|
|
||||||
case 0xED:
|
|
||||||
format = mnemonicsED[A];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
format = mnemonicsDD[A];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0xED:
|
|
||||||
bank_num = bank_offset = addr & 0xFFFF;
|
|
||||||
bank_offset &= low_mask;
|
|
||||||
bank_num = (bank_num >> bank_shift)& high_mask;
|
|
||||||
addr++;
|
|
||||||
|
|
||||||
A = MemoryMap[bank_num][bank_offset];
|
|
||||||
format = mnemonicsED[A];
|
|
||||||
break;
|
|
||||||
case 0xFD:
|
|
||||||
bank_num = bank_offset = addr & 0xFFFF;
|
|
||||||
bank_offset &= low_mask;
|
|
||||||
bank_num = (bank_num >> bank_shift)& high_mask;
|
|
||||||
addr++;
|
|
||||||
|
|
||||||
A = MemoryMap[bank_num][bank_offset];
|
|
||||||
switch (A)
|
|
||||||
{
|
|
||||||
case 0xCB:
|
|
||||||
bank_num = bank_offset = (addr + 1) & 0xFFFF;
|
|
||||||
bank_offset &= low_mask;
|
|
||||||
bank_num = (bank_num >> bank_shift)& high_mask;
|
|
||||||
|
|
||||||
format = mnemonicsFDCB[MemoryMap[bank_num][bank_offset]];
|
|
||||||
extra_inc = 1;
|
|
||||||
break;
|
|
||||||
case 0xED:
|
|
||||||
format = mnemonicsED[A];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
format = mnemonicsFD[A];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: format = mnemonics[A]; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
string temp = Result(format, addr);
|
|
||||||
|
|
||||||
addr += extra_inc;
|
|
||||||
|
|
||||||
size[0] = addr - start_addr;
|
|
||||||
// handle case of addr wrapping around at 16 bit boundary
|
|
||||||
if (addr < start_addr)
|
|
||||||
{
|
|
||||||
size[0] = (0x10000 + addr) - start_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
string Disassemble(MemoryDomain m, uuint32_t addr, out uint32_t length)
|
|
||||||
{
|
|
||||||
string ret = Disassemble((uint32_t)addr, a = > m.PeekByte(a), out length);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue