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,7 +8,7 @@
|
|||
#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",
|
||||
|
@ -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];
|
||||
|
@ -135,3 +133,8 @@ bool GameboyDisUtils::IsReturnInstruction(uint8_t opCode)
|
|||
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,6 +278,12 @@ namespace Mesen.GUI.Debugger
|
|||
bytes.Add(0xEA);
|
||||
}
|
||||
|
||||
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 });
|
||||
|
@ -271,8 +291,9 @@ namespace Mesen.GUI.Debugger
|
|||
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