Debugger: GB - Added assembler tool
This commit is contained in:
parent
7852d6f75c
commit
9c4fb9c45c
22 changed files with 608 additions and 57 deletions
|
@ -415,6 +415,10 @@ Assembler::Assembler(shared_ptr<LabelManager> labelManager)
|
|||
_labelManager = labelManager;
|
||||
}
|
||||
|
||||
Assembler::~Assembler()
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t Assembler::AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode)
|
||||
{
|
||||
for(uint8_t i = 0; i < 255; i++) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <unordered_set>
|
||||
#include <regex>
|
||||
#include "IAssembler.h"
|
||||
#include "CpuDisUtils.h"
|
||||
|
||||
class LabelManager;
|
||||
|
@ -21,24 +21,7 @@ struct LineData
|
|||
bool HasOpeningBracket = false;
|
||||
};
|
||||
|
||||
enum AssemblerSpecialCodes
|
||||
{
|
||||
OK = 0,
|
||||
EndOfLine = -1,
|
||||
ParsingError = -2,
|
||||
OutOfRangeJump = -3,
|
||||
LabelRedefinition = -4,
|
||||
MissingOperand = -5,
|
||||
OperandOutOfRange = -6,
|
||||
InvalidHex = -7,
|
||||
InvalidSpaces = -8,
|
||||
TrailingText = -9,
|
||||
UnknownLabel = -10,
|
||||
InvalidInstruction = -11,
|
||||
InvalidBinaryValue = -12,
|
||||
};
|
||||
|
||||
class Assembler
|
||||
class Assembler : public IAssembler
|
||||
{
|
||||
private:
|
||||
std::unordered_map<string, std::unordered_set<int>> _availableModesByOpName;
|
||||
|
@ -54,6 +37,7 @@ private:
|
|||
|
||||
public:
|
||||
Assembler(shared_ptr<LabelManager> labelManager);
|
||||
virtual ~Assembler();
|
||||
|
||||
uint32_t AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode);
|
||||
};
|
|
@ -69,6 +69,7 @@
|
|||
<ClInclude Include="GameboyDisUtils.h" />
|
||||
<ClInclude Include="GameboyHeader.h" />
|
||||
<ClInclude Include="GbApu.h" />
|
||||
<ClInclude Include="GbAssembler.h" />
|
||||
<ClInclude Include="GbCart.h" />
|
||||
<ClInclude Include="GbCartFactory.h" />
|
||||
<ClInclude Include="GbCpu.h" />
|
||||
|
@ -86,6 +87,7 @@
|
|||
<ClInclude Include="GbTimer.h" />
|
||||
<ClInclude Include="GbTypes.h" />
|
||||
<ClInclude Include="GbWaveChannel.h" />
|
||||
<ClInclude Include="IAssembler.h" />
|
||||
<ClInclude Include="NecDspDebugger.h" />
|
||||
<ClInclude Include="ForceDisconnectMessage.h" />
|
||||
<ClInclude Include="GameClient.h" />
|
||||
|
@ -279,6 +281,7 @@
|
|||
<ClCompile Include="Gameboy.cpp" />
|
||||
<ClCompile Include="GameboyDisUtils.cpp" />
|
||||
<ClCompile Include="GbApu.cpp" />
|
||||
<ClCompile Include="GbAssembler.cpp" />
|
||||
<ClCompile Include="GbCpu.cpp" />
|
||||
<ClCompile Include="GbDebugger.cpp" />
|
||||
<ClCompile Include="GbDmaController.cpp" />
|
||||
|
|
|
@ -488,9 +488,6 @@
|
|||
<ClInclude Include="Profiler.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Assembler.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Spc7110.h">
|
||||
<Filter>SNES\Coprocessors\SPC7110</Filter>
|
||||
</ClInclude>
|
||||
|
@ -590,6 +587,15 @@
|
|||
<ClInclude Include="GbDmaController.h">
|
||||
<Filter>GB</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Assembler.h">
|
||||
<Filter>Debugger\Assembler</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GbAssembler.h">
|
||||
<Filter>Debugger\Assembler</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IAssembler.h">
|
||||
<Filter>Debugger\Assembler</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp" />
|
||||
|
@ -875,9 +881,6 @@
|
|||
<ClCompile Include="Profiler.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Assembler.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Spc7110.cpp">
|
||||
<Filter>SNES\Coprocessors\SPC7110</Filter>
|
||||
</ClCompile>
|
||||
|
@ -938,6 +941,12 @@
|
|||
<ClCompile Include="GbDmaController.cpp">
|
||||
<Filter>GB</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Assembler.cpp">
|
||||
<Filter>Debugger\Assembler</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GbAssembler.cpp">
|
||||
<Filter>Debugger\Assembler</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="SNES">
|
||||
|
@ -1027,5 +1036,8 @@
|
|||
<Filter Include="Debugger\EventManager">
|
||||
<UniqueIdentifier>{b1753ff0-0c73-4acf-978b-1964222e01c6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Debugger\Assembler">
|
||||
<UniqueIdentifier>{8abb14c6-e2ee-48cb-ad91-38402cb8510c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -20,6 +20,7 @@
|
|||
#include "Console.h"
|
||||
#include "MemoryAccessCounter.h"
|
||||
#include "ExpressionEvaluator.h"
|
||||
#include "Assembler.h"
|
||||
|
||||
CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
|
||||
{
|
||||
|
@ -39,6 +40,7 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
|
|||
_callstackManager.reset(new CallstackManager(debugger));
|
||||
_breakpointManager.reset(new BreakpointManager(debugger, cpuType, _eventManager.get()));
|
||||
_step.reset(new StepRequest());
|
||||
_assembler.reset(new Assembler(_debugger->GetLabelManager()));
|
||||
|
||||
if(GetState().PC == 0) {
|
||||
//Enable breaking on uninit reads when debugger is opened at power on
|
||||
|
@ -240,6 +242,11 @@ shared_ptr<EventManager> CpuDebugger::GetEventManager()
|
|||
return _eventManager;
|
||||
}
|
||||
|
||||
shared_ptr<Assembler> CpuDebugger::GetAssembler()
|
||||
{
|
||||
return _assembler;
|
||||
}
|
||||
|
||||
shared_ptr<CallstackManager> CpuDebugger::GetCallstackManager()
|
||||
{
|
||||
return _callstackManager;
|
||||
|
|
|
@ -17,6 +17,7 @@ class EventManager;
|
|||
class MemoryMappings;
|
||||
class BreakpointManager;
|
||||
class Sa1;
|
||||
class Assembler;
|
||||
|
||||
class CpuDebugger final : public IDebugger
|
||||
{
|
||||
|
@ -31,6 +32,7 @@ class CpuDebugger final : public IDebugger
|
|||
Sa1* _sa1;
|
||||
|
||||
shared_ptr<EventManager> _eventManager;
|
||||
shared_ptr<Assembler> _assembler;
|
||||
shared_ptr<CallstackManager> _callstackManager;
|
||||
unique_ptr<BreakpointManager> _breakpointManager;
|
||||
unique_ptr<StepRequest> _step;
|
||||
|
@ -55,6 +57,7 @@ public:
|
|||
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);
|
||||
|
||||
shared_ptr<EventManager> GetEventManager();
|
||||
shared_ptr<Assembler> GetAssembler();
|
||||
shared_ptr<CallstackManager> GetCallstackManager();
|
||||
BreakpointManager* GetBreakpointManager();
|
||||
};
|
|
@ -42,6 +42,7 @@
|
|||
#include "InternalRegisters.h"
|
||||
#include "AluMulDiv.h"
|
||||
#include "Assembler.h"
|
||||
#include "GbAssembler.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "../Utilities/IpsPatcher.h"
|
||||
|
@ -74,7 +75,6 @@ Debugger::Debugger(shared_ptr<Console> console)
|
|||
_memoryAccessCounter.reset(new MemoryAccessCounter(this, console.get()));
|
||||
_ppuTools.reset(new PpuTools(_console.get(), _ppu.get()));
|
||||
_scriptManager.reset(new ScriptManager(this));
|
||||
_assembler.reset(new Assembler(_labelManager));
|
||||
|
||||
if(_cart->GetGameboy()) {
|
||||
_gbDebugger.reset(new GbDebugger(this));
|
||||
|
@ -715,9 +715,13 @@ shared_ptr<Console> Debugger::GetConsole()
|
|||
return _console;
|
||||
}
|
||||
|
||||
shared_ptr<Assembler> Debugger::GetAssembler()
|
||||
shared_ptr<IAssembler> Debugger::GetAssembler(CpuType cpuType)
|
||||
{
|
||||
return _assembler;
|
||||
if(cpuType == CpuType::Gameboy) {
|
||||
return std::dynamic_pointer_cast<IAssembler>(_gbDebugger->GetAssembler());
|
||||
} else {
|
||||
return std::dynamic_pointer_cast<IAssembler>(_cpuDebugger->GetAssembler());
|
||||
}
|
||||
}
|
||||
|
||||
template void Debugger::ProcessMemoryRead<CpuType::Cpu>(uint32_t addr, uint8_t value, MemoryOperationType opType);
|
||||
|
|
|
@ -34,8 +34,8 @@ class NecDspDebugger;
|
|||
class Cx4Debugger;
|
||||
class GbDebugger;
|
||||
class Breakpoint;
|
||||
class Assembler;
|
||||
class IEventManager;
|
||||
class IAssembler;
|
||||
|
||||
enum class EventType;
|
||||
enum class EvalResultType : int32_t;
|
||||
|
@ -69,7 +69,6 @@ private:
|
|||
shared_ptr<Disassembler> _disassembler;
|
||||
shared_ptr<PpuTools> _ppuTools;
|
||||
shared_ptr<LabelManager> _labelManager;
|
||||
shared_ptr<Assembler> _assembler;
|
||||
|
||||
unique_ptr<ExpressionEvaluator> _watchExpEval[(int)DebugUtilities::GetLastCpuType() + 1];
|
||||
|
||||
|
@ -144,5 +143,5 @@ public:
|
|||
shared_ptr<ScriptManager> GetScriptManager();
|
||||
shared_ptr<CallstackManager> GetCallstackManager(CpuType cpuType);
|
||||
shared_ptr<Console> GetConsole();
|
||||
shared_ptr<Assembler> GetAssembler();
|
||||
shared_ptr<IAssembler> GetAssembler(CpuType cpuType);
|
||||
};
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
#include"../Utilities/HexUtilities.h"
|
||||
|
||||
constexpr const char* _opTemplate[256] = {
|
||||
"NOP", "LD BC, e", "LD (BC), A", "INC BC", "INC B", "DEC B", "LD B, d", "RLCA", "LD (b), SP", "ADD HL, BC", "LD A, (BC)", "DEC BC", "INC C", "DEC C", "LD C, d", "RRCA",
|
||||
"NOP", "LD BC, e", "LD (BC), A", "INC BC", "INC B", "DEC B", "LD B, d", "RLCA", "LD (a), SP", "ADD HL, BC", "LD A, (BC)", "DEC BC", "INC C", "DEC C", "LD C, d", "RRCA",
|
||||
"STOP", "LD DE, e", "LD (DE), A", "INC DE", "INC D", "DEC D", "LD D, d", "RLA", "JR r", "ADD HL, DE", "LD A, (DE)", "DEC DE", "INC E", "DEC E", "LD E, d", "RRA",
|
||||
"JR NZ, r", "LD HL, e", "LD (HL+), A", "INC HL", "INC H", "DEC H", "LD H, d", "DAA", "JR Z, r", "ADD HL, HL", "LD A, (HL+)", "DEC HL", "INC L", "DEC L", "LD L, d", "CPL",
|
||||
"JR NC, r", "LD SP, e", "LD (HL-), A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL), d", "SCF", "JR C, r", "ADD HL, SP", "LD A, (HL-)", "DEC SP", "INC A", "DEC A", "LD A, d", "CCF",
|
||||
"LD B, B", "LD B, C", "LD B, D", "LD B, E", "LD B, H", "LD B, L", "LD B, (HL)", "LD B, A", "LD C, B", "LD C, C", "LD C, D", "LD C, E", "LD C, H", "LD C, L", "LD C, (HL)", "LD C, A",
|
||||
"LD D, B", "LD D, C", "LD D, D", "LD D, E", "LD D, H", "LD D, L", "LD D, (HL)", "LD D, A", "LD E, B", "LD E, C", "LD E, D", "LD E, E", "LD E, H", "LD E, L", "LD E, (HL)", "LD E, A",
|
||||
"LD H, B", "LD H, C", "LD H, D", "LD H, E", "LD H, H", "LD H, L", "LD H, (HL)", "LD H, A", "LD L, B", "LD L, C", "LD L, D", "LD L, E", "LD L, H", "LD L, L", "LD L, (HL)", "LD L, A",
|
||||
"LD (HL), B", "LD (HL), C", "LD (HL), D", "LD (HL), E","LD (HL), H", "LD (HL), L", "HALT", "LD (HL), A", "LD A, B", "LD A, C", "LD A, D", "LD A, E", "LD A, H", "LD A, L", "LD A, (HL)", "LD A, A",
|
||||
"LD (HL), B", "LD (HL), C", "LD (HL), D", "LD (HL), E","LD (HL), H", "LD (HL), L", "HALT", "LD (HL), A","LD A, B", "LD A, C", "LD A, D", "LD A, E", "LD A, H", "LD A, L", "LD A, (HL)", "LD A, A",
|
||||
"ADD A, B", "ADD A, C", "ADD A, D", "ADD A, E", "ADD A, H", "ADD A, L", "ADD A, (HL)", "ADD A, A", "ADC A, B", "ADC A, C", "ADC A, D", "ADC A, E", "ADC A, H", "ADC A, L", "ADC A, (HL)", "ADC A, A",
|
||||
"SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", "SBC A, B", "SBC A, C", "SBC A, D", "SBC A, E", "SBC A, H", "SBC A, L", "SBC A, (HL)", "SBC A, A",
|
||||
"AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", "XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A",
|
||||
|
@ -23,7 +23,7 @@ constexpr const char* _opTemplate[256] = {
|
|||
"RET NZ", "POP BC", "JP NZ, a", "JP a", "CALL NZ, a", "PUSH BC", "ADD A, d", "RST 00H", "RET Z", "RET", "JP Z, a", "PREFIX", "CALL Z, a","CALL a", "ADC A, d", "RST 08H",
|
||||
"RET NC", "POP DE", "JP NC, a", "ILL_D3", "CALL NC, a", "PUSH DE", "SUB d", "RST 10H", "RET C", "RETI", "JP C, a", "ILL_DB", "CALL C, a","ILL_DD", "SBC A, d", "RST 18H",
|
||||
"LDH (c), A", "POP HL", "LD ($FF00+C), A","ILL_E3","ILL_E4", "PUSH HL", "AND d", "RST 20H", "ADD SP, d", "JP HL", "LD (a), A", "ILL_EB", "ILL_EC", "ILL_ED", "XOR d", "RST 28H",
|
||||
"LDH A, (c)", "POP AF", "LD A, ($FF00+C)","DI", "ILL_F4", "PUSH AF", "OR d", "RST 30H", "LD HL, SP+d", "LD SP, HL", "LD A, (a)", "EI", "ILL_FC", "ILL_FD", "CP d", "RST 38H"
|
||||
"LDH A, (c)", "POP AF", "LD A, ($FF00+C)","DI", "ILL_F4", "PUSH AF", "OR d", "RST 30H", "LD HL, SP+d", "LD SP, HL", "LD A, (a)", "EI", "ILL_FC", "ILL_FD", "CP d", "RST 38H"
|
||||
};
|
||||
|
||||
constexpr const char* _cbTemplate[256] = {
|
||||
|
@ -93,8 +93,7 @@ void GameboyDisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_
|
|||
|
||||
//Jump addresses, memory addresses
|
||||
case 'a': getOperand((uint16_t)(byteCode[1] | (byteCode[2] << 8))); break;
|
||||
case 'b': str.WriteAll('$', HexUtilities::ToHex(byteCode[1])); break;
|
||||
case 'c': str.WriteAll('$', HexUtilities::ToHex((uint16_t)(0xFF00 | byteCode[1]))); break;
|
||||
case 'c': getOperand((uint16_t)(0xFF00 | byteCode[1])); break;
|
||||
|
||||
//Immediate values
|
||||
case 'd': str.WriteAll("$", HexUtilities::ToHex(byteCode[1])); break;
|
||||
|
@ -113,7 +112,6 @@ int32_t GameboyDisUtils::GetEffectiveAddress(DisassemblyInfo& info, Console* con
|
|||
return -1;
|
||||
}
|
||||
|
||||
|
||||
uint8_t GameboyDisUtils::GetOpSize(uint8_t opCode)
|
||||
{
|
||||
return _opSize[opCode];
|
||||
|
@ -134,4 +132,9 @@ bool GameboyDisUtils::IsReturnInstruction(uint8_t opCode)
|
|||
opCode == 0xC0 || opCode == 0xC8 || opCode == 0xD0 || opCode == 0xD8 || //Conditional RET
|
||||
opCode == 0xC9 || opCode == 0xD9 //Unconditional RET/RETI
|
||||
);
|
||||
}
|
||||
|
||||
string GameboyDisUtils::GetOpTemplate(uint8_t op, bool prefixed)
|
||||
{
|
||||
return prefixed ? _cbTemplate[op] : _opTemplate[op];
|
||||
}
|
|
@ -15,4 +15,5 @@ public:
|
|||
static uint8_t GetOpSize(uint8_t opCode);
|
||||
static bool IsJumpToSub(uint8_t opCode);
|
||||
static bool IsReturnInstruction(uint8_t opCode);
|
||||
static string GetOpTemplate(uint8_t op, bool prefixed);
|
||||
};
|
||||
|
|
410
Core/GbAssembler.cpp
Normal file
410
Core/GbAssembler.cpp
Normal file
|
@ -0,0 +1,410 @@
|
|||
#include "stdafx.h"
|
||||
#include <regex>
|
||||
#include "GbAssembler.h"
|
||||
#include "LabelManager.h"
|
||||
#include "GameboyDisUtils.h"
|
||||
#include "../Utilities/StringUtilities.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
|
||||
static const std::regex labelRegex = std::regex("^\\s*([@_a-zA-Z][@_a-zA-Z0-9]*)", std::regex_constants::icase);
|
||||
|
||||
GbAssembler::GbAssembler(shared_ptr<LabelManager> labelManager)
|
||||
{
|
||||
_labelManager = labelManager;
|
||||
InitAssembler();
|
||||
}
|
||||
|
||||
void GbAssembler::InitAssembler()
|
||||
{
|
||||
for(int i = 0; i < 512; i++) {
|
||||
string op = GameboyDisUtils::GetOpTemplate(i & 0xFF, i >= 256);
|
||||
size_t spaceIndex = op.find(' ');
|
||||
size_t commaIndex = op.find(',');
|
||||
string opName;
|
||||
|
||||
OpCodeEntry entry = {};
|
||||
if(spaceIndex != string::npos) {
|
||||
opName = op.substr(0, spaceIndex);
|
||||
entry.ParamCount = commaIndex != string::npos ? 2 : 1;
|
||||
} else {
|
||||
opName = op;
|
||||
entry.ParamCount = 0;
|
||||
}
|
||||
|
||||
entry.OpCode = i < 256 ? i : ((i << 8) | 0xCB);
|
||||
|
||||
std::transform(opName.begin(), opName.end(), opName.begin(), ::tolower);
|
||||
if(_opCodes.find(opName) == _opCodes.end()) {
|
||||
_opCodes[opName] = vector<OpCodeEntry>();
|
||||
}
|
||||
|
||||
if(entry.ParamCount > 0) {
|
||||
string operands = op.substr(spaceIndex + 1);
|
||||
operands.erase(std::remove_if(operands.begin(), operands.end(), isspace), operands.end());
|
||||
if(entry.ParamCount == 2) {
|
||||
vector<string> operandList = StringUtilities::Split(operands, ',');
|
||||
InitParamEntry(entry.Param1, operandList[0]);
|
||||
InitParamEntry(entry.Param2, operandList[1]);
|
||||
} else if(entry.ParamCount == 1) {
|
||||
InitParamEntry(entry.Param1, operands);
|
||||
}
|
||||
}
|
||||
_opCodes[opName].push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void GbAssembler::InitParamEntry(ParamEntry& entry, string param)
|
||||
{
|
||||
if(param == "a") {
|
||||
entry.Type = ParamType::Short;
|
||||
} else if(param == "d") {
|
||||
entry.Type = ParamType::Byte;
|
||||
} else if(param == "e") {
|
||||
entry.Type = ParamType::Short;
|
||||
} else if(param == "r") {
|
||||
entry.Type = ParamType::RelAddress;
|
||||
} else if(param == "(a)") {
|
||||
entry.Type = ParamType::Address;
|
||||
} else if(param == "(c)") {
|
||||
entry.Type = ParamType::HighAddress;
|
||||
} else if(param == "SP+d") {
|
||||
entry.Type = ParamType::StackOffset;
|
||||
} else {
|
||||
std::transform(param.begin(), param.end(), param.begin(), ::tolower);
|
||||
entry.Type = ParamType::Literal;
|
||||
entry.Param = param;
|
||||
}
|
||||
entry.Param = param;
|
||||
}
|
||||
|
||||
bool GbAssembler::IsRegisterName(string op)
|
||||
{
|
||||
return op == "hl" || op == "af" || op == "bc" || op == "de" || op == "a" || op == "b" || op == "c" || op == "d" || op == "e" || op == "f" || op == "l" || op == "h";
|
||||
}
|
||||
|
||||
int GbAssembler::ReadValue(string operand, int min, int max, unordered_map<string, uint16_t>& localLabels, bool firstPass)
|
||||
{
|
||||
int value = 0;
|
||||
switch(operand[0]) {
|
||||
//Hex
|
||||
case '$': value = HexUtilities::FromHex(operand.substr(1)); break;
|
||||
|
||||
case '%':
|
||||
//Binary
|
||||
for(size_t i = 1; i < operand.size(); i++) {
|
||||
value <<= 1;
|
||||
value |= operand[i] == '1' ? 1 : 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if(std::regex_match(operand, labelRegex)) {
|
||||
if(firstPass) {
|
||||
return 0;
|
||||
} else if(localLabels.find(operand) != localLabels.end()) {
|
||||
value = localLabels.find(operand)->second;
|
||||
} else {
|
||||
int labelAddress = _labelManager->GetLabelRelativeAddress(operand, CpuType::Gameboy);
|
||||
if(labelAddress >= 0) {
|
||||
//Matching label found
|
||||
value = labelAddress;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Decimal
|
||||
for(int i = 0; i < operand.size(); i++) {
|
||||
if(operand[i] != '-' && (operand[i] < '0' || operand[i] > '9')) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
value = std::stoi(operand);
|
||||
if(value < 0) {
|
||||
value = max + value + 1;
|
||||
}
|
||||
} catch(std::exception&) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(value < min || value > max) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool GbAssembler::IsMatch(ParamEntry& entry, string operand, uint32_t address, unordered_map<string, uint16_t>& localLabels, bool firstPass)
|
||||
{
|
||||
if(entry.Type != ParamType::Literal && IsRegisterName(operand)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(entry.Type) {
|
||||
case ParamType::None: return false;
|
||||
|
||||
case ParamType::Literal: {
|
||||
string param = entry.Param;
|
||||
std::transform(param.begin(), param.end(), param.begin(), ::tolower);
|
||||
std::transform(operand.begin(), operand.end(), operand.begin(), ::tolower);
|
||||
return operand == param;
|
||||
}
|
||||
|
||||
case ParamType::Byte:
|
||||
return ReadValue(operand, -128, 0xFF, localLabels, firstPass) >= 0;
|
||||
|
||||
case ParamType::Short:
|
||||
return ReadValue(operand, -32768, 0xFFFF, localLabels, firstPass) >= 0;
|
||||
|
||||
case ParamType::Address:
|
||||
if(operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')') {
|
||||
return ReadValue(operand.substr(1, operand.size() - 2), 0, 0xFFFF, localLabels, firstPass) >= 0;
|
||||
}
|
||||
return false;
|
||||
|
||||
case ParamType::HighAddress:
|
||||
if(operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')') {
|
||||
return ReadValue(operand.substr(1, operand.size() - 2), 0xFF00, 0xFFFF, localLabels, firstPass) >= 0;
|
||||
}
|
||||
return false;
|
||||
|
||||
case ParamType::StackOffset:
|
||||
std::transform(operand.begin(), operand.end(), operand.begin(), ::tolower);
|
||||
if(operand.size() > 3 && operand.substr(0, 3) == "sp+") {
|
||||
return ReadValue(operand.substr(3), 0, 0xFF, localLabels, firstPass) >= 0;
|
||||
}
|
||||
return false;
|
||||
|
||||
case ParamType::RelAddress: {
|
||||
int value = ReadValue(operand, 0, 0xFFFF, localLabels, firstPass);
|
||||
if(value >= 0) {
|
||||
int offset = (value - (address + 2));
|
||||
return offset >= -128 && offset <= 127;
|
||||
} else if(firstPass) {
|
||||
return 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GbAssembler::PushOp(uint16_t opCode, vector<int16_t>& output, uint32_t& address)
|
||||
{
|
||||
if(opCode < 256) {
|
||||
PushByte((uint8_t)opCode, output, address);
|
||||
} else {
|
||||
PushWord((uint16_t)opCode, output, address);
|
||||
}
|
||||
}
|
||||
|
||||
void GbAssembler::PushByte(uint8_t operand, vector<int16_t>& output, uint32_t& address)
|
||||
{
|
||||
output.push_back(operand);
|
||||
address++;
|
||||
}
|
||||
|
||||
void GbAssembler::PushWord(uint16_t operand, vector<int16_t>& output, uint32_t& address)
|
||||
{
|
||||
output.push_back((uint8_t)operand);
|
||||
output.push_back((operand >> 8));
|
||||
address += 2;
|
||||
}
|
||||
|
||||
void GbAssembler::ProcessOperand(ParamEntry& entry, string operand, vector<int16_t>& output, uint32_t& address, unordered_map<string, uint16_t>& localLabels, bool firstPass)
|
||||
{
|
||||
switch(entry.Type) {
|
||||
case ParamType::Byte:
|
||||
PushByte((uint8_t)ReadValue(operand, -128, 255, localLabels, firstPass), output, address);
|
||||
break;
|
||||
|
||||
case ParamType::Short:
|
||||
PushWord((uint16_t)ReadValue(operand, -32768, 65535, localLabels, firstPass), output, address);
|
||||
break;
|
||||
|
||||
case ParamType::Address:
|
||||
if(operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')') {
|
||||
PushWord((uint16_t)ReadValue(operand.substr(1, operand.size() - 2), 0, 0xFFFF, localLabels, firstPass), output, address);
|
||||
}
|
||||
break;
|
||||
|
||||
case ParamType::HighAddress:
|
||||
if(operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')') {
|
||||
PushByte((uint8_t)ReadValue(operand.substr(1, operand.size() - 2), 0xFF00, 0xFFFF, localLabels, firstPass), output, address);
|
||||
}
|
||||
break;
|
||||
|
||||
case ParamType::StackOffset:
|
||||
std::transform(operand.begin(), operand.end(), operand.begin(), ::tolower);
|
||||
if(operand.size() > 3 && operand.substr(0, 3) == "sp+") {
|
||||
PushByte((uint8_t)ReadValue(operand.substr(3), 0, 0xFF, localLabels, firstPass), output, address);
|
||||
}
|
||||
break;
|
||||
|
||||
case ParamType::RelAddress: {
|
||||
int value = ReadValue(operand, 0, 0xFFFF, localLabels, firstPass);
|
||||
int offset = (value - (address + 1));
|
||||
PushByte((uint8_t)offset, output, address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GbAssembler::RunPass(vector<int16_t>& output, string code, uint32_t address, int16_t* assembledCode, bool firstPass, unordered_map<string, uint16_t>& localLabels)
|
||||
{
|
||||
unordered_set<string> currentPassLabels;
|
||||
for(string line : StringUtilities::Split(code, '\n')) {
|
||||
//Remove comment
|
||||
size_t commentIndex = line.find(';');
|
||||
if(commentIndex != string::npos) {
|
||||
line = line.substr(0, commentIndex);
|
||||
}
|
||||
|
||||
//Check if this is a label definition
|
||||
size_t labelDefIndex = line.find(':');
|
||||
if(labelDefIndex != string::npos) {
|
||||
std::smatch match;
|
||||
string labelName = line.substr(0, labelDefIndex);
|
||||
if(std::regex_search(labelName, match, labelRegex)) {
|
||||
string label = match.str(1);
|
||||
if(firstPass && currentPassLabels.find(label) != currentPassLabels.end()) {
|
||||
output.push_back(AssemblerSpecialCodes::LabelRedefinition);
|
||||
continue;
|
||||
} else {
|
||||
localLabels[label] = address;
|
||||
currentPassLabels.emplace(label);
|
||||
line = line.substr(labelDefIndex + 1);
|
||||
}
|
||||
} else {
|
||||
output.push_back(AssemblerSpecialCodes::InvalidLabel);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Trim left spaces
|
||||
size_t startIndex = line.find_first_not_of("\t ");
|
||||
if(startIndex > 0 && startIndex != string::npos) {
|
||||
line = line.substr(startIndex);
|
||||
}
|
||||
|
||||
//Check if this is a .db statement
|
||||
if(line.size() > 3 && line.substr(0, 3) == ".db") {
|
||||
line = line.substr(3);
|
||||
for(string byte : StringUtilities::Split(line, ' ')) {
|
||||
if(byte.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int value = ReadValue(byte, -128, 255, localLabels, true);
|
||||
if(value >= 0) {
|
||||
PushByte((uint8_t)value, output, address);
|
||||
}
|
||||
}
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
//Find op code name
|
||||
size_t spaceIndex = line.find(' ');
|
||||
string opName;
|
||||
if(spaceIndex != string::npos) {
|
||||
opName = line.substr(0, spaceIndex);
|
||||
} else {
|
||||
opName = line;
|
||||
}
|
||||
|
||||
if(opName.empty()) {
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::transform(opName.begin(), opName.end(), opName.begin(), ::tolower);
|
||||
|
||||
if(_opCodes.find(opName) == _opCodes.end()) {
|
||||
//No matching opcode found, mark it as invalid
|
||||
output.push_back(AssemblerSpecialCodes::InvalidInstruction);
|
||||
continue;
|
||||
}
|
||||
|
||||
//Find the operands given
|
||||
int paramCount = 0;
|
||||
vector<string> operandList;
|
||||
if(spaceIndex != string::npos) {
|
||||
string operands = line.substr(spaceIndex + 1);
|
||||
operands.erase(std::remove_if(operands.begin(), operands.end(), isspace), operands.end());
|
||||
if(!operands.empty()) {
|
||||
size_t commaIndex = line.find(',');
|
||||
if(commaIndex != string::npos) {
|
||||
paramCount = 2;
|
||||
operandList = StringUtilities::Split(operands, ',');
|
||||
|
||||
bool invalid = operandList.size() > 2;
|
||||
for(string operand : operandList) {
|
||||
if(operand.empty()) {
|
||||
invalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(invalid) {
|
||||
output.push_back(AssemblerSpecialCodes::InvalidOperands);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
paramCount = 1;
|
||||
operandList = { operands };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool matchFound = false;
|
||||
//Find a matching set of opcode + operands
|
||||
for(OpCodeEntry& entry : _opCodes.find(opName)->second) {
|
||||
if(entry.ParamCount == paramCount) {
|
||||
if(paramCount == 0) {
|
||||
PushOp(entry.OpCode, output, address);
|
||||
matchFound = true;
|
||||
break;
|
||||
} else if(paramCount == 1) {
|
||||
if(IsMatch(entry.Param1, operandList[0], address, localLabels, firstPass)) {
|
||||
PushOp(entry.OpCode, output, address);
|
||||
ProcessOperand(entry.Param1, operandList[0], output, address, localLabels, firstPass);
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
} else if(paramCount == 2) {
|
||||
if(IsMatch(entry.Param1, operandList[0], address, localLabels, firstPass) && IsMatch(entry.Param2, operandList[1], address, localLabels, firstPass)) {
|
||||
PushOp(entry.OpCode, output, address);
|
||||
ProcessOperand(entry.Param1, operandList[0], output, address, localLabels, firstPass);
|
||||
ProcessOperand(entry.Param2, operandList[1], output, address, localLabels, firstPass);
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!matchFound) {
|
||||
output.push_back(AssemblerSpecialCodes::InvalidOperands);
|
||||
} else {
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GbAssembler::AssembleCode(string code, uint32_t address, int16_t* assembledCode)
|
||||
{
|
||||
vector<int16_t> output;
|
||||
unordered_map<string, uint16_t> localLabels;
|
||||
|
||||
RunPass(output, code, address, assembledCode, true, localLabels);
|
||||
output.clear();
|
||||
RunPass(output, code, address, assembledCode, false, localLabels);
|
||||
|
||||
memcpy(assembledCode, output.data(), std::min<int>(100000, (int)output.size()) * sizeof(uint16_t));
|
||||
return (uint32_t)output.size();
|
||||
}
|
55
Core/GbAssembler.h
Normal file
55
Core/GbAssembler.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "IAssembler.h"
|
||||
|
||||
class LabelManager;
|
||||
|
||||
enum class ParamType
|
||||
{
|
||||
None,
|
||||
Literal,
|
||||
Byte,
|
||||
Short,
|
||||
Address,
|
||||
HighAddress,
|
||||
RelAddress,
|
||||
StackOffset
|
||||
};
|
||||
|
||||
struct ParamEntry
|
||||
{
|
||||
string Param;
|
||||
ParamType Type;
|
||||
};
|
||||
|
||||
struct OpCodeEntry
|
||||
{
|
||||
uint16_t OpCode;
|
||||
int ParamCount;
|
||||
ParamEntry Param1;
|
||||
ParamEntry Param2;
|
||||
};
|
||||
|
||||
class GbAssembler : public IAssembler
|
||||
{
|
||||
private:
|
||||
unordered_map<string, vector<OpCodeEntry>> _opCodes;
|
||||
shared_ptr<LabelManager> _labelManager;
|
||||
|
||||
void InitParamEntry(ParamEntry& entry, string param);
|
||||
bool IsRegisterName(string op);
|
||||
void InitAssembler();
|
||||
int ReadValue(string operand, int min, int max, unordered_map<string, uint16_t>& localLabels, bool firstPass);
|
||||
bool IsMatch(ParamEntry& entry, string operand, uint32_t address, unordered_map<string, uint16_t>& localLabels, bool firstPass);
|
||||
void PushOp(uint16_t opCode, vector<int16_t>& output, uint32_t& address);
|
||||
void PushByte(uint8_t operand, vector<int16_t>& output, uint32_t& address);
|
||||
void PushWord(uint16_t operand, vector<int16_t>& output, uint32_t& address);
|
||||
void ProcessOperand(ParamEntry& entry, string operand, vector<int16_t>& output, uint32_t& address, unordered_map<string, uint16_t>& localLabels, bool firstPass);
|
||||
|
||||
void RunPass(vector<int16_t>& output, string code, uint32_t address, int16_t* assembledCode, bool firstPass, unordered_map<string, uint16_t>& localLabels);
|
||||
|
||||
public:
|
||||
GbAssembler(shared_ptr<LabelManager> labelManager);
|
||||
|
||||
uint32_t AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode);
|
||||
};
|
|
@ -16,6 +16,7 @@
|
|||
#include "GameboyDisUtils.h"
|
||||
#include "GbEventManager.h"
|
||||
#include "BaseEventManager.h"
|
||||
#include "GbAssembler.h"
|
||||
|
||||
GbDebugger::GbDebugger(Debugger* debugger)
|
||||
{
|
||||
|
@ -31,6 +32,11 @@ GbDebugger::GbDebugger(Debugger* debugger)
|
|||
_callstackManager.reset(new CallstackManager(debugger));
|
||||
_breakpointManager.reset(new BreakpointManager(debugger, CpuType::Gameboy, _eventManager.get()));
|
||||
_step.reset(new StepRequest());
|
||||
_assembler.reset(new GbAssembler(debugger->GetLabelManager()));
|
||||
}
|
||||
|
||||
GbDebugger::~GbDebugger()
|
||||
{
|
||||
}
|
||||
|
||||
void GbDebugger::Reset()
|
||||
|
@ -159,6 +165,11 @@ shared_ptr<GbEventManager> GbDebugger::GetEventManager()
|
|||
return _eventManager;
|
||||
}
|
||||
|
||||
shared_ptr<GbAssembler> GbDebugger::GetAssembler()
|
||||
{
|
||||
return _assembler;
|
||||
}
|
||||
|
||||
shared_ptr<CallstackManager> GbDebugger::GetCallstackManager()
|
||||
{
|
||||
return _callstackManager;
|
||||
|
|
|
@ -13,6 +13,7 @@ class MemoryManager;
|
|||
class BreakpointManager;
|
||||
class EmuSettings;
|
||||
class GbEventManager;
|
||||
class GbAssembler;
|
||||
|
||||
class GbDebugger final : public IDebugger
|
||||
{
|
||||
|
@ -28,12 +29,14 @@ class GbDebugger final : public IDebugger
|
|||
shared_ptr<CallstackManager> _callstackManager;
|
||||
unique_ptr<BreakpointManager> _breakpointManager;
|
||||
unique_ptr<StepRequest> _step;
|
||||
shared_ptr<GbAssembler> _assembler;
|
||||
|
||||
uint8_t _prevOpCode = 0xFF;
|
||||
uint32_t _prevProgramCounter = 0;
|
||||
|
||||
public:
|
||||
GbDebugger(Debugger* debugger);
|
||||
~GbDebugger();
|
||||
|
||||
void Reset();
|
||||
|
||||
|
@ -44,6 +47,7 @@ public:
|
|||
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc);
|
||||
|
||||
shared_ptr<GbEventManager> GetEventManager();
|
||||
shared_ptr<GbAssembler> GetAssembler();
|
||||
shared_ptr<CallstackManager> GetCallstackManager();
|
||||
BreakpointManager* GetBreakpointManager();
|
||||
};
|
27
Core/IAssembler.h
Normal file
27
Core/IAssembler.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
class IAssembler
|
||||
{
|
||||
public:
|
||||
virtual uint32_t AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode) = 0;
|
||||
};
|
||||
|
||||
enum AssemblerSpecialCodes
|
||||
{
|
||||
OK = 0,
|
||||
EndOfLine = -1,
|
||||
ParsingError = -2,
|
||||
OutOfRangeJump = -3,
|
||||
LabelRedefinition = -4,
|
||||
MissingOperand = -5,
|
||||
OperandOutOfRange = -6,
|
||||
InvalidHex = -7,
|
||||
InvalidSpaces = -8,
|
||||
TrailingText = -9,
|
||||
UnknownLabel = -10,
|
||||
InvalidInstruction = -11,
|
||||
InvalidBinaryValue = -12,
|
||||
InvalidOperands = -13,
|
||||
InvalidLabel = -14,
|
||||
};
|
|
@ -106,7 +106,7 @@ extern "C"
|
|||
DllExport const char* __stdcall GetScriptLog(int32_t scriptId) { return GetDebugger()->GetScriptManager()->GetScriptLog(scriptId); }
|
||||
//DllExport void __stdcall DebugSetScriptTimeout(uint32_t timeout) { LuaScriptingContext::SetScriptTimeout(timeout); }
|
||||
|
||||
DllExport uint32_t __stdcall AssembleCode(char* code, uint32_t startAddress, int16_t* assembledOutput) { return GetDebugger()->GetAssembler()->AssembleCode(code, startAddress, assembledOutput); }
|
||||
DllExport uint32_t __stdcall AssembleCode(CpuType cpuType, char* code, uint32_t startAddress, int16_t* assembledOutput) { return GetDebugger()->GetAssembler(cpuType)->AssembleCode(code, startAddress, assembledOutput); }
|
||||
|
||||
DllExport void __stdcall SaveRomToDisk(char* filename, bool saveIpsFile, CdlStripOption cdlStripOption) { GetDebugger()->SaveRomToDisk(filename, saveIpsFile, cdlStripOption); }
|
||||
};
|
|
@ -63,6 +63,7 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
|
|||
$(CORE_DIR)/GameServer.cpp \
|
||||
$(CORE_DIR)/GameServerConnection.cpp \
|
||||
$(CORE_DIR)/Gameboy.cpp \
|
||||
$(CORE_DIR)/GbAssembler.cpp \
|
||||
$(CORE_DIR)/GbCpu.cpp \
|
||||
$(CORE_DIR)/GbDmaController.cpp \
|
||||
$(CORE_DIR)/GbPpu.cpp \
|
||||
|
|
|
@ -511,7 +511,7 @@ namespace Mesen.GUI.Debugger.Controls
|
|||
|
||||
if(range.Start.Address >= 0 && range.End.Address >= 0 && range.Start.Address <= range.End.Address) {
|
||||
int length = range.End.Address - range.Start.Address + 1;
|
||||
DebugWindowManager.OpenAssembler(GetSelectedCode(), range.Start.Address, length);
|
||||
DebugWindowManager.OpenAssembler(_manager.CpuType, GetSelectedCode(), range.Start.Address, length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -650,7 +650,9 @@ namespace Mesen.GUI.Debugger.Controls
|
|||
|
||||
bool showMarkAs = !_inSourceView && (_manager.CpuType == CpuType.Cpu || _manager.CpuType == CpuType.Sa1);
|
||||
mnuMarkSelectionAs.Visible = showMarkAs;
|
||||
mnuEditSelectedCode.Visible = showMarkAs;
|
||||
|
||||
bool showEditCode = !_inSourceView && (_manager.CpuType == CpuType.Cpu || _manager.CpuType == CpuType.Sa1 || _manager.CpuType == CpuType.Gameboy);
|
||||
mnuEditSelectedCode.Visible = showEditCode;
|
||||
|
||||
mnuAddToWatch.Enabled = active;
|
||||
mnuEditInMemoryTools.Enabled = active;
|
||||
|
|
|
@ -111,13 +111,13 @@ namespace Mesen.GUI.Debugger
|
|||
throw new Exception("Invalid CPU type");
|
||||
}
|
||||
|
||||
public static void OpenAssembler(string code = "", int startAddress = 0, int blockLength = 0)
|
||||
public static void OpenAssembler(CpuType cpuType, string code = "", int startAddress = 0, int blockLength = 0)
|
||||
{
|
||||
if(_openedWindows.Count == 0) {
|
||||
DebugWorkspaceManager.GetWorkspace();
|
||||
}
|
||||
|
||||
frmAssembler frm = new frmAssembler(code, startAddress, blockLength);
|
||||
frmAssembler frm = new frmAssembler(cpuType, code, startAddress, blockLength);
|
||||
frm.Icon = Properties.Resources.Chip;
|
||||
_openedWindows.Add(frm);
|
||||
frm.FormClosed += Debugger_FormClosed;
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Mesen.GUI.Debugger
|
|||
{
|
||||
public partial class frmAssembler : BaseForm
|
||||
{
|
||||
private CpuType _cpuType;
|
||||
private int _startAddress;
|
||||
private int _blockLength;
|
||||
private bool _hasParsingErrors = false;
|
||||
|
@ -27,9 +28,20 @@ namespace Mesen.GUI.Debugger
|
|||
private int _textVersion = 0;
|
||||
private bool _updating = false;
|
||||
|
||||
public frmAssembler(string code = "", int startAddress = 0, int blockLength = 0)
|
||||
public frmAssembler(CpuType? cpuType = null, string code = "", int startAddress = 0, int blockLength = 0)
|
||||
{
|
||||
if(cpuType != null) {
|
||||
_cpuType = cpuType.Value;
|
||||
} else {
|
||||
_cpuType = EmuApi.GetRomInfo().CoprocessorType == CoprocessorType.Gameboy ? CpuType.Gameboy : CpuType.Cpu;
|
||||
}
|
||||
|
||||
if(_cpuType == CpuType.Sa1) {
|
||||
_cpuType = CpuType.Cpu;
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
txtCode.ForeColor = Color.Black;
|
||||
|
||||
AssemblerConfig cfg = ConfigManager.Config.Debug.Assembler;
|
||||
|
@ -126,7 +138,7 @@ namespace Mesen.GUI.Debugger
|
|||
return false;
|
||||
} else {
|
||||
for(int i = 0; i < ctrlHexBox.ByteProvider.Length; i++) {
|
||||
if(DebugApi.GetMemoryValue(SnesMemoryType.CpuMemory, (UInt32)(_startAddress + i)) != ctrlHexBox.ByteProvider.ReadByte(i)) {
|
||||
if(DebugApi.GetMemoryValue(_cpuType.ToMemoryType(), (UInt32)(_startAddress + i)) != ctrlHexBox.ByteProvider.ReadByte(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +165,7 @@ namespace Mesen.GUI.Debugger
|
|||
int version = _textVersion;
|
||||
|
||||
Task.Run(() => {
|
||||
short[] byteCode = DebugApi.AssembleCode(text, (UInt32)_startAddress);
|
||||
short[] byteCode = DebugApi.AssembleCode(_cpuType, text, (UInt32)_startAddress);
|
||||
|
||||
this.BeginInvoke((Action)(() => {
|
||||
_updating = false;
|
||||
|
@ -184,6 +196,8 @@ namespace Mesen.GUI.Debugger
|
|||
case AssemblerSpecialCodes.UnknownLabel: message = "Unknown label"; break;
|
||||
case AssemblerSpecialCodes.InvalidInstruction: message = "Invalid instruction"; break;
|
||||
case AssemblerSpecialCodes.InvalidBinaryValue: message = "Invalid binary value"; break;
|
||||
case AssemblerSpecialCodes.InvalidOperands: message = "Invalid operands for instruction"; break;
|
||||
case AssemblerSpecialCodes.InvalidLabel: message = "Invalid label name"; break;
|
||||
}
|
||||
errorList.Add(new ErrorDetail() { Message = message + " - " + codeLines[line-1], LineNumber = line });
|
||||
line++;
|
||||
|
@ -264,15 +278,22 @@ namespace Mesen.GUI.Debugger
|
|||
bytes.Add(0xEA);
|
||||
}
|
||||
|
||||
DebugApi.SetMemoryValues(SnesMemoryType.CpuMemory, (UInt32)_startAddress, bytes.ToArray(), bytes.Count);
|
||||
frmDebugger debugger = null;
|
||||
if(_cpuType == CpuType.Gameboy) {
|
||||
DebugApi.SetMemoryValues(SnesMemoryType.GameboyMemory, (UInt32)_startAddress, bytes.ToArray(), bytes.Count);
|
||||
debugger = DebugWindowManager.OpenDebugger(CpuType.Gameboy);
|
||||
//TODO: CDL for gameboy
|
||||
} else {
|
||||
DebugApi.SetMemoryValues(SnesMemoryType.CpuMemory, (UInt32)_startAddress, bytes.ToArray(), bytes.Count);
|
||||
|
||||
AddressInfo absStart = DebugApi.GetAbsoluteAddress(new AddressInfo() { Address = _startAddress, Type = SnesMemoryType.CpuMemory });
|
||||
AddressInfo absEnd = DebugApi.GetAbsoluteAddress(new AddressInfo() { Address = _startAddress + bytes.Count, Type = SnesMemoryType.CpuMemory });
|
||||
if(absStart.Type == SnesMemoryType.PrgRom && absEnd.Type == SnesMemoryType.PrgRom && (absEnd.Address - absStart.Address) == bytes.Count) {
|
||||
DebugApi.MarkBytesAs((uint)absStart.Address, (uint)absEnd.Address, CdlFlags.Code);
|
||||
AddressInfo absStart = DebugApi.GetAbsoluteAddress(new AddressInfo() { Address = _startAddress, Type = SnesMemoryType.CpuMemory });
|
||||
AddressInfo absEnd = DebugApi.GetAbsoluteAddress(new AddressInfo() { Address = _startAddress + bytes.Count, Type = SnesMemoryType.CpuMemory });
|
||||
if(absStart.Type == SnesMemoryType.PrgRom && absEnd.Type == SnesMemoryType.PrgRom && (absEnd.Address - absStart.Address) == bytes.Count) {
|
||||
DebugApi.MarkBytesAs((uint)absStart.Address, (uint)absEnd.Address, CdlFlags.Code);
|
||||
}
|
||||
debugger = DebugWindowManager.OpenDebugger(CpuType.Cpu);
|
||||
}
|
||||
|
||||
frmDebugger debugger = DebugWindowManager.OpenDebugger(CpuType.Cpu);
|
||||
if(debugger != null) {
|
||||
debugger.RefreshDisassembly();
|
||||
}
|
||||
|
@ -374,7 +395,9 @@ namespace Mesen.GUI.Debugger
|
|||
TrailingText = -9,
|
||||
UnknownLabel = -10,
|
||||
InvalidInstruction = -11,
|
||||
InvalidBinaryValue = -12
|
||||
InvalidBinaryValue = -12,
|
||||
InvalidOperands = -13,
|
||||
InvalidLabel = -14,
|
||||
}
|
||||
|
||||
private void lstErrors_DoubleClick(object sender, EventArgs e)
|
||||
|
|
|
@ -481,8 +481,6 @@ namespace Mesen.GUI.Forms
|
|||
mnuDebugger.Visible = !isGameboyMode;
|
||||
mnuSpcDebugger.Enabled = !isGameboyMode;
|
||||
mnuSpcDebugger.Visible = !isGameboyMode;
|
||||
mnuAssembler.Enabled = !isGameboyMode;
|
||||
mnuAssembler.Visible = !isGameboyMode;
|
||||
sepCoprocessors.Visible = !isGameboyMode;
|
||||
}
|
||||
|
||||
|
|
|
@ -176,12 +176,12 @@ namespace Mesen.GUI
|
|||
[DllImport(DllPath)] public static extern void SetCdlData([In]byte[] cdlData, Int32 length);
|
||||
[DllImport(DllPath)] public static extern void MarkBytesAs(UInt32 start, UInt32 end, CdlFlags type);
|
||||
|
||||
[DllImport(DllPath, EntryPoint = "AssembleCode")] private static extern UInt32 AssembleCodeWrapper([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string code, UInt32 startAddress, [In, Out]Int16[] assembledCodeBuffer);
|
||||
public static Int16[] AssembleCode(string code, UInt32 startAddress)
|
||||
[DllImport(DllPath, EntryPoint = "AssembleCode")] private static extern UInt32 AssembleCodeWrapper(CpuType cpuType, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string code, UInt32 startAddress, [In, Out]Int16[] assembledCodeBuffer);
|
||||
public static Int16[] AssembleCode(CpuType cpuType, string code, UInt32 startAddress)
|
||||
{
|
||||
code = code.Replace(Environment.NewLine, "\n");
|
||||
Int16[] assembledCode = new Int16[100000];
|
||||
UInt32 size = DebugApi.AssembleCodeWrapper(code, startAddress, assembledCode);
|
||||
UInt32 size = DebugApi.AssembleCodeWrapper(cpuType, code, startAddress, assembledCode);
|
||||
Array.Resize(ref assembledCode, (int)size);
|
||||
return assembledCode;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue