mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
Debugger: Assembler rewrite/refactoring for NES/SNES/PCE
This commit is contained in:
parent
34414743da
commit
55b96c9ebc
23 changed files with 1039 additions and 985 deletions
|
@ -19,6 +19,8 @@
|
|||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Debugger\Base6502Assembler.h" />
|
||||
<ClInclude Include="PCE\Debugger\PceAssembler.h" />
|
||||
<ClInclude Include="PCE\IPceMapper.h" />
|
||||
<ClInclude Include="PCE\PceArcadeCard.h" />
|
||||
<ClInclude Include="PCE\PceVpc.h" />
|
||||
|
@ -411,6 +413,7 @@
|
|||
<ClInclude Include="Shared\Audio\WaveRecorder.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Debugger\Base6502Assembler.cpp" />
|
||||
<ClCompile Include="Debugger\BaseEventManager.cpp" />
|
||||
<ClCompile Include="Debugger\ExpressionEvaluator.Cx4.cpp" />
|
||||
<ClCompile Include="Debugger\ExpressionEvaluator.Gameboy.cpp" />
|
||||
|
@ -450,6 +453,7 @@
|
|||
<ClCompile Include="NES\Mappers\NsfMapper.cpp" />
|
||||
<ClCompile Include="NES\Mappers\VsSystem\VsControlManager.cpp" />
|
||||
<ClCompile Include="NES\NesNtscFilter.cpp" />
|
||||
<ClCompile Include="PCE\Debugger\PceAssembler.cpp" />
|
||||
<ClCompile Include="PCE\Input\PceTurboTap.cpp" />
|
||||
<ClCompile Include="PCE\Debugger\DummyPceCpu.cpp" />
|
||||
<ClCompile Include="PCE\Debugger\PceDebugger.cpp" />
|
||||
|
|
|
@ -1823,6 +1823,12 @@
|
|||
<ClInclude Include="PCE\PceArcadeCard.h">
|
||||
<Filter>PCE</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PCE\Debugger\PceAssembler.h">
|
||||
<Filter>PCE\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Debugger\Base6502Assembler.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Shared\Video\RotateFilter.cpp">
|
||||
|
@ -1907,6 +1913,12 @@
|
|||
<ClCompile Include="PCE\PceArcadeCard.cpp">
|
||||
<Filter>PCE</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PCE\Debugger\PceAssembler.cpp">
|
||||
<Filter>PCE\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debugger\Base6502Assembler.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="PCE">
|
||||
|
|
300
Core/Debugger/Base6502Assembler.cpp
Normal file
300
Core/Debugger/Base6502Assembler.cpp
Normal file
|
@ -0,0 +1,300 @@
|
|||
#include "stdafx.h"
|
||||
#include <regex>
|
||||
#include <unordered_map>
|
||||
#include "Utilities/HexUtilities.h"
|
||||
#include "Utilities/StringUtilities.h"
|
||||
#include "Debugger/Base6502Assembler.h"
|
||||
#include "Debugger/DisassemblyInfo.h"
|
||||
#include "Debugger/LabelManager.h"
|
||||
|
||||
static const std::regex labelRegex = std::regex("^\\s*([@_a-zA-Z][@_a-zA-Z0-9]*):(.*)", std::regex_constants::icase);
|
||||
static const std::regex byteRegex = std::regex("^\\s*[.]db\\s+((\\$[a-fA-F0-9]{1,2}[ ])*)(\\$[a-fA-F0-9]{1,2})+\\s*(;*)(.*)$", std::regex_constants::icase);
|
||||
|
||||
template<class T>
|
||||
void Base6502Assembler<T>::ProcessLine(string code, uint32_t& instructionAddress, vector<int16_t>& output, unordered_map<string, uint32_t>& labels, bool firstPass, unordered_map<string, uint32_t>& currentPassLabels)
|
||||
{
|
||||
//Remove comments
|
||||
size_t commentOffset = code.find_first_of(';');
|
||||
if(commentOffset != string::npos) {
|
||||
code = code.substr(0, commentOffset);
|
||||
}
|
||||
|
||||
code = StringUtilities::Trim(code);
|
||||
|
||||
std::smatch match;
|
||||
if(std::regex_search(code, match, byteRegex)) {
|
||||
//Parse .db statements
|
||||
vector<string> bytes = StringUtilities::Split(match.str(1) + match.str(3), ' ');
|
||||
for(string& byte : bytes) {
|
||||
output.push_back((uint8_t)(HexUtilities::FromHex(byte.substr(1))));
|
||||
instructionAddress++;
|
||||
}
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
return;
|
||||
} else if(std::regex_search(code, match, labelRegex)) {
|
||||
//Parse label definitions
|
||||
string label = match.str(1);
|
||||
string afterLabel = match.str(2);
|
||||
if(currentPassLabels.find(match.str(1)) != currentPassLabels.end()) {
|
||||
output.push_back(AssemblerSpecialCodes::LabelRedefinition);
|
||||
} else {
|
||||
labels[match.str(1)] = instructionAddress;
|
||||
currentPassLabels[match.str(1)] = instructionAddress;
|
||||
ProcessLine(afterLabel, instructionAddress, output, labels, firstPass, currentPassLabels);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
code = StringUtilities::Trim(code);
|
||||
if(code.empty()) {
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
return;
|
||||
}
|
||||
|
||||
AssemblerLineData op = {};
|
||||
|
||||
size_t opnameOffset = code.find_first_of(' ', 0);
|
||||
if(opnameOffset != string::npos) {
|
||||
op.OpCode = StringUtilities::ToUpper(code.substr(0, opnameOffset));
|
||||
code = StringUtilities::Trim(code.substr(opnameOffset));
|
||||
if(code.size() > 0) {
|
||||
vector<string> operands = StringUtilities::Split(code, ',');
|
||||
for(string& operand : operands) {
|
||||
if(op.OperandCount >= 3) {
|
||||
output.push_back(AssemblerSpecialCodes::InvalidOperands);
|
||||
return;
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes result = ParseOperand(op, StringUtilities::Trim(operand), firstPass, labels);
|
||||
if(result != AssemblerSpecialCodes::OK) {
|
||||
output.push_back(result);
|
||||
return;
|
||||
}
|
||||
op.OperandCount++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//no operands could be found
|
||||
op.OpCode = StringUtilities::ToUpper(code);
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes result = ResolveOpMode(op, instructionAddress, firstPass);
|
||||
if(result != AssemblerSpecialCodes::OK) {
|
||||
output.push_back(result);
|
||||
return;
|
||||
}
|
||||
|
||||
AssembleInstruction(op, instructionAddress, output, firstPass);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
AssemblerSpecialCodes Base6502Assembler<T>::ParseOperand(AssemblerLineData& lineData, string operandStr, bool firstPass, unordered_map<string, uint32_t>& labels)
|
||||
{
|
||||
AssemblerOperand& operand = lineData.Operands[lineData.OperandCount];
|
||||
|
||||
if(operandStr.empty()) {
|
||||
return AssemblerSpecialCodes::OK;
|
||||
}
|
||||
|
||||
//Check for operands that match
|
||||
static const std::regex operandRegex = std::regex("^([(\\[]?)[\\s]*(((#?)[\\s]*([$%]?)(-?)([0-9a-f]{1,16}))|([@_a-zA-Z][@_a-zA-Z0-9]*))[\\s]*([\\])]?)[\\s]*$", std::regex_constants::icase);
|
||||
|
||||
std::smatch match;
|
||||
if(std::regex_search(operandStr, match, operandRegex)) {
|
||||
operand.HasOpeningBracket = !match.str(1).empty() && match.str(1) == "[";
|
||||
operand.HasOpeningParenthesis = !match.str(1).empty() && match.str(1) == "(";
|
||||
|
||||
operand.HasClosingBracket = !match.str(9).empty() && match.str(9) == "]";
|
||||
operand.HasClosingParenthesis = !match.str(9).empty() && match.str(9) == ")";
|
||||
|
||||
operand.IsImmediate = !match.str(4).empty();
|
||||
|
||||
bool hasNegativeSign = !match.str(6).empty();
|
||||
string label = match.str(8);
|
||||
|
||||
if(!match.str(5).empty()) {
|
||||
if(hasNegativeSign) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
operand.ValueType = match.str(5) == "$" ? OperandValueType::Hex : OperandValueType::Binary;
|
||||
} else if(!label.empty()) {
|
||||
operand.ValueType = OperandValueType::Label;
|
||||
} else {
|
||||
if(match.str(7)[0] < '0' || match.str(7)[0] > '9') {
|
||||
label = match.str(7);
|
||||
operand.ValueType = OperandValueType::Label;
|
||||
} else {
|
||||
operand.ValueType = OperandValueType::Decimal;
|
||||
}
|
||||
}
|
||||
|
||||
if(operand.ValueType != OperandValueType::Label) {
|
||||
operand.Type = OperandType::Custom;
|
||||
|
||||
string rawOperand = match.str(7);
|
||||
for(char c : rawOperand) {
|
||||
if(operand.ValueType == OperandValueType::Hex && !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) {
|
||||
return AssemblerSpecialCodes::InvalidHex;
|
||||
} else if(operand.ValueType == OperandValueType::Binary && c != '0' && c != '1') {
|
||||
return AssemblerSpecialCodes::InvalidBinaryValue;
|
||||
} else if(operand.ValueType == OperandValueType::Decimal && (c < '0' || c > '9')) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
}
|
||||
|
||||
if(operand.ValueType == OperandValueType::Hex) {
|
||||
operand.Value = HexUtilities::FromHex(rawOperand);
|
||||
if(rawOperand.size() == 0) {
|
||||
return AssemblerSpecialCodes::MissingOperand;
|
||||
} else if(rawOperand.size() <= 2) {
|
||||
operand.ByteCount = 1;
|
||||
} else if(rawOperand.size() <= 4) {
|
||||
operand.ByteCount = 2;
|
||||
} else if(rawOperand.size() <= 6) {
|
||||
operand.ByteCount = 3;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(operand.ValueType == OperandValueType::Decimal) {
|
||||
operand.Value = std::stoll((hasNegativeSign ? "-" : "") + rawOperand);
|
||||
if(operand.Value < -0x800000) {
|
||||
//< -2^23 is invalid
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
} else if(operand.Value < -0x8000) {
|
||||
operand.ByteCount = 3;
|
||||
} else if(operand.Value < -0x80) {
|
||||
operand.ByteCount = 2;
|
||||
} else if(operand.Value <= 0xFF) {
|
||||
operand.ByteCount = 1;
|
||||
} else if(operand.Value <= 0xFFFF) {
|
||||
operand.ByteCount = 2;
|
||||
} else if(operand.Value <= 0xFFFFFF) {
|
||||
operand.ByteCount = 3;
|
||||
} else {
|
||||
//>= 2^23 is invalid
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(operand.ValueType == OperandValueType::Binary) {
|
||||
//Convert the binary value to hex
|
||||
int32_t value = 0;
|
||||
for(size_t i = 0; i < rawOperand.size(); i++) {
|
||||
value <<= 1;
|
||||
value |= rawOperand[i] == '1' ? 1 : 0;
|
||||
}
|
||||
operand.Value = value;
|
||||
operand.ByteCount = rawOperand.size() > 8 ? 2 : 1;
|
||||
}
|
||||
} else {
|
||||
//Labels / register name
|
||||
string lcOperand = StringUtilities::ToLower(label);
|
||||
if(lcOperand == "a") {
|
||||
operand.Type = OperandType::A;
|
||||
} else if(lcOperand == "x") {
|
||||
operand.Type = OperandType::X;
|
||||
} else if(lcOperand == "y") {
|
||||
operand.Type = OperandType::Y;
|
||||
} else if(lcOperand == "s") {
|
||||
operand.Type = OperandType::S;
|
||||
} else {
|
||||
operand.Type = OperandType::Custom;
|
||||
|
||||
//Label
|
||||
auto result = labels.find(label);
|
||||
int32_t addr = -1;
|
||||
if(result != labels.end()) {
|
||||
addr = result->second;
|
||||
} else {
|
||||
addr = _labelManager->GetLabelRelativeAddress(label, _cpuType);
|
||||
}
|
||||
|
||||
if(addr > 0xFFFF) {
|
||||
operand.Value = addr;
|
||||
operand.ByteCount = 3;
|
||||
} else if(addr > 0xFF) {
|
||||
operand.Value = addr;
|
||||
operand.ByteCount = 2;
|
||||
} else if(addr >= 0) {
|
||||
operand.Value = addr;
|
||||
operand.ByteCount = 1;
|
||||
} else {
|
||||
if(firstPass) {
|
||||
//First pass, we couldn't find a matching label, so it might be defined later on
|
||||
//Pretend it exists for now
|
||||
_needSecondPass = true;
|
||||
operand.Value = 0xFFFF;
|
||||
operand.ByteCount = 2;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::UnknownLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return AssemblerSpecialCodes::OK;
|
||||
} else {
|
||||
//invalid operand
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool Base6502Assembler<T>::IsOpModeAvailable(string& opCode, T addrMode)
|
||||
{
|
||||
return _availableModesByOpName[opCode].find(addrMode) != _availableModesByOpName[opCode].end();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int16_t Base6502Assembler<T>::GetOpByteCode(string& opCode, T addrMode)
|
||||
{
|
||||
auto nameResult = _availableModesByOpName.find(opCode);
|
||||
if(nameResult != _availableModesByOpName.end()) {
|
||||
auto modeResult = nameResult->second.find(addrMode);
|
||||
if(modeResult != _availableModesByOpName[opCode].end()) {
|
||||
return modeResult->second;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Base6502Assembler<T>::AssembleInstruction(AssemblerLineData& op, uint32_t& instructionAddress, vector<int16_t>& output, bool firstPass)
|
||||
{
|
||||
int16_t result = GetOpByteCode(op.OpCode, op.AddrMode);
|
||||
if(result < 0) {
|
||||
output.push_back(AssemblerSpecialCodes::InvalidInstruction);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t opCode = (uint8_t)result;
|
||||
output.push_back(opCode);
|
||||
instructionAddress++;
|
||||
|
||||
for(int i = 0; i < op.OperandCount; i++) {
|
||||
instructionAddress += op.Operands[i].ByteCount;
|
||||
|
||||
if(op.Operands[i].ByteCount == 1) {
|
||||
output.push_back(op.Operands[i].Value & 0xFF);
|
||||
} else if(op.Operands[i].ByteCount == 2) {
|
||||
output.push_back(op.Operands[i].Value & 0xFF);
|
||||
output.push_back((op.Operands[i].Value >> 8) & 0xFF);
|
||||
} else if(op.Operands[i].ByteCount == 3) {
|
||||
output.push_back(op.Operands[i].Value & 0xFF);
|
||||
output.push_back((op.Operands[i].Value >> 8) & 0xFF);
|
||||
output.push_back((op.Operands[i].Value >> 16) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
}
|
||||
|
||||
enum class SnesAddrMode : uint8_t;
|
||||
template void Base6502Assembler<SnesAddrMode>::ProcessLine(string code, uint32_t& instructionAddress, vector<int16_t>& output, unordered_map<string, uint32_t>& labels, bool firstPass, unordered_map<string, uint32_t>& currentPassLabels);
|
||||
template bool Base6502Assembler<SnesAddrMode>::IsOpModeAvailable(string& opcode, SnesAddrMode mode);
|
||||
|
||||
enum class NesAddrMode;
|
||||
template void Base6502Assembler<NesAddrMode>::ProcessLine(string code, uint32_t& instructionAddress, vector<int16_t>& output, unordered_map<string, uint32_t>& labels, bool firstPass, unordered_map<string, uint32_t>& currentPassLabels);
|
||||
template bool Base6502Assembler<NesAddrMode>::IsOpModeAvailable(string& opcode, NesAddrMode mode);
|
||||
|
||||
enum class PceAddrMode;
|
||||
template void Base6502Assembler<PceAddrMode>::ProcessLine(string code, uint32_t& instructionAddress, vector<int16_t>& output, unordered_map<string, uint32_t>& labels, bool firstPass, unordered_map<string, uint32_t>& currentPassLabels);
|
||||
template bool Base6502Assembler<PceAddrMode>::IsOpModeAvailable(string& opcode, PceAddrMode mode);
|
131
Core/Debugger/Base6502Assembler.h
Normal file
131
Core/Debugger/Base6502Assembler.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Shared/CpuType.h"
|
||||
#include "Debugger/IAssembler.h"
|
||||
#include "Utilities/StringUtilities.h"
|
||||
|
||||
class LabelManager;
|
||||
|
||||
template<class T>
|
||||
class Base6502Assembler : public IAssembler
|
||||
{
|
||||
protected:
|
||||
enum class OperandType
|
||||
{
|
||||
None,
|
||||
A,
|
||||
X,
|
||||
Y,
|
||||
S,
|
||||
Custom
|
||||
};
|
||||
|
||||
enum class OperandValueType
|
||||
{
|
||||
None,
|
||||
Decimal,
|
||||
Hex,
|
||||
Binary,
|
||||
Label
|
||||
};
|
||||
|
||||
struct AssemblerOperand
|
||||
{
|
||||
OperandType Type = OperandType::None;
|
||||
OperandValueType ValueType = OperandValueType::None;
|
||||
int64_t Value = 0;
|
||||
int ByteCount = 0;
|
||||
int OperandSize = 0;
|
||||
bool IsImmediate = false;
|
||||
bool HasOpeningParenthesis = false;
|
||||
bool HasClosingParenthesis = false;
|
||||
bool HasOpeningBracket = false;
|
||||
bool HasClosingBracket = false;
|
||||
|
||||
bool HasParenOrBracket()
|
||||
{
|
||||
return HasOpeningBracket || HasClosingParenthesis || HasOpeningBracket || HasClosingBracket;
|
||||
}
|
||||
};
|
||||
|
||||
struct AssemblerLineData
|
||||
{
|
||||
string OpCode;
|
||||
T AddrMode = {};
|
||||
AssemblerOperand Operands[3] = {};
|
||||
int OperandCount = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
unordered_map<string, unordered_map<T, uint8_t>> _availableModesByOpName;
|
||||
bool _needSecondPass = false;
|
||||
CpuType _cpuType;
|
||||
|
||||
LabelManager* _labelManager;
|
||||
void ProcessLine(string code, uint32_t& instructionAddress, vector<int16_t>& output, unordered_map<string, uint32_t>& labels, bool firstPass, unordered_map<string, uint32_t>& currentPassLabels);
|
||||
AssemblerSpecialCodes ParseOperand(AssemblerLineData& lineData, string operand, bool firstPass, unordered_map<string, uint32_t>& labels);
|
||||
void AssembleInstruction(AssemblerLineData& lineData, uint32_t& instructionAddress, vector<int16_t>& output, bool firstPass);
|
||||
|
||||
protected:
|
||||
bool IsOpModeAvailable(string& opCode, T mode);
|
||||
int16_t GetOpByteCode(string& opCode, T addrMode);
|
||||
|
||||
virtual string GetOpName(uint8_t opcode) = 0;
|
||||
virtual T GetOpMode(uint8_t opcode) = 0;
|
||||
virtual bool IsOfficialOp(uint8_t opcode) = 0;
|
||||
virtual AssemblerSpecialCodes ResolveOpMode(AssemblerLineData& op, uint32_t instructionAddress, bool firstPass) = 0;
|
||||
|
||||
public:
|
||||
Base6502Assembler(LabelManager* labelManager, CpuType cpuType)
|
||||
{
|
||||
_labelManager = labelManager;
|
||||
_cpuType = cpuType;
|
||||
}
|
||||
|
||||
virtual ~Base6502Assembler()
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode)
|
||||
{
|
||||
for(int i = 0; i < 256; i++) {
|
||||
string opName = StringUtilities::ToUpper(GetOpName(i));
|
||||
if(_availableModesByOpName.find(opName) == _availableModesByOpName.end()) {
|
||||
_availableModesByOpName[opName] = unordered_map<T, uint8_t>();
|
||||
}
|
||||
|
||||
T addrMode = GetOpMode(i);
|
||||
if(IsOfficialOp(i) || _availableModesByOpName[opName].find(addrMode) == _availableModesByOpName[opName].end()) {
|
||||
_availableModesByOpName[opName][addrMode] = i;
|
||||
}
|
||||
}
|
||||
|
||||
unordered_map<string, uint32_t> temporaryLabels;
|
||||
unordered_map<string, uint32_t> currentPassLabels;
|
||||
|
||||
vector<int16_t> output;
|
||||
output.reserve(1000);
|
||||
|
||||
uint32_t originalStartAddr = startAddress;
|
||||
|
||||
vector<string> codeLines = StringUtilities::Split(code, '\n');
|
||||
|
||||
//Make 2 passes - first one to find all labels, second to assemble
|
||||
_needSecondPass = false;
|
||||
for(string& line : codeLines) {
|
||||
ProcessLine(line, startAddress, output, temporaryLabels, true, currentPassLabels);
|
||||
}
|
||||
|
||||
if(_needSecondPass) {
|
||||
currentPassLabels.clear();
|
||||
output.clear();
|
||||
startAddress = originalStartAddr;
|
||||
for(string& line : codeLines) {
|
||||
ProcessLine(line, startAddress, output, temporaryLabels, false, currentPassLabels);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(assembledCode, output.data(), std::min<int>(100000, (int)output.size()) * sizeof(uint16_t));
|
||||
return (uint32_t)output.size();
|
||||
}
|
||||
};
|
|
@ -40,7 +40,7 @@ public:
|
|||
void SetLabel(uint32_t address, MemoryType memType, string label, string comment);
|
||||
void ClearLabels();
|
||||
|
||||
int32_t GetLabelRelativeAddress(string &label, CpuType cpuType = CpuType::Snes);
|
||||
int32_t GetLabelRelativeAddress(string &label, CpuType cpuType);
|
||||
|
||||
string GetLabel(AddressInfo address, bool checkRegisterLabels = true);
|
||||
string GetComment(AddressInfo absAddress);
|
||||
|
|
|
@ -220,11 +220,13 @@ int LuaApi::GetLabelAddress(lua_State *lua)
|
|||
errorCond(label.length() == 0, "label cannot be empty");
|
||||
|
||||
LabelManager* lblMan = _debugger->GetLabelManager();
|
||||
int32_t value = lblMan->GetLabelRelativeAddress(label);
|
||||
//TODO hardcoded cpu type
|
||||
int32_t value = lblMan->GetLabelRelativeAddress(label, CpuType::Snes);
|
||||
if(value == -2) {
|
||||
//Check to see if the label is a multi-byte label instead
|
||||
string mbLabel = label + "+0";
|
||||
value = lblMan->GetLabelRelativeAddress(mbLabel);
|
||||
//TODO hardcoded cpu type
|
||||
value = lblMan->GetLabelRelativeAddress(mbLabel, CpuType::Snes);
|
||||
}
|
||||
errorCond(value == -1, "label out of scope (not mapped to CPU memory)");
|
||||
errorCond(value <= -2, "label not found");
|
||||
|
|
|
@ -7,422 +7,123 @@
|
|||
#include "NES/Debugger/NesDisUtils.h"
|
||||
#include "Debugger/LabelManager.h"
|
||||
|
||||
static const std::regex instRegex = std::regex("^\\s*([a-zA-Z]{3})[*]{0,1}[\\s]*(#%|#){0,1}([(]{0,1})[\\s]*([$]{0,1})([^,)(;:]*)[\\s]*((,x\\)|\\),y|,x|,y|\\)){0,1})\\s*(;*)(.*)", std::regex_constants::icase);
|
||||
static const std::regex isCommentOrBlank = std::regex("^\\s*([;]+.*$|\\s*$)", std::regex_constants::icase);
|
||||
static const std::regex labelRegex = std::regex("^\\s*([@_a-zA-Z][@_a-zA-Z0-9]*):(.*)", std::regex_constants::icase);
|
||||
static const std::regex byteRegex = std::regex("^\\s*[.]db\\s+((\\$[a-fA-F0-9]{1,2}[ ])*)(\\$[a-fA-F0-9]{1,2})+\\s*(;*)(.*)$", std::regex_constants::icase);
|
||||
|
||||
void NesAssembler::ProcessLine(string code, uint32_t &instructionAddress, vector<int16_t>& output, std::unordered_map<string, uint16_t> &labels, bool firstPass, std::unordered_map<string, uint16_t> ¤tPassLabels)
|
||||
NesAssembler::NesAssembler(LabelManager* labelManager) : Base6502Assembler<NesAddrMode>(labelManager, CpuType::Nes)
|
||||
{
|
||||
//Remove extra spaces as part of processing
|
||||
size_t offset = code.find_first_of(',', 0);
|
||||
if(offset != string::npos) {
|
||||
code.erase(std::remove(code.begin() + offset + 1, code.end(), ' '), code.end());
|
||||
}
|
||||
|
||||
string NesAssembler::GetOpName(uint8_t opcode)
|
||||
{
|
||||
return NesDisUtils::GetOpName(opcode);
|
||||
}
|
||||
|
||||
bool NesAssembler::IsOfficialOp(uint8_t opcode)
|
||||
{
|
||||
return !NesDisUtils::IsOpUnofficial(opcode);
|
||||
}
|
||||
|
||||
NesAddrMode NesAssembler::GetOpMode(uint8_t opcode)
|
||||
{
|
||||
NesAddrMode mode = NesDisUtils::GetOpMode(opcode);
|
||||
switch(mode) {
|
||||
case NesAddrMode::AbsXW: return NesAddrMode::AbsX;
|
||||
case NesAddrMode::AbsYW: return NesAddrMode::AbsY;
|
||||
case NesAddrMode::IndYW: return NesAddrMode::IndY;
|
||||
default: return mode;
|
||||
}
|
||||
offset = code.find_first_of(')', 0);
|
||||
if(offset != string::npos) {
|
||||
code.erase(std::remove(code.begin() + offset + 1, code.end(), ' '), code.end());
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes NesAssembler::ResolveOpMode(AssemblerLineData& op, uint32_t instructionAddress, bool firstPass)
|
||||
{
|
||||
if(op.OperandCount > 2) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
} else if(op.OperandCount == 2 && op.Operands[1].Type != OperandType::X && op.Operands[1].Type != OperandType::Y) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
|
||||
//Determine if the line is blank, a comment, a label or code
|
||||
std::smatch match;
|
||||
if(std::regex_search(code, match, byteRegex)) {
|
||||
vector<string> bytes = StringUtilities::Split(match.str(1) + match.str(3), ' ');
|
||||
for(string &byte : bytes) {
|
||||
output.push_back((uint8_t)(HexUtilities::FromHex(byte.substr(1))));
|
||||
instructionAddress++;
|
||||
AssemblerOperand& operand = op.Operands[0];
|
||||
AssemblerOperand& operand2 = op.Operands[1];
|
||||
if(operand.ByteCount > 2 || operand2.ByteCount > 0) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
|
||||
if(operand.IsImmediate) {
|
||||
if(operand.HasParenOrBracket() || operand.ByteCount == 0 || op.OperandCount > 1) {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
} else if(operand.ByteCount > 1) {
|
||||
//Can't be a 2-byte operand
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
} else if(std::regex_search(code, match, labelRegex)) {
|
||||
string label = match.str(1);
|
||||
string afterLabel = match.str(2);
|
||||
if(currentPassLabels.find(match.str(1)) != currentPassLabels.end()) {
|
||||
output.push_back(AssemblerSpecialCodes::LabelRedefinition);
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, NesAddrMode::Rel) ? NesAddrMode::Rel : NesAddrMode::Imm;
|
||||
} else if(operand.HasOpeningParenthesis) {
|
||||
if(operand2.Type == OperandType::X && operand2.HasClosingParenthesis && operand.ByteCount == 1) {
|
||||
op.AddrMode = NesAddrMode::IndX;
|
||||
} else if(operand.HasClosingParenthesis && operand2.Type == OperandType::Y && operand.ByteCount == 1) {
|
||||
op.AddrMode = NesAddrMode::IndY;
|
||||
} else if(operand.HasClosingParenthesis && operand.ByteCount > 0) {
|
||||
op.AddrMode = NesAddrMode::Ind;
|
||||
} else {
|
||||
labels[match.str(1)] = instructionAddress;
|
||||
currentPassLabels[match.str(1)] = instructionAddress;
|
||||
ProcessLine(afterLabel, instructionAddress, output, labels, firstPass, currentPassLabels);
|
||||
}
|
||||
return;
|
||||
} else if(std::regex_search(code, match, isCommentOrBlank)) {
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
return;
|
||||
} else if(std::regex_search(code, match, instRegex) && match.size() > 1) {
|
||||
NesLineData lineData = {};
|
||||
AssemblerSpecialCodes result = GetLineData(match, lineData, labels, firstPass);
|
||||
if(result == AssemblerSpecialCodes::OK) {
|
||||
AssembleInstruction(lineData, instructionAddress, output, firstPass);
|
||||
} else {
|
||||
output.push_back(result);
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
} else if(operand.HasParenOrBracket() || operand2.HasParenOrBracket()) {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
} else {
|
||||
output.push_back(AssemblerSpecialCodes::ParsingError);
|
||||
}
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes NesAssembler::GetLineData(std::smatch match, NesLineData& lineData, unordered_map<string, uint16_t> &labels, bool firstPass)
|
||||
{
|
||||
bool isBinary = match.str(2).length() > 1 && match.str(2)[1] == '%'; //Immediate + binary: "#%"
|
||||
|
||||
lineData.OpCode = match.str(1);
|
||||
lineData.IsImmediate = !match.str(2).empty();
|
||||
lineData.IsHex = !match.str(4).empty();
|
||||
lineData.HasComment = !match.str(8).empty();
|
||||
lineData.OperandSuffix = match.str(6);
|
||||
lineData.HasOpeningParenthesis = !match.str(3).empty();
|
||||
|
||||
std::transform(lineData.OperandSuffix.begin(), lineData.OperandSuffix.end(), lineData.OperandSuffix.begin(), ::toupper);
|
||||
std::transform(lineData.OpCode.begin(), lineData.OpCode.end(), lineData.OpCode.begin(), ::toupper);
|
||||
|
||||
bool foundSpace = false;
|
||||
for(char c : match.str(5)) {
|
||||
if(c != ' ' && c != '\t') {
|
||||
if(foundSpace) {
|
||||
//can't have spaces in operands (except at the very end)
|
||||
return AssemblerSpecialCodes::InvalidSpaces;
|
||||
} else if(lineData.IsHex && !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) {
|
||||
//invalid hex
|
||||
return AssemblerSpecialCodes::InvalidHex;
|
||||
} else if(isBinary && c != '0' && c != '1') {
|
||||
return AssemblerSpecialCodes::InvalidBinaryValue;
|
||||
}
|
||||
|
||||
lineData.Operand.push_back(c);
|
||||
} else {
|
||||
foundSpace = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(isBinary) {
|
||||
//Convert the binary value to hex
|
||||
if(lineData.Operand.size() == 0) {
|
||||
return AssemblerSpecialCodes::MissingOperand;
|
||||
} else if(lineData.Operand.size() <= 8) {
|
||||
lineData.IsHex = true;
|
||||
int value = 0;
|
||||
for(size_t i = 0; i < lineData.Operand.size(); i++) {
|
||||
value <<= 1;
|
||||
value |= lineData.Operand[i] == '1' ? 1 : 0;
|
||||
}
|
||||
lineData.Operand = HexUtilities::ToHex(value, false);
|
||||
} else {
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
}
|
||||
|
||||
if(!lineData.HasComment && !match.str(9).empty()) {
|
||||
//something is trailing at the end of the line, and it's not a comment
|
||||
return AssemblerSpecialCodes::TrailingText;
|
||||
}
|
||||
|
||||
if(!lineData.IsHex) {
|
||||
bool allNumeric = true;
|
||||
for(size_t i = 0; i < lineData.Operand.size(); i++) {
|
||||
if(lineData.Operand[i] == '-' && i == 0 && lineData.Operand.size() > 1) {
|
||||
//First char is a minus sign, and more characters follow, continue
|
||||
continue;
|
||||
}
|
||||
|
||||
if((lineData.Operand[i] < '0' || lineData.Operand[i] > '9')) {
|
||||
allNumeric = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allNumeric && !lineData.Operand.empty()) {
|
||||
//Operand is not empty, and it only contains decimal values
|
||||
lineData.IsDecimal = true;
|
||||
} else {
|
||||
lineData.IsDecimal = false;
|
||||
}
|
||||
}
|
||||
|
||||
return GetAddrModeAndOperandSize(lineData, labels, firstPass);
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes NesAssembler::GetAddrModeAndOperandSize(NesLineData& lineData, unordered_map<string, uint16_t> &labels, bool firstPass)
|
||||
{
|
||||
int opSize = 0;
|
||||
bool invalid = false;
|
||||
string operand = lineData.Operand;
|
||||
|
||||
if(lineData.IsHex) {
|
||||
if(operand.size() == 0) {
|
||||
return AssemblerSpecialCodes::MissingOperand;
|
||||
} else if(operand.size() <= 2) {
|
||||
opSize = 1;
|
||||
} else if(operand.size() <= 4) {
|
||||
opSize = 2;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(lineData.IsDecimal) {
|
||||
int value = std::stoi(operand.c_str());
|
||||
if(value < -32768) {
|
||||
//< -32768 is invalid
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
} else if(value < -128) {
|
||||
//-32768 to -129 is 2 bytes
|
||||
opSize = 2;
|
||||
} else if(value <= 255) {
|
||||
//-128 to 255 is 2 bytes
|
||||
opSize = 1;
|
||||
} else if(value <= 65535) {
|
||||
//256 to 65535 is 2 bytes
|
||||
opSize = 2;
|
||||
} else {
|
||||
//> 65535 is invalid
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(!operand.empty()) {
|
||||
//Check if the operand is a known label
|
||||
auto findResult = labels.find(operand);
|
||||
if(findResult != labels.end()) {
|
||||
lineData.Operand = HexUtilities::ToHex((uint16_t)findResult->second);
|
||||
lineData.IsHex = true;
|
||||
opSize = 2;
|
||||
} else if(operand.size() == 1 && (operand[0] == 'A' || operand[0] == 'a') && lineData.OperandSuffix.empty() && !lineData.IsHex && !lineData.IsImmediate && !lineData.HasOpeningParenthesis) {
|
||||
//Allow optional "A" after AddrMode == Accumulator instructions
|
||||
lineData.Mode = NesAddrMode::Acc;
|
||||
opSize = 0;
|
||||
} else {
|
||||
int32_t addr = _labelManager->GetLabelRelativeAddress(operand);
|
||||
if(addr >= 256) {
|
||||
lineData.Operand = HexUtilities::ToHex((uint16_t)addr);
|
||||
lineData.IsHex = true;
|
||||
opSize = 2;
|
||||
} else if(addr >= 0) {
|
||||
lineData.Operand = HexUtilities::ToHex((uint8_t)addr);
|
||||
lineData.IsHex = true;
|
||||
opSize = 1;
|
||||
if(operand2.Type == OperandType::X) {
|
||||
if(operand.ByteCount == 2) {
|
||||
op.AddrMode = NesAddrMode::AbsX;
|
||||
} else if(operand.ByteCount == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, NesAddrMode::ZeroX) ? NesAddrMode::ZeroX : NesAddrMode::AbsX;
|
||||
} else {
|
||||
if(firstPass) {
|
||||
//First pass, we couldn't find a matching label, so it might be defined later on
|
||||
//Pretend it exists for now
|
||||
_needSecondPass = true;
|
||||
lineData.Operand = "FFFF";
|
||||
lineData.IsHex = true;
|
||||
opSize = 2;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::UnknownLabel;
|
||||
}
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//No operand
|
||||
opSize = 0;
|
||||
}
|
||||
|
||||
if(lineData.Mode == NesAddrMode::None) {
|
||||
if(lineData.IsImmediate) {
|
||||
if(lineData.HasOpeningParenthesis || opSize == 0) {
|
||||
invalid = true;
|
||||
} else if(opSize > 1) {
|
||||
if(lineData.IsHex && HexUtilities::FromHex(operand) > 0xFF) {
|
||||
//Can't be a 2-byte operand
|
||||
invalid = true;
|
||||
} else if(lineData.IsDecimal) {
|
||||
int value = std::stoi(operand.c_str());
|
||||
if(value < -128 || value > 255) {
|
||||
//Can't be a 2-byte operand
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
opSize = 1;
|
||||
}
|
||||
lineData.Mode = NesAddrMode::Imm; //or Rel
|
||||
} else if(lineData.HasOpeningParenthesis) {
|
||||
if(lineData.OperandSuffix.compare(")") == 0) {
|
||||
opSize = 2;
|
||||
lineData.Mode = NesAddrMode::Ind;
|
||||
} else if(lineData.OperandSuffix.compare(",X)") == 0) {
|
||||
opSize = 1;
|
||||
lineData.Mode = NesAddrMode::IndX;
|
||||
} else if(lineData.OperandSuffix.compare("),Y") == 0) {
|
||||
opSize = 1;
|
||||
lineData.Mode = NesAddrMode::IndY;
|
||||
} else if(operand2.Type == OperandType::Y) {
|
||||
if(operand.ByteCount == 2) {
|
||||
op.AddrMode = NesAddrMode::AbsY;
|
||||
} else if(operand.ByteCount == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, NesAddrMode::ZeroY) ? NesAddrMode::ZeroY : NesAddrMode::AbsY;
|
||||
} else {
|
||||
invalid = true;
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
} else {
|
||||
if(lineData.OperandSuffix.compare(",X") == 0) {
|
||||
if(opSize == 2) {
|
||||
lineData.Mode = NesAddrMode::AbsX;
|
||||
} else if(opSize == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
lineData.Mode = IsOpModeAvailable(lineData.OpCode, NesAddrMode::ZeroX) ? NesAddrMode::ZeroX : NesAddrMode::AbsX;
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
} else if(lineData.OperandSuffix.compare(",Y") == 0) {
|
||||
if(opSize == 2) {
|
||||
lineData.Mode = NesAddrMode::AbsY;
|
||||
} else if(opSize == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
lineData.Mode = IsOpModeAvailable(lineData.OpCode, NesAddrMode::ZeroY) ? NesAddrMode::ZeroY : NesAddrMode::AbsY;
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
} else if(lineData.OperandSuffix.empty()) {
|
||||
if(opSize == 0) {
|
||||
lineData.Mode = NesAddrMode::Imp; //or Acc
|
||||
} else if(opSize == 2) {
|
||||
lineData.Mode = NesAddrMode::Abs;
|
||||
} else if(opSize == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
lineData.Mode = IsOpModeAvailable(lineData.OpCode, NesAddrMode::Zero) ? NesAddrMode::Zero : NesAddrMode::Abs;
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(operand.Type == OperandType::A) {
|
||||
op.AddrMode = NesAddrMode::Acc;
|
||||
} else if(op.OperandCount == 0) {
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, NesAddrMode::Acc) ? NesAddrMode::Acc : NesAddrMode::Imp;
|
||||
} else if(op.OperandCount == 1) {
|
||||
if(operand.ByteCount == 2) {
|
||||
if(IsOpModeAvailable(op.OpCode, NesAddrMode::Rel)) {
|
||||
op.AddrMode = NesAddrMode::Rel;
|
||||
|
||||
if(lineData.Mode == NesAddrMode::None) {
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
lineData.OperandSize = opSize;
|
||||
|
||||
return invalid ? AssemblerSpecialCodes::ParsingError : AssemblerSpecialCodes::OK;
|
||||
}
|
||||
|
||||
bool NesAssembler::IsOpModeAvailable(string &opCode, NesAddrMode mode)
|
||||
{
|
||||
return _availableModesByOpName[opCode].find((int)mode) != _availableModesByOpName[opCode].end();
|
||||
}
|
||||
|
||||
void NesAssembler::AssembleInstruction(NesLineData& lineData, uint32_t &instructionAddress, vector<int16_t>& output, bool firstPass)
|
||||
{
|
||||
bool foundMatch = false;
|
||||
if(lineData.Mode == NesAddrMode::Imp && lineData.OpCode.compare("NOP") == 0) {
|
||||
//NOP has multiple name+addressing type collisions, the "official" NOP is 0xEA
|
||||
output.push_back(0xEA);
|
||||
instructionAddress++;
|
||||
foundMatch = true;
|
||||
} else {
|
||||
for(int i = 0; i < 256; i++) {
|
||||
NesAddrMode opMode = NesDisUtils::GetOpMode(i);
|
||||
if(lineData.OpCode.compare(NesDisUtils::GetOpName(i)) == 0) {
|
||||
bool modeMatch = opMode == lineData.Mode;
|
||||
if(!modeMatch) {
|
||||
if((lineData.Mode == NesAddrMode::Imp && opMode == NesAddrMode::Acc) ||
|
||||
(lineData.Mode == NesAddrMode::IndY && opMode == NesAddrMode::IndYW) ||
|
||||
(lineData.Mode == NesAddrMode::AbsY && opMode == NesAddrMode::AbsYW) ||
|
||||
(lineData.Mode == NesAddrMode::AbsX && opMode == NesAddrMode::AbsXW)) {
|
||||
modeMatch = true;
|
||||
} else if((lineData.Mode == NesAddrMode::Abs && opMode == NesAddrMode::Rel) ||
|
||||
(lineData.Mode == NesAddrMode::Imm && opMode == NesAddrMode::Rel)) {
|
||||
if(lineData.OperandSize == 2) {
|
||||
if(lineData.Mode == NesAddrMode::Imm) {
|
||||
//Hardcoded jump values must be 1-byte
|
||||
if(firstPass) {
|
||||
//Pretend this is ok on first pass, we're just trying to find all labels
|
||||
lineData.OperandSize = 1;
|
||||
lineData.IsHex = true;
|
||||
lineData.Operand = "0";
|
||||
modeMatch = true;
|
||||
} else {
|
||||
output.push_back(AssemblerSpecialCodes::OutOfRangeJump);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
modeMatch = true;
|
||||
|
||||
//Convert "absolute" jump to a relative jump
|
||||
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
|
||||
|
||||
int16_t addressGap = value - (instructionAddress + 2);
|
||||
if(addressGap > 127 || addressGap < -128) {
|
||||
//Gap too long, can't jump that far
|
||||
if(!firstPass) {
|
||||
//Pretend this is ok on first pass, we're just trying to find all labels
|
||||
output.push_back(AssemblerSpecialCodes::OutOfRangeJump);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Update data to match relative jump
|
||||
lineData.OperandSize = 1;
|
||||
lineData.IsHex = true;
|
||||
lineData.Operand = HexUtilities::ToHex((uint8_t)addressGap);
|
||||
}
|
||||
} else {
|
||||
//Accept 1-byte relative jumps
|
||||
modeMatch = true;
|
||||
//Convert "absolute" jump to a relative jump
|
||||
int16_t addressGap = operand.Value - (instructionAddress + 2);
|
||||
if(addressGap > 127 || addressGap < -128) {
|
||||
//Gap too long, can't jump that far
|
||||
if(!firstPass) {
|
||||
//Pretend this is ok on first pass, we're just trying to find all labels
|
||||
return AssemblerSpecialCodes::OutOfRangeJump;
|
||||
}
|
||||
}
|
||||
|
||||
//Update data to match relative jump
|
||||
operand.ByteCount = 1;
|
||||
operand.Value = (uint8_t)addressGap;
|
||||
} else {
|
||||
op.AddrMode = NesAddrMode::Abs;
|
||||
}
|
||||
|
||||
if(modeMatch) {
|
||||
output.push_back(i);
|
||||
instructionAddress += (lineData.OperandSize + 1);
|
||||
|
||||
if(lineData.OperandSize == 1) {
|
||||
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
|
||||
output.push_back(value & 0xFF);
|
||||
} else if(lineData.OperandSize == 2) {
|
||||
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
|
||||
output.push_back(value & 0xFF);
|
||||
output.push_back((value >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
foundMatch = true;
|
||||
break;
|
||||
} else if(operand.ByteCount == 1) {
|
||||
if(IsOpModeAvailable(op.OpCode, NesAddrMode::Rel)) {
|
||||
op.AddrMode = NesAddrMode::Rel;
|
||||
} else {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, NesAddrMode::Zero) ? NesAddrMode::Zero : NesAddrMode::Abs;
|
||||
}
|
||||
} else {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
} else {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
}
|
||||
|
||||
if(!foundMatch) {
|
||||
output.push_back(AssemblerSpecialCodes::InvalidInstruction);
|
||||
} else {
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
}
|
||||
}
|
||||
|
||||
NesAssembler::NesAssembler(LabelManager* labelManager)
|
||||
{
|
||||
_labelManager = labelManager;
|
||||
}
|
||||
|
||||
uint32_t NesAssembler::AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode)
|
||||
{
|
||||
for(uint8_t i = 0; i < 255; i++) {
|
||||
string opName = NesDisUtils::GetOpName(i);
|
||||
if(_availableModesByOpName.find(opName) == _availableModesByOpName.end()) {
|
||||
_availableModesByOpName[opName] = std::unordered_set<int>();
|
||||
}
|
||||
_availableModesByOpName[opName].emplace((int)NesDisUtils::GetOpMode(i));
|
||||
}
|
||||
|
||||
std::unordered_map<string, uint16_t> temporaryLabels;
|
||||
std::unordered_map<string, uint16_t> currentPassLabels;
|
||||
|
||||
vector<int16_t> output;
|
||||
output.reserve(1000);
|
||||
|
||||
uint16_t originalStartAddr = startAddress;
|
||||
|
||||
vector<string> codeLines = StringUtilities::Split(code, '\n');
|
||||
|
||||
//Make 2 passes - first one to find all labels, second to assemble
|
||||
_needSecondPass = false;
|
||||
for(string &line : codeLines) {
|
||||
ProcessLine(line, startAddress, output, temporaryLabels, true, currentPassLabels);
|
||||
}
|
||||
|
||||
if(_needSecondPass) {
|
||||
currentPassLabels.clear();
|
||||
output.clear();
|
||||
startAddress = originalStartAddr;
|
||||
for(string &line : codeLines) {
|
||||
ProcessLine(line, startAddress, output, temporaryLabels, false, currentPassLabels);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(assembledCode, output.data(), std::min<int>(100000, (int)output.size()) * sizeof(uint16_t));
|
||||
return (uint32_t)output.size();
|
||||
return AssemblerSpecialCodes::OK;
|
||||
}
|
||||
|
|
|
@ -1,42 +1,20 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <regex>
|
||||
#include "Debugger/IAssembler.h"
|
||||
#include "Debugger/Base6502Assembler.h"
|
||||
#include "NES/NesTypes.h"
|
||||
|
||||
class LabelManager;
|
||||
|
||||
struct NesLineData
|
||||
{
|
||||
string OpCode;
|
||||
string Operand;
|
||||
string OperandSuffix;
|
||||
NesAddrMode Mode = NesAddrMode::None;
|
||||
int OperandSize = 0;
|
||||
bool IsHex = false;
|
||||
bool IsDecimal = false;
|
||||
bool IsImmediate = false;
|
||||
bool HasComment = false;
|
||||
bool HasOpeningParenthesis = false;
|
||||
};
|
||||
|
||||
class NesAssembler final : public IAssembler
|
||||
class NesAssembler final : public Base6502Assembler<NesAddrMode>
|
||||
{
|
||||
private:
|
||||
std::unordered_map<string, unordered_set<int>> _availableModesByOpName;
|
||||
bool _needSecondPass = false;
|
||||
|
||||
LabelManager* _labelManager;
|
||||
void ProcessLine(string code, uint32_t &instructionAddress, vector<int16_t>& output, unordered_map<string, uint16_t> &labels, bool firstPass, unordered_map<string, uint16_t> ¤tPassLabels);
|
||||
AssemblerSpecialCodes GetLineData(std::smatch match, NesLineData& lineData, unordered_map<string, uint16_t> &labels, bool firstPass);
|
||||
AssemblerSpecialCodes GetAddrModeAndOperandSize(NesLineData& lineData, unordered_map<string, uint16_t> &labels, bool firstPass);
|
||||
void AssembleInstruction(NesLineData& lineData, uint32_t &instructionAddress, vector<int16_t>& output, bool firstPass);
|
||||
|
||||
bool IsOpModeAvailable(string &opCode, NesAddrMode mode);
|
||||
string GetOpName(uint8_t opcode) override;
|
||||
bool IsOfficialOp(uint8_t opcode) override;
|
||||
NesAddrMode GetOpMode(uint8_t opcode) override;
|
||||
AssemblerSpecialCodes ResolveOpMode(AssemblerLineData& op, uint32_t instructionAddress, bool firstPass) override;
|
||||
|
||||
public:
|
||||
NesAssembler(LabelManager* labelManager);
|
||||
virtual ~NesAssembler() = default;
|
||||
|
||||
uint32_t AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode);
|
||||
};
|
193
Core/PCE/Debugger/PceAssembler.cpp
Normal file
193
Core/PCE/Debugger/PceAssembler.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
#include "stdafx.h"
|
||||
#include <regex>
|
||||
#include <unordered_map>
|
||||
#include "Utilities/HexUtilities.h"
|
||||
#include "Utilities/StringUtilities.h"
|
||||
#include "PCE/Debugger/PceAssembler.h"
|
||||
#include "PCE/Debugger/PceDisUtils.h"
|
||||
#include "Debugger/LabelManager.h"
|
||||
|
||||
PceAssembler::PceAssembler(LabelManager* labelManager) : Base6502Assembler<PceAddrMode>(labelManager, CpuType::Pce)
|
||||
{
|
||||
}
|
||||
|
||||
string PceAssembler::GetOpName(uint8_t opcode)
|
||||
{
|
||||
return PceDisUtils::GetOpName(opcode);
|
||||
}
|
||||
|
||||
PceAddrMode PceAssembler::GetOpMode(uint8_t opcode)
|
||||
{
|
||||
return PceDisUtils::GetOpMode(opcode);
|
||||
}
|
||||
|
||||
bool PceAssembler::IsOfficialOp(uint8_t opcode)
|
||||
{
|
||||
//Not implemented, but not necessary for PCE assembler
|
||||
return true;
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes PceAssembler::ResolveOpMode(AssemblerLineData& op, uint32_t instructionAddress, bool firstPass)
|
||||
{
|
||||
AssemblerOperand& operand = op.Operands[0];
|
||||
AssemblerOperand& operand2 = op.Operands[1];
|
||||
AssemblerOperand& operand3 = op.Operands[2];
|
||||
if(operand.ByteCount > 2 || operand2.ByteCount > 2 || operand3.ByteCount > 2) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
|
||||
if(operand3.Type != OperandType::None) {
|
||||
if(operand3.ByteCount > 0) {
|
||||
//Block transfer
|
||||
if(operand.ByteCount >= 1 && operand.ByteCount <= 2 && operand2.ByteCount >= 1 && operand2.ByteCount <= 2 && operand3.ByteCount >= 1 && operand3.ByteCount <= 2) {
|
||||
if(operand.IsImmediate || operand.HasParenOrBracket() || operand2.IsImmediate || operand2.HasParenOrBracket() || !operand3.IsImmediate || operand3.HasParenOrBracket()) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
} else {
|
||||
op.AddrMode = PceAddrMode::Block;
|
||||
}
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
} else {
|
||||
//ImZeroX, ImAbsX
|
||||
if(!operand.IsImmediate || operand.ByteCount != 1 || operand3.Type != OperandType::X) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
|
||||
if(operand2.ByteCount == 1) {
|
||||
op.AddrMode = PceAddrMode::ImZeroX;
|
||||
} else if(operand2.ByteCount == 2) {
|
||||
op.AddrMode = PceAddrMode::ImAbsX;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
}
|
||||
} else if(operand2.Type == OperandType::Custom) {
|
||||
//ImZero, ImAbs, ZeroRel
|
||||
if(!operand.IsImmediate && operand.ByteCount == 1) {
|
||||
if(operand2.ByteCount == 2) {
|
||||
int16_t addressGap = operand2.Value - (instructionAddress + 3);
|
||||
if(addressGap > 127 || addressGap < -128) {
|
||||
//Gap too long, can't jump that far
|
||||
if(!firstPass) {
|
||||
//Pretend this is ok on first pass, we're just trying to find all labels
|
||||
return AssemblerSpecialCodes::OutOfRangeJump;
|
||||
}
|
||||
}
|
||||
|
||||
//Update data to match relative jump
|
||||
operand2.ByteCount = 1;
|
||||
operand2.Value = (uint8_t)addressGap;
|
||||
op.AddrMode = PceAddrMode::ZeroRel;
|
||||
} else if(operand2.ByteCount == 1) {
|
||||
op.AddrMode = PceAddrMode::ZeroRel;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
} else {
|
||||
if(!operand.IsImmediate || operand.ByteCount != 1) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
|
||||
if(operand2.ByteCount == 1) {
|
||||
op.AddrMode = PceAddrMode::ImZero;
|
||||
} else if(operand2.ByteCount == 2) {
|
||||
op.AddrMode = PceAddrMode::ImAbs;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
}
|
||||
} else if(operand.IsImmediate) {
|
||||
if(operand.HasOpeningParenthesis || operand.ByteCount == 0 || op.OperandCount > 1) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
} else if(operand.ByteCount > 1) {
|
||||
//Can't be a 2-byte operand
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, PceAddrMode::Rel) ? PceAddrMode::Rel : PceAddrMode::Imm;
|
||||
} else if(operand.HasOpeningParenthesis) {
|
||||
//Ind, IndX, IndY, AbsXInd, ZInd
|
||||
if(operand2.Type == OperandType::X && operand2.HasClosingParenthesis) {
|
||||
if(operand.ByteCount == 2) {
|
||||
op.AddrMode = PceAddrMode::AbsXInd;
|
||||
} else if(operand.ByteCount == 1) {
|
||||
op.AddrMode = PceAddrMode::IndX;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
} else if(operand.HasClosingParenthesis && operand2.Type == OperandType::Y) {
|
||||
op.AddrMode = PceAddrMode::IndY;
|
||||
} else if(operand.HasClosingParenthesis) {
|
||||
if(operand.ByteCount == 2) {
|
||||
op.AddrMode = PceAddrMode::Ind;
|
||||
} else if(operand.ByteCount == 1) {
|
||||
op.AddrMode = PceAddrMode::ZInd;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
} else if(operand.HasParenOrBracket() || operand2.HasParenOrBracket()) {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
} else {
|
||||
if(operand2.Type == OperandType::X) {
|
||||
if(operand.ByteCount == 2) {
|
||||
op.AddrMode = PceAddrMode::AbsX;
|
||||
} else if(operand.ByteCount == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, PceAddrMode::ZeroX) ? PceAddrMode::ZeroX : PceAddrMode::AbsX;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
} else if(operand2.Type == OperandType::Y) {
|
||||
if(operand.ByteCount == 2) {
|
||||
op.AddrMode = PceAddrMode::AbsY;
|
||||
} else if(operand.ByteCount == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, PceAddrMode::ZeroY) ? PceAddrMode::ZeroY : PceAddrMode::AbsY;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
} else if(operand.Type == OperandType::A) {
|
||||
op.AddrMode = PceAddrMode::Acc;
|
||||
} else if(op.OperandCount == 0) {
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, PceAddrMode::Acc) ? PceAddrMode::Acc : PceAddrMode::Imp;
|
||||
} else if(op.OperandCount == 1) {
|
||||
if(operand.ByteCount == 2) {
|
||||
if(IsOpModeAvailable(op.OpCode, PceAddrMode::Rel)) {
|
||||
op.AddrMode = PceAddrMode::Rel;
|
||||
|
||||
//Convert "absolute" jump to a relative jump
|
||||
int16_t addressGap = operand.Value - (instructionAddress + 2);
|
||||
if(addressGap > 127 || addressGap < -128) {
|
||||
//Gap too long, can't jump that far
|
||||
if(!firstPass) {
|
||||
//Pretend this is ok on first pass, we're just trying to find all labels
|
||||
return AssemblerSpecialCodes::OutOfRangeJump;
|
||||
}
|
||||
}
|
||||
|
||||
//Update data to match relative jump
|
||||
operand.ByteCount = 1;
|
||||
operand.Value = (uint8_t)addressGap;
|
||||
} else {
|
||||
op.AddrMode = PceAddrMode::Abs;
|
||||
}
|
||||
} else if(operand.ByteCount == 1) {
|
||||
if(IsOpModeAvailable(op.OpCode, PceAddrMode::Rel)) {
|
||||
op.AddrMode = PceAddrMode::Rel;
|
||||
} else {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, PceAddrMode::Zero) ? PceAddrMode::Zero : PceAddrMode::Abs;
|
||||
}
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
} else {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
}
|
||||
|
||||
return AssemblerSpecialCodes::OK;
|
||||
}
|
20
Core/PCE/Debugger/PceAssembler.h
Normal file
20
Core/PCE/Debugger/PceAssembler.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <regex>
|
||||
#include "Debugger/Base6502Assembler.h"
|
||||
#include "PCE/PceTypes.h"
|
||||
|
||||
class LabelManager;
|
||||
|
||||
class PceAssembler final : public Base6502Assembler<PceAddrMode>
|
||||
{
|
||||
private:
|
||||
string GetOpName(uint8_t opcode) override;
|
||||
PceAddrMode GetOpMode(uint8_t opcode) override;
|
||||
bool IsOfficialOp(uint8_t opcode) override;
|
||||
AssemblerSpecialCodes ResolveOpMode(AssemblerLineData& op, uint32_t instructionAddress, bool firstPass) override;
|
||||
|
||||
public:
|
||||
PceAssembler(LabelManager* labelManager);
|
||||
virtual ~PceAssembler() = default;
|
||||
};
|
|
@ -20,6 +20,7 @@
|
|||
#include "PCE/Debugger/PceDisUtils.h"
|
||||
#include "PCE/Debugger/DummyPceCpu.h"
|
||||
#include "PCE/Debugger/PceEventManager.h"
|
||||
#include "PCE/Debugger/PceAssembler.h"
|
||||
#include "Utilities/HexUtilities.h"
|
||||
#include "Utilities/FolderUtilities.h"
|
||||
#include "Utilities/Patches/IpsPatcher.h"
|
||||
|
@ -53,7 +54,8 @@ PceDebugger::PceDebugger(Debugger* debugger)
|
|||
_callstackManager.reset(new CallstackManager(debugger));
|
||||
_breakpointManager.reset(new BreakpointManager(debugger, this, CpuType::Pce, _eventManager.get()));
|
||||
_step.reset(new StepRequest());
|
||||
|
||||
_assembler.reset(new PceAssembler(_debugger->GetLabelManager()));
|
||||
|
||||
if(_console->GetMasterClock() < 1000) {
|
||||
//Enable breaking on uninit reads when debugger is opened at power on
|
||||
_enableBreakOnUninitRead = true;
|
||||
|
@ -330,8 +332,7 @@ BreakpointManager* PceDebugger::GetBreakpointManager()
|
|||
|
||||
IAssembler* PceDebugger::GetAssembler()
|
||||
{
|
||||
//todo
|
||||
return nullptr;// _assembler.get();
|
||||
return _assembler.get();
|
||||
}
|
||||
|
||||
BaseEventManager* PceDebugger::GetEventManager()
|
||||
|
|
|
@ -40,7 +40,7 @@ class PceDebugger final : public IDebugger
|
|||
|
||||
unique_ptr<CodeDataLogger> _codeDataLogger;
|
||||
unique_ptr<BaseEventManager> _eventManager;
|
||||
//unique_ptr<IAssembler> _assembler;
|
||||
unique_ptr<IAssembler> _assembler;
|
||||
unique_ptr<CallstackManager> _callstackManager;
|
||||
unique_ptr<BreakpointManager> _breakpointManager;
|
||||
unique_ptr<PceTraceLogger> _traceLogger;
|
||||
|
|
|
@ -24,22 +24,22 @@ static constexpr uint8_t _opSize[24] = {
|
|||
|
||||
static constexpr const char* _opName[256] = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
"BRK", "ORA", "SXY", "ST0", "TSB", "ORA", "ASL", "RMB0", "PHP", "ORA", "ASL", "NOP*", "TSB", "ORA", "ASL", "BBR0", //0
|
||||
"BPL", "ORA", "ORA", "ST1", "TRB", "ORA", "ASL", "RMB1", "CLC", "ORA", "INC", "NOP*", "TRB", "ORA", "ASL", "BBR1", //1
|
||||
"JSR", "AND", "SAX", "ST2", "BIT", "AND", "ROL", "RMB2", "PLP", "AND", "ROL", "NOP*", "BIT", "AND", "ROL", "BBR2", //2
|
||||
"BMI", "AND", "AND", "NOP*", "BIT", "AND", "ROL", "RMB3", "SEC", "AND", "DEC", "NOP*", "BIT", "AND", "ROL", "BBR3", //3
|
||||
"RTI", "EOR", "SAY", "TMA", "BSR", "EOR", "LSR", "RMB4", "PHA", "EOR", "LSR", "NOP*", "JMP", "EOR", "LSR", "BBR4", //4
|
||||
"BVC", "EOR", "EOR", "TAM", "CSL", "EOR", "LSR", "RMB5", "CLI", "EOR", "PHY", "NOP*", "NOP*", "EOR", "LSR", "BBR5", //5
|
||||
"RTS", "ADC", "CLA", "NOP*", "STZ", "ADC", "ROR", "RMB6", "PLA", "ADC", "ROR", "NOP*", "JMP", "ADC", "ROR", "BBR6", //6
|
||||
"BVS", "ADC", "ADC", "TII", "STZ", "ADC", "ROR", "RMB7", "SEI", "ADC", "PLY", "NOP*", "JMP", "ADC", "ROR", "BBR7", //7
|
||||
"BRA", "STA", "CLX", "TST", "STY", "STA", "STX", "SMB0", "DEY", "BIT", "TXA", "NOP*", "STY", "STA", "STX", "BBS0", //8
|
||||
"BCC", "STA", "STA", "TST", "STY", "STA", "STX", "SMB1", "TYA", "STA", "TXS", "NOP*", "STZ", "STA", "STZ", "BBS1", //9
|
||||
"LDY", "LDA", "LDX", "TST", "LDY", "LDA", "LDX", "SMB2", "TAY", "LDA", "TAX", "NOP*", "LDY", "LDA", "LDX", "BBS2", //A
|
||||
"BCS", "LDA", "LDA", "TST", "LDY", "LDA", "LDX", "SMB3", "CLV", "LDA", "TSX", "NOP*", "LDY", "LDA", "LDX", "BBS3", //B
|
||||
"CPY", "CMP", "CLY", "TDD", "CPY", "CMP", "DEC", "SMB4", "INY", "CMP", "DEX", "NOP*", "CPY", "CMP", "DEC", "BBS4", //C
|
||||
"BNE", "CMP", "CMP", "TIN", "CSH", "CMP", "DEC", "SMB5", "CLD", "CMP", "PHX", "NOP*", "NOP*", "CMP", "DEC", "BBS5", //D
|
||||
"CPX", "SBC", "NOP*", "TIA", "CPX", "SBC", "INC", "SMB6", "INX", "SBC", "NOP", "NOP*", "CPX", "SBC", "INC", "BBS6", //E
|
||||
"BEQ", "SBC", "SBC", "TAI", "SET", "SBC", "INC", "SMB7", "SED", "SBC", "PLX", "NOP*", "NOP*", "SBC", "INC", "BBS7" //F
|
||||
"BRK", "ORA", "SXY", "ST0", "TSB", "ORA", "ASL", "RMB0", "PHP", "ORA", "ASL", "NOP_0B","TSB", "ORA", "ASL", "BBR0", //0
|
||||
"BPL", "ORA", "ORA", "ST1", "TRB", "ORA", "ASL", "RMB1", "CLC", "ORA", "INC", "NOP_1B","TRB", "ORA", "ASL", "BBR1", //1
|
||||
"JSR", "AND", "SAX", "ST2", "BIT", "AND", "ROL", "RMB2", "PLP", "AND", "ROL", "NOP_2B","BIT", "AND", "ROL", "BBR2", //2
|
||||
"BMI", "AND", "AND", "NOP_33","BIT", "AND", "ROL", "RMB3", "SEC", "AND", "DEC", "NOP_3B","BIT", "AND", "ROL", "BBR3", //3
|
||||
"RTI", "EOR", "SAY", "TMA", "BSR", "EOR", "LSR", "RMB4", "PHA", "EOR", "LSR", "NOP_4B","JMP", "EOR", "LSR", "BBR4", //4
|
||||
"BVC", "EOR", "EOR", "TAM", "CSL", "EOR", "LSR", "RMB5", "CLI", "EOR", "PHY", "NOP_5B","NOP_5C","EOR", "LSR", "BBR5", //5
|
||||
"RTS", "ADC", "CLA", "NOP_63","STZ", "ADC", "ROR", "RMB6", "PLA", "ADC", "ROR", "NOP_6B","JMP", "ADC", "ROR", "BBR6", //6
|
||||
"BVS", "ADC", "ADC", "TII", "STZ", "ADC", "ROR", "RMB7", "SEI", "ADC", "PLY", "NOP_7B","JMP", "ADC", "ROR", "BBR7", //7
|
||||
"BRA", "STA", "CLX", "TST", "STY", "STA", "STX", "SMB0", "DEY", "BIT", "TXA", "NOP_8B","STY", "STA", "STX", "BBS0", //8
|
||||
"BCC", "STA", "STA", "TST", "STY", "STA", "STX", "SMB1", "TYA", "STA", "TXS", "NOP_9B","STZ", "STA", "STZ", "BBS1", //9
|
||||
"LDY", "LDA", "LDX", "TST", "LDY", "LDA", "LDX", "SMB2", "TAY", "LDA", "TAX", "NOP_AB","LDY", "LDA", "LDX", "BBS2", //A
|
||||
"BCS", "LDA", "LDA", "TST", "LDY", "LDA", "LDX", "SMB3", "CLV", "LDA", "TSX", "NOP_BB","LDY", "LDA", "LDX", "BBS3", //B
|
||||
"CPY", "CMP", "CLY", "TDD", "CPY", "CMP", "DEC", "SMB4", "INY", "CMP", "DEX", "NOP_CB","CPY", "CMP", "DEC", "BBS4", //C
|
||||
"BNE", "CMP", "CMP", "TIN", "CSH", "CMP", "DEC", "SMB5", "CLD", "CMP", "PHX", "NOP_DB","NOP_DC","CMP", "DEC", "BBS5", //D
|
||||
"CPX", "SBC", "NOP_E2","TIA", "CPX", "SBC", "INC", "SMB6", "INX", "SBC", "NOP", "NOP_EB","CPX", "SBC", "INC", "BBS6", //E
|
||||
"BEQ", "SBC", "SBC", "TAI", "SET", "SBC", "INC", "SMB7", "SED", "SBC", "PLX", "NOP_FB","NOP_FC","SBC", "INC", "BBS7" //F
|
||||
};
|
||||
|
||||
typedef PceAddrMode M;
|
||||
|
|
|
@ -9,450 +9,156 @@
|
|||
#include "Debugger/DisassemblyInfo.h"
|
||||
#include "Debugger/LabelManager.h"
|
||||
|
||||
static const std::regex instRegex = std::regex("^\\s*([a-zA-Z]{3})[\\s]*(#%|#){0,1}([([]{0,1})[\\s]*([$]{0,1})([^\\[\\],)(;:]*)[\\s]*((,[$][0-9a-f]{1,2}|,x\\)|\\),y|,x|,y|,s\\),y|,s|\\)|\\],y|\\]){0,1})\\s*(;*)(.*)", std::regex_constants::icase);
|
||||
static const std::regex isCommentOrBlank = std::regex("^\\s*([;]+.*$|\\s*$)", std::regex_constants::icase);
|
||||
static const std::regex labelRegex = std::regex("^\\s*([@_a-zA-Z][@_a-zA-Z0-9]*):(.*)", std::regex_constants::icase);
|
||||
static const std::regex byteRegex = std::regex("^\\s*[.]db\\s+((\\$[a-fA-F0-9]{1,2}[ ])*)(\\$[a-fA-F0-9]{1,2})+\\s*(;*)(.*)$", std::regex_constants::icase);
|
||||
|
||||
void SnesAssembler::ProcessLine(string code, uint32_t& instructionAddress, vector<int16_t>& output, std::unordered_map<string, uint32_t>& labels, bool firstPass, std::unordered_map<string, uint32_t>& currentPassLabels)
|
||||
SnesAssembler::SnesAssembler(LabelManager* labelManager) : Base6502Assembler<SnesAddrMode>(labelManager, CpuType::Snes)
|
||||
{
|
||||
//Remove extra spaces as part of processing
|
||||
size_t offset = code.find_first_of(',', 0);
|
||||
if(offset != string::npos) {
|
||||
code.erase(std::remove(code.begin() + offset + 1, code.end(), ' '), code.end());
|
||||
}
|
||||
offset = code.find_first_of(')', 0);
|
||||
if(offset != string::npos) {
|
||||
code.erase(std::remove(code.begin() + offset + 1, code.end(), ' '), code.end());
|
||||
}
|
||||
|
||||
//Determine if the line is blank, a comment, a label or code
|
||||
std::smatch match;
|
||||
if(std::regex_search(code, match, byteRegex)) {
|
||||
vector<string> bytes = StringUtilities::Split(match.str(1) + match.str(3), ' ');
|
||||
for(string& byte : bytes) {
|
||||
output.push_back((uint8_t)(HexUtilities::FromHex(byte.substr(1))));
|
||||
instructionAddress++;
|
||||
}
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
} else if(std::regex_search(code, match, labelRegex)) {
|
||||
string label = match.str(1);
|
||||
string afterLabel = match.str(2);
|
||||
if(currentPassLabels.find(match.str(1)) != currentPassLabels.end()) {
|
||||
output.push_back(AssemblerSpecialCodes::LabelRedefinition);
|
||||
} else {
|
||||
labels[match.str(1)] = instructionAddress;
|
||||
currentPassLabels[match.str(1)] = instructionAddress;
|
||||
ProcessLine(afterLabel, instructionAddress, output, labels, firstPass, currentPassLabels);
|
||||
}
|
||||
return;
|
||||
} else if(std::regex_search(code, match, isCommentOrBlank)) {
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
return;
|
||||
} else if(std::regex_search(code, match, instRegex) && match.size() > 1) {
|
||||
SnesLineData lineData;
|
||||
AssemblerSpecialCodes result = GetLineData(match, lineData, labels, firstPass);
|
||||
if(result == AssemblerSpecialCodes::OK) {
|
||||
AssembleInstruction(lineData, instructionAddress, output, firstPass);
|
||||
} else {
|
||||
output.push_back(result);
|
||||
}
|
||||
} else {
|
||||
output.push_back(AssemblerSpecialCodes::ParsingError);
|
||||
}
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes SnesAssembler::GetLineData(std::smatch match, SnesLineData& lineData, std::unordered_map<string, uint32_t>& labels, bool firstPass)
|
||||
string SnesAssembler::GetOpName(uint8_t opcode)
|
||||
{
|
||||
bool isBinary = match.str(2).length() > 1 && match.str(2)[1] == '%'; //Immediate + binary: "#%"
|
||||
return SnesDisUtils::OpName[opcode];
|
||||
}
|
||||
|
||||
lineData.OpCode = match.str(1);
|
||||
lineData.IsImmediate = !match.str(2).empty();
|
||||
lineData.IsHex = !match.str(4).empty();
|
||||
lineData.HasComment = !match.str(8).empty();
|
||||
lineData.OperandSuffix = match.str(6);
|
||||
lineData.HasOpeningParenthesis = match.str(3) == "(";
|
||||
lineData.HasOpeningBracket = match.str(3) == "[";
|
||||
SnesAddrMode SnesAssembler::GetOpMode(uint8_t opcode)
|
||||
{
|
||||
return SnesDisUtils::OpMode[opcode];
|
||||
}
|
||||
|
||||
std::transform(lineData.OperandSuffix.begin(), lineData.OperandSuffix.end(), lineData.OperandSuffix.begin(), ::toupper);
|
||||
std::transform(lineData.OpCode.begin(), lineData.OpCode.end(), lineData.OpCode.begin(), ::toupper);
|
||||
bool SnesAssembler::IsOfficialOp(uint8_t opcode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool foundSpace = false;
|
||||
for(char c : match.str(5)) {
|
||||
if(c != ' ' && c != '\t') {
|
||||
if(foundSpace) {
|
||||
//can't have spaces in operands (except at the very end)
|
||||
return AssemblerSpecialCodes::InvalidSpaces;
|
||||
} else if(lineData.IsHex && !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) {
|
||||
//invalid hex
|
||||
return AssemblerSpecialCodes::InvalidHex;
|
||||
} else if(isBinary && c != '0' && c != '1') {
|
||||
return AssemblerSpecialCodes::InvalidBinaryValue;
|
||||
}
|
||||
AssemblerSpecialCodes SnesAssembler::ResolveOpMode(AssemblerLineData& op, uint32_t instructionAddress, bool firstPass)
|
||||
{
|
||||
AssemblerOperand& operand = op.Operands[0];
|
||||
AssemblerOperand& operand2 = op.Operands[1];
|
||||
AssemblerOperand& operand3 = op.Operands[2];
|
||||
if(operand.ByteCount > 3 || operand2.ByteCount > 1 || operand3.ByteCount > 0) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
|
||||
lineData.Operand.push_back(c);
|
||||
if(op.OperandCount == 3) {
|
||||
//StkRelIndIdxY
|
||||
if(operand.HasOpeningParenthesis && operand.ByteCount == 1 && operand2.HasClosingParenthesis && operand2.Type == OperandType::S && operand3.Type == OperandType::Y) {
|
||||
op.AddrMode = SnesAddrMode::StkRelIndIdxY;
|
||||
} else {
|
||||
foundSpace = true;
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
}
|
||||
|
||||
if(isBinary) {
|
||||
//Convert the binary value to hex
|
||||
if(lineData.Operand.size() == 0) {
|
||||
return AssemblerSpecialCodes::MissingOperand;
|
||||
} else if(lineData.Operand.size() <= 8) {
|
||||
lineData.IsHex = true;
|
||||
int value = 0;
|
||||
for(size_t i = 0; i < lineData.Operand.size(); i++) {
|
||||
value <<= 1;
|
||||
value |= lineData.Operand[i] == '1' ? 1 : 0;
|
||||
}
|
||||
lineData.Operand = HexUtilities::ToHex(value, false);
|
||||
} else if(operand.Type == OperandType::Custom && operand2.Type == OperandType::Custom && op.OperandCount == 2) {
|
||||
//BlkMov (MVP/MVN)
|
||||
if(operand.HasParenOrBracket() || operand2.HasParenOrBracket() || operand.ByteCount != 1 || operand2.ByteCount != 1) {
|
||||
return AssemblerSpecialCodes::InvalidOperands;
|
||||
}
|
||||
op.AddrMode = SnesAddrMode::BlkMov;
|
||||
} else if(operand.IsImmediate) {
|
||||
//Imm16, Imm8, Rel
|
||||
if(operand.HasParenOrBracket() || operand.ByteCount > 2 || op.OperandCount > 1) {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
if(IsOpModeAvailable(op.OpCode, SnesAddrMode::Rel)) {
|
||||
op.AddrMode = SnesAddrMode::Rel;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
}
|
||||
|
||||
if(!lineData.HasComment && !match.str(9).empty()) {
|
||||
//something is trailing at the end of the line, and it's not a comment
|
||||
return AssemblerSpecialCodes::TrailingText;
|
||||
}
|
||||
|
||||
if(!lineData.IsHex) {
|
||||
bool allNumeric = true;
|
||||
for(size_t i = 0; i < lineData.Operand.size(); i++) {
|
||||
if(lineData.Operand[i] == '-' && i == 0 && lineData.Operand.size() > 1) {
|
||||
//First char is a minus sign, and more characters follow, continue
|
||||
continue;
|
||||
if(IsOpModeAvailable(op.OpCode, SnesAddrMode::ImmM)) {
|
||||
op.AddrMode = SnesAddrMode::ImmM;
|
||||
} else if(IsOpModeAvailable(op.OpCode, SnesAddrMode::ImmX)) {
|
||||
op.AddrMode = SnesAddrMode::ImmX;
|
||||
} else {
|
||||
op.AddrMode = operand.ByteCount == 2 ? SnesAddrMode::Imm16 : SnesAddrMode::Imm8;
|
||||
}
|
||||
|
||||
if((lineData.Operand[i] < '0' || lineData.Operand[i] > '9')) {
|
||||
allNumeric = false;
|
||||
}
|
||||
} else if(operand.HasOpeningBracket) {
|
||||
//DirIndLng, DirIndLngIdxY, AbsIndLng
|
||||
if(operand.HasClosingBracket) {
|
||||
if(operand2.Type == OperandType::Y) {
|
||||
op.AddrMode = SnesAddrMode::DirIndLngIdxY;
|
||||
} else {
|
||||
if(op.OperandCount > 1) {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
op.AddrMode = operand.ByteCount == 1 ? SnesAddrMode::DirIndLng : SnesAddrMode::AbsIndLng;
|
||||
}
|
||||
} else {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
} else if(operand.HasOpeningParenthesis) {
|
||||
//DirInd, AbsInd, DirIdxIndX, AbsIdxXInd, DirIndIdxY
|
||||
if(operand.HasClosingParenthesis) {
|
||||
if(operand2.Type == OperandType::Y) {
|
||||
op.AddrMode = SnesAddrMode::DirIndIdxY;
|
||||
} else {
|
||||
if(op.OperandCount > 1) {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
op.AddrMode = operand.ByteCount == 1 ? SnesAddrMode::DirInd : SnesAddrMode::AbsInd;
|
||||
}
|
||||
} else if(operand2.Type == OperandType::X && operand2.HasClosingParenthesis) {
|
||||
op.AddrMode = operand.ByteCount == 1 ? SnesAddrMode::DirIdxIndX : SnesAddrMode::AbsIdxXInd;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
} else if(operand.HasParenOrBracket() || operand2.HasParenOrBracket()) {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
} else if(op.OperandCount == 2) {
|
||||
if(operand2.Type == OperandType::X) {
|
||||
switch(operand.ByteCount) {
|
||||
case 1: op.AddrMode = SnesAddrMode::DirIdxX; break;
|
||||
case 2: op.AddrMode = SnesAddrMode::AbsIdxX; break;
|
||||
case 3: op.AddrMode = SnesAddrMode::AbsLngIdxX; break;
|
||||
default: return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
} else if(operand2.Type == OperandType::Y) {
|
||||
switch(operand.ByteCount) {
|
||||
case 1: op.AddrMode = SnesAddrMode::DirIdxY; break;
|
||||
case 2: op.AddrMode = SnesAddrMode::AbsIdxY; break;
|
||||
default: return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
} else if(operand2.Type == OperandType::S && operand.ByteCount == 1) {
|
||||
op.AddrMode = SnesAddrMode::StkRel;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
} else if(op.OperandCount == 1) {
|
||||
switch(operand.ByteCount) {
|
||||
case 1:
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, SnesAddrMode::Rel) ? SnesAddrMode::Rel : SnesAddrMode::Dir;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allNumeric && !lineData.Operand.empty()) {
|
||||
//Operand is not empty, and it only contains decimal values
|
||||
lineData.IsDecimal = true;
|
||||
} else {
|
||||
lineData.IsDecimal = false;
|
||||
}
|
||||
}
|
||||
|
||||
return GetAddrModeAndOperandSize(lineData, labels, firstPass);
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes SnesAssembler::GetAddrModeAndOperandSize(SnesLineData& lineData, std::unordered_map<string, uint32_t>& labels, bool firstPass)
|
||||
{
|
||||
int opSize = 0;
|
||||
bool invalid = false;
|
||||
string operand = lineData.Operand;
|
||||
|
||||
if(lineData.IsHex) {
|
||||
if(operand.size() == 0) {
|
||||
return AssemblerSpecialCodes::MissingOperand;
|
||||
} else if(operand.size() <= 2) {
|
||||
opSize = 1;
|
||||
} else if(operand.size() <= 4) {
|
||||
opSize = 2;
|
||||
} else if(operand.size() <= 6) {
|
||||
opSize = 3;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(lineData.IsDecimal) {
|
||||
int value = std::stoi(operand.c_str());
|
||||
if(value < -0x800000) {
|
||||
//< -2^23 is invalid
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
} else if(value < -0x8000) {
|
||||
opSize = 3;
|
||||
} else if(value < -0x80) {
|
||||
opSize = 2;
|
||||
} else if(value <= 0xFF) {
|
||||
opSize = 1;
|
||||
} else if(value <= 0xFFFF) {
|
||||
opSize = 2;
|
||||
} else if(value <= 0xFFFFFF) {
|
||||
opSize = 3;
|
||||
} else {
|
||||
//>= 2^23 is invalid
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(!operand.empty()) {
|
||||
//Check if the operand is a known label
|
||||
auto findResult = labels.find(operand);
|
||||
if(findResult != labels.end()) {
|
||||
lineData.Operand = HexUtilities::ToHex((uint16_t)findResult->second);
|
||||
lineData.IsHex = true;
|
||||
opSize = 2;
|
||||
} else if(operand.size() == 1 && (operand[0] == 'A' || operand[0] == 'a') && lineData.OperandSuffix.empty() && !lineData.IsHex && !lineData.IsImmediate && !lineData.HasOpeningParenthesis && !lineData.HasOpeningBracket) {
|
||||
//Allow optional "A" after AddrMode == Accumulator instructions
|
||||
lineData.Mode = AddrMode::Acc;
|
||||
opSize = 0;
|
||||
} else {
|
||||
int32_t addr = _labelManager->GetLabelRelativeAddress(operand);
|
||||
if(addr > 0xFFFF) {
|
||||
lineData.Operand = HexUtilities::ToHex24(addr);
|
||||
lineData.IsHex = true;
|
||||
opSize = 3;
|
||||
} else if(addr > 0xFF) {
|
||||
lineData.Operand = HexUtilities::ToHex((uint16_t)addr);
|
||||
lineData.IsHex = true;
|
||||
opSize = 2;
|
||||
} else if(addr >= 0) {
|
||||
lineData.Operand = HexUtilities::ToHex((uint8_t)addr);
|
||||
lineData.IsHex = true;
|
||||
opSize = 1;
|
||||
} else {
|
||||
if(firstPass) {
|
||||
//First pass, we couldn't find a matching label, so it might be defined later on
|
||||
//Pretend it exists for now
|
||||
_needSecondPass = true;
|
||||
lineData.Operand = "FFFF";
|
||||
lineData.IsHex = true;
|
||||
opSize = 2;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::UnknownLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//No operand
|
||||
opSize = 0;
|
||||
}
|
||||
|
||||
if(lineData.Mode == AddrMode::Imp) {
|
||||
if(lineData.OperandSuffix.substr(0, 2) == ",$") {
|
||||
//Used by MVP, MVN
|
||||
opSize = 2;
|
||||
lineData.Mode = AddrMode::BlkMov;
|
||||
uint8_t dest = HexUtilities::FromHex(lineData.OperandSuffix.substr(2));
|
||||
lineData.Operand += HexUtilities::ToHex(dest);
|
||||
} else if(lineData.IsImmediate) {
|
||||
if(lineData.HasOpeningParenthesis || lineData.HasOpeningBracket|| opSize == 0) {
|
||||
invalid = true;
|
||||
} else if(opSize >= 3) {
|
||||
invalid = true;
|
||||
}
|
||||
lineData.Mode = opSize == 2 ? AddrMode::Imm16 : AddrMode::Imm8; //or Rel
|
||||
} else if(lineData.HasOpeningBracket) {
|
||||
if(lineData.OperandSuffix == "]") {
|
||||
switch(opSize){
|
||||
case 1: lineData.Mode = AddrMode::DirIndLng; break;
|
||||
case 2: lineData.Mode = AddrMode::AbsIndLng; break;
|
||||
default: invalid = true; break;
|
||||
}
|
||||
} else if(lineData.OperandSuffix == "],Y") {
|
||||
if(opSize == 1) {
|
||||
lineData.Mode = AddrMode::DirIndLngIdxY;
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
} else if(lineData.HasOpeningParenthesis) {
|
||||
if(lineData.OperandSuffix == ")") {
|
||||
lineData.Mode = opSize == 1 ? AddrMode::DirInd : AddrMode::AbsInd;
|
||||
} else if(lineData.OperandSuffix == ",X)") {
|
||||
lineData.Mode = opSize == 1 ? AddrMode::DirIdxIndX : AddrMode::AbsIdxXInd;
|
||||
} else if(lineData.OperandSuffix == "),Y") {
|
||||
if(opSize == 1) {
|
||||
lineData.Mode = AddrMode::DirIndIdxY;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(lineData.OperandSuffix == ",S),Y") {
|
||||
if(opSize == 1) {
|
||||
lineData.Mode = AddrMode::StkRelIndIdxY;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
} else {
|
||||
if(lineData.OperandSuffix == ",X") {
|
||||
if(opSize == 3) {
|
||||
lineData.Mode = AddrMode::AbsLngIdxX;
|
||||
} else if(opSize == 2) {
|
||||
lineData.Mode = AddrMode::AbsIdxX;
|
||||
} else if(opSize == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
lineData.Mode = AddrMode::DirIdxX;
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
} else if(lineData.OperandSuffix == ",Y") {
|
||||
if(opSize == 2) {
|
||||
lineData.Mode = AddrMode::AbsIdxY;
|
||||
} else if(opSize == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
lineData.Mode = AddrMode::DirIdxY;
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
} else if(lineData.OperandSuffix == ",S") {
|
||||
if(opSize == 1) {
|
||||
lineData.Mode = AddrMode::StkRel;
|
||||
} else {
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(lineData.OperandSuffix.empty()) {
|
||||
if(opSize == 0) {
|
||||
lineData.Mode = AddrMode::Imp; //or Acc
|
||||
} else if(opSize == 3) {
|
||||
lineData.Mode = AddrMode::AbsLng;
|
||||
} else if(opSize == 2) {
|
||||
lineData.Mode = AddrMode::Abs;
|
||||
} else if(opSize == 1) {
|
||||
//Sometimes zero page addressing is not available, even if the operand is in the zero page
|
||||
lineData.Mode = AddrMode::Dir;
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*if(lineData.Mode == AddrMode::None) {
|
||||
invalid = true;
|
||||
}*/
|
||||
|
||||
lineData.OperandSize = opSize;
|
||||
|
||||
return invalid ? AssemblerSpecialCodes::ParsingError : AssemblerSpecialCodes::OK;
|
||||
}
|
||||
|
||||
bool SnesAssembler::IsOpModeAvailable(string& opCode, AddrMode mode)
|
||||
{
|
||||
return _availableModesByOpName[opCode].find((int)mode) != _availableModesByOpName[opCode].end();
|
||||
}
|
||||
|
||||
void SnesAssembler::AssembleInstruction(SnesLineData& lineData, uint32_t& instructionAddress, vector<int16_t>& output, bool firstPass)
|
||||
{
|
||||
bool foundMatch = false;
|
||||
|
||||
for(int i = 0; i < 256; i++) {
|
||||
AddrMode opMode = SnesDisUtils::OpMode[i];
|
||||
if(lineData.OpCode == SnesDisUtils::OpName[i]) {
|
||||
bool modeMatch = opMode == lineData.Mode;
|
||||
if(!modeMatch) {
|
||||
if(lineData.Mode == AddrMode::Imp && (opMode == AddrMode::Acc || opMode == AddrMode::Stk)) {
|
||||
modeMatch = true;
|
||||
} else if(lineData.Mode == AddrMode::Imm8 && opMode == AddrMode::Sig8) {
|
||||
modeMatch = true;
|
||||
} else if((lineData.Mode == AddrMode::Imm8 || lineData.Mode == AddrMode::Imm16) && (opMode == AddrMode::ImmX || opMode == AddrMode::ImmM)) {
|
||||
modeMatch = true;
|
||||
} else if((opMode == AddrMode::Rel || opMode == AddrMode::RelLng) && (lineData.Mode == AddrMode::AbsLng || lineData.Mode == AddrMode::Abs || lineData.Mode == AddrMode::Dir)) {
|
||||
bool lngBranch = opMode == AddrMode::RelLng;
|
||||
modeMatch = true;
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
if(IsOpModeAvailable(op.OpCode, SnesAddrMode::Rel)) {
|
||||
op.AddrMode = SnesAddrMode::Rel;
|
||||
|
||||
//Convert "absolute" jump to a relative jump
|
||||
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
|
||||
if(lineData.Mode == AddrMode::Abs) {
|
||||
value |= (instructionAddress & 0xFF0000);
|
||||
}
|
||||
|
||||
int32_t addressGap;
|
||||
if(lineData.Mode == AddrMode::Dir) {
|
||||
addressGap = (int8_t)value;
|
||||
} else {
|
||||
addressGap = value - (instructionAddress + (lngBranch ? 3 : 2));
|
||||
}
|
||||
|
||||
if(addressGap > (lngBranch ? 32767 : 127) || addressGap < (lngBranch ? -32768 : -128)) {
|
||||
int32_t addressGap = operand.Value - (instructionAddress + 2);
|
||||
bool lngBranch = addressGap < -128 || addressGap > 127;
|
||||
int16_t maxPosGap = lngBranch ? 32767 : 127;
|
||||
int16_t maxNegGap = lngBranch ? -32768 : -128;
|
||||
if(addressGap > maxPosGap || addressGap < maxNegGap) {
|
||||
//Gap too long, can't jump that far
|
||||
if(!firstPass) {
|
||||
//Pretend this is ok on first pass, we're just trying to find all labels
|
||||
output.push_back(AssemblerSpecialCodes::OutOfRangeJump);
|
||||
return;
|
||||
return AssemblerSpecialCodes::OutOfRangeJump;
|
||||
}
|
||||
}
|
||||
|
||||
//Update data to match relative jump
|
||||
lineData.OperandSize = lngBranch ? 2 : 1;
|
||||
lineData.IsHex = true;
|
||||
lineData.Operand = lngBranch ? HexUtilities::ToHex((uint16_t)addressGap) : HexUtilities::ToHex((uint8_t)addressGap);
|
||||
operand.ByteCount = lngBranch ? 2 : 1;
|
||||
operand.Value = lngBranch ? (uint16_t)addressGap : (uint8_t)addressGap;
|
||||
} else {
|
||||
op.AddrMode = operand.ByteCount == 3 ? SnesAddrMode::AbsLng : SnesAddrMode::Abs;
|
||||
}
|
||||
}
|
||||
|
||||
if(modeMatch) {
|
||||
output.push_back(i);
|
||||
instructionAddress += (lineData.OperandSize + 1);
|
||||
|
||||
if(lineData.OperandSize == 1) {
|
||||
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
|
||||
output.push_back(value & 0xFF);
|
||||
} else if(lineData.OperandSize == 2) {
|
||||
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
|
||||
output.push_back(value & 0xFF);
|
||||
output.push_back((value >> 8) & 0xFF);
|
||||
} else if(lineData.OperandSize == 3) {
|
||||
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
|
||||
output.push_back(value & 0xFF);
|
||||
output.push_back((value >> 8) & 0xFF);
|
||||
output.push_back((value >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!foundMatch) {
|
||||
output.push_back(AssemblerSpecialCodes::InvalidInstruction);
|
||||
default:
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
} else if(op.OperandCount == 0) {
|
||||
op.AddrMode = IsOpModeAvailable(op.OpCode, SnesAddrMode::Acc) ? SnesAddrMode::Acc : (IsOpModeAvailable(op.OpCode, SnesAddrMode::Stk) ? SnesAddrMode::Stk : SnesAddrMode::Imp);
|
||||
} else {
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
}
|
||||
}
|
||||
|
||||
SnesAssembler::SnesAssembler(LabelManager* labelManager)
|
||||
{
|
||||
_labelManager = labelManager;
|
||||
}
|
||||
|
||||
SnesAssembler::~SnesAssembler()
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t SnesAssembler::AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode)
|
||||
{
|
||||
for(uint8_t i = 0; i < 255; i++) {
|
||||
if(_availableModesByOpName.find(SnesDisUtils::OpName[i]) == _availableModesByOpName.end()) {
|
||||
_availableModesByOpName[SnesDisUtils::OpName[i]] = std::unordered_set<int>();
|
||||
}
|
||||
_availableModesByOpName[SnesDisUtils::OpName[i]].emplace((int)SnesDisUtils::OpMode[i]);
|
||||
return AssemblerSpecialCodes::ParsingError;
|
||||
}
|
||||
|
||||
std::unordered_map<string, uint32_t> temporaryLabels;
|
||||
std::unordered_map<string, uint32_t> currentPassLabels;
|
||||
|
||||
vector<int16_t> output;
|
||||
output.reserve(1000);
|
||||
|
||||
uint32_t originalStartAddr = startAddress;
|
||||
|
||||
vector<string> codeLines = StringUtilities::Split(code, '\n');
|
||||
|
||||
//Make 2 passes - first one to find all labels, second to assemble
|
||||
_needSecondPass = false;
|
||||
for(string& line : codeLines) {
|
||||
ProcessLine(line, startAddress, output, temporaryLabels, true, currentPassLabels);
|
||||
}
|
||||
|
||||
if(_needSecondPass) {
|
||||
currentPassLabels.clear();
|
||||
output.clear();
|
||||
startAddress = originalStartAddr;
|
||||
for(string& line : codeLines) {
|
||||
ProcessLine(line, startAddress, output, temporaryLabels, false, currentPassLabels);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(assembledCode, output.data(), std::min<int>(100000, (int)output.size()) * sizeof(uint16_t));
|
||||
return (uint32_t)output.size();
|
||||
}
|
||||
return AssemblerSpecialCodes::OK;
|
||||
}
|
|
@ -1,43 +1,19 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <regex>
|
||||
#include "Debugger/IAssembler.h"
|
||||
#include "SNES/Debugger/SnesDisUtils.h"
|
||||
#include "Debugger/Base6502Assembler.h"
|
||||
|
||||
class LabelManager;
|
||||
enum class SnesAddrMode : uint8_t;
|
||||
|
||||
struct SnesLineData
|
||||
class SnesAssembler : public Base6502Assembler<SnesAddrMode>
|
||||
{
|
||||
string OpCode;
|
||||
string Operand;
|
||||
string OperandSuffix;
|
||||
AddrMode Mode = AddrMode::Imp;
|
||||
int OperandSize = 0;
|
||||
bool IsHex = false;
|
||||
bool IsDecimal = false;
|
||||
bool IsImmediate = false;
|
||||
bool HasComment = false;
|
||||
bool HasOpeningParenthesis = false;
|
||||
bool HasOpeningBracket = false;
|
||||
};
|
||||
|
||||
class SnesAssembler : public IAssembler
|
||||
{
|
||||
private:
|
||||
std::unordered_map<string, std::unordered_set<int>> _availableModesByOpName;
|
||||
bool _needSecondPass = false;
|
||||
|
||||
LabelManager* _labelManager;
|
||||
void ProcessLine(string code, uint32_t& instructionAddress, vector<int16_t>& output, std::unordered_map<string, uint32_t>& labels, bool firstPass, std::unordered_map<string, uint32_t>& currentPassLabels);
|
||||
AssemblerSpecialCodes GetLineData(std::smatch match, SnesLineData& lineData, std::unordered_map<string, uint32_t>& labels, bool firstPass);
|
||||
AssemblerSpecialCodes GetAddrModeAndOperandSize(SnesLineData& lineData, std::unordered_map<string, uint32_t>& labels, bool firstPass);
|
||||
void AssembleInstruction(SnesLineData& lineData, uint32_t& instructionAddress, vector<int16_t>& output, bool firstPass);
|
||||
|
||||
bool IsOpModeAvailable(string& opCode, AddrMode mode);
|
||||
protected:
|
||||
string GetOpName(uint8_t opcode) override;
|
||||
SnesAddrMode GetOpMode(uint8_t opcode) override;
|
||||
bool IsOfficialOp(uint8_t opcode) override;
|
||||
AssemblerSpecialCodes ResolveOpMode(AssemblerLineData& op, uint32_t instructionAddress, bool firstPass) override;
|
||||
|
||||
public:
|
||||
SnesAssembler(LabelManager* labelManager);
|
||||
virtual ~SnesAssembler();
|
||||
|
||||
uint32_t AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode);
|
||||
virtual ~SnesAssembler() {}
|
||||
};
|
|
@ -8,6 +8,7 @@
|
|||
#include "SNES/SnesPpu.h"
|
||||
#include "SNES/MemoryMappings.h"
|
||||
#include "SNES/Debugger/DummySnesCpu.h"
|
||||
#include "SNES/Debugger/SnesDisUtils.h"
|
||||
#include "SNES/Debugger/SnesAssembler.h"
|
||||
#include "SNES/Debugger/SnesDebugger.h"
|
||||
#include "SNES/Debugger/SnesEventManager.h"
|
||||
|
|
|
@ -15,7 +15,7 @@ void SnesDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t m
|
|||
FastString str(settings->CheckDebuggerFlag(DebuggerFlags::UseLowerCaseDisassembly));
|
||||
|
||||
uint8_t opCode = info.GetOpCode();
|
||||
AddrMode addrMode = SnesDisUtils::OpMode[opCode];
|
||||
SnesAddrMode addrMode = SnesDisUtils::OpMode[opCode];
|
||||
str.Write(SnesDisUtils::OpName[opCode]);
|
||||
str.Write(' ');
|
||||
|
||||
|
@ -24,7 +24,7 @@ void SnesDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t m
|
|||
|
||||
FastString operand(settings->CheckDebuggerFlag(DebuggerFlags::UseLowerCaseDisassembly));
|
||||
if(opSize > 1) {
|
||||
if(addrMode == AddrMode::Rel || addrMode == AddrMode::RelLng || opSize == 4) {
|
||||
if(addrMode == SnesAddrMode::Rel || addrMode == SnesAddrMode::RelLng || opSize == 4) {
|
||||
AddressInfo address { (int32_t)opAddr, MemoryType::SnesMemory };
|
||||
string label = labelManager ? labelManager->GetLabel(address) : "";
|
||||
if(label.size()) {
|
||||
|
@ -40,38 +40,37 @@ void SnesDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t m
|
|||
}
|
||||
|
||||
switch(addrMode) {
|
||||
case AddrMode::Abs: str.Write(operand); break;
|
||||
case AddrMode::AbsJmp: str.Write(operand); break;
|
||||
case AddrMode::AbsIdxXInd: str.WriteAll('(', operand, ",X)"); break;
|
||||
case AddrMode::AbsIdxX: str.WriteAll(operand, ",X"); break;
|
||||
case AddrMode::AbsIdxY: str.WriteAll(operand, ",Y"); break;
|
||||
case AddrMode::AbsInd: str.WriteAll('(', operand, ')'); break;
|
||||
case AddrMode::AbsIndLng: str.WriteAll('[', operand, ']'); break;
|
||||
case AddrMode::AbsLngIdxX: str.WriteAll(operand, ",X"); break;
|
||||
case AddrMode::AbsLng: str.Write(operand); break;
|
||||
case AddrMode::AbsLngJmp: str.Write(operand); break;
|
||||
case AddrMode::Acc: break;
|
||||
case AddrMode::BlkMov: str.WriteAll('$', operand[1], operand[2], ','); str.WriteAll('$', operand[3], operand[4]); break;
|
||||
case AddrMode::DirIdxIndX: str.WriteAll('(', operand, ",X)"); break;
|
||||
case AddrMode::DirIdxX: str.WriteAll(operand, ",X"); break;
|
||||
case AddrMode::DirIdxY: str.WriteAll(operand, ",Y"); break;
|
||||
case AddrMode::DirIndIdxY: str.WriteAll("(", operand, "),Y"); break;
|
||||
case AddrMode::DirIndLngIdxY: str.WriteAll("[", operand, "],Y"); break;
|
||||
case AddrMode::DirIndLng: str.WriteAll("[", operand, "]"); break;
|
||||
case AddrMode::DirInd: str.WriteAll("(", operand, ")"); break;
|
||||
case AddrMode::Dir: str.Write(operand); break;
|
||||
case SnesAddrMode::Abs: str.Write(operand); break;
|
||||
case SnesAddrMode::AbsJmp: str.Write(operand); break;
|
||||
case SnesAddrMode::AbsIdxXInd: str.WriteAll('(', operand, ",X)"); break;
|
||||
case SnesAddrMode::AbsIdxX: str.WriteAll(operand, ",X"); break;
|
||||
case SnesAddrMode::AbsIdxY: str.WriteAll(operand, ",Y"); break;
|
||||
case SnesAddrMode::AbsInd: str.WriteAll('(', operand, ')'); break;
|
||||
case SnesAddrMode::AbsIndLng: str.WriteAll('[', operand, ']'); break;
|
||||
case SnesAddrMode::AbsLngIdxX: str.WriteAll(operand, ",X"); break;
|
||||
case SnesAddrMode::AbsLng: str.Write(operand); break;
|
||||
case SnesAddrMode::AbsLngJmp: str.Write(operand); break;
|
||||
case SnesAddrMode::Acc: break;
|
||||
case SnesAddrMode::BlkMov: str.WriteAll('$', operand[1], operand[2], ','); str.WriteAll('$', operand[3], operand[4]); break;
|
||||
case SnesAddrMode::DirIdxIndX: str.WriteAll('(', operand, ",X)"); break;
|
||||
case SnesAddrMode::DirIdxX: str.WriteAll(operand, ",X"); break;
|
||||
case SnesAddrMode::DirIdxY: str.WriteAll(operand, ",Y"); break;
|
||||
case SnesAddrMode::DirIndIdxY: str.WriteAll("(", operand, "),Y"); break;
|
||||
case SnesAddrMode::DirIndLngIdxY: str.WriteAll("[", operand, "],Y"); break;
|
||||
case SnesAddrMode::DirIndLng: str.WriteAll("[", operand, "]"); break;
|
||||
case SnesAddrMode::DirInd: str.WriteAll("(", operand, ")"); break;
|
||||
case SnesAddrMode::Dir: str.Write(operand); break;
|
||||
|
||||
case AddrMode::Imm8: case AddrMode::Imm16: case AddrMode::ImmX: case AddrMode::ImmM:
|
||||
case SnesAddrMode::Imm8: case SnesAddrMode::Imm16: case SnesAddrMode::ImmX: case SnesAddrMode::ImmM:
|
||||
str.WriteAll('#', operand);
|
||||
break;
|
||||
|
||||
case AddrMode::Sig8: str.WriteAll('#', operand); break; //BRK/COP signature
|
||||
case AddrMode::Imp: break;
|
||||
case AddrMode::RelLng: str.Write(operand); break;
|
||||
case AddrMode::Rel: str.Write(operand); break;
|
||||
case AddrMode::Stk: break;
|
||||
case AddrMode::StkRel: str.WriteAll(operand, ",S"); break;
|
||||
case AddrMode::StkRelIndIdxY: str.WriteAll('(', operand, ",S),Y"); break;
|
||||
case SnesAddrMode::Imp: break;
|
||||
case SnesAddrMode::RelLng: str.Write(operand); break;
|
||||
case SnesAddrMode::Rel: str.Write(operand); break;
|
||||
case SnesAddrMode::Stk: break;
|
||||
case SnesAddrMode::StkRel: str.WriteAll(operand, ",S"); break;
|
||||
case SnesAddrMode::StkRelIndIdxY: str.WriteAll('(', operand, ",S),Y"); break;
|
||||
|
||||
default: throw std::runtime_error("invalid address mode");
|
||||
}
|
||||
|
@ -92,8 +91,8 @@ uint32_t SnesDisUtils::GetOperandAddress(DisassemblyInfo &info, uint32_t memoryA
|
|||
opAddr = byteCode[1] | (byteCode[2] << 8) | (byteCode[3] << 16);
|
||||
}
|
||||
|
||||
AddrMode addrMode = SnesDisUtils::OpMode[byteCode[0]];
|
||||
if(addrMode == AddrMode::Rel || addrMode == AddrMode::RelLng) {
|
||||
SnesAddrMode addrMode = SnesDisUtils::OpMode[byteCode[0]];
|
||||
if(addrMode == SnesAddrMode::Rel || addrMode == SnesAddrMode::RelLng) {
|
||||
if(opSize == 2) {
|
||||
opAddr = (memoryAddr & 0xFF0000) | (((int8_t)opAddr + memoryAddr + 2) & 0xFFFF);
|
||||
} else {
|
||||
|
@ -117,53 +116,53 @@ int32_t SnesDisUtils::GetEffectiveAddress(DisassemblyInfo &info, SnesConsole *co
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool SnesDisUtils::HasEffectiveAddress(AddrMode addrMode)
|
||||
bool SnesDisUtils::HasEffectiveAddress(SnesAddrMode addrMode)
|
||||
{
|
||||
switch(addrMode) {
|
||||
case AddrMode::Acc:
|
||||
case AddrMode::Imp:
|
||||
case AddrMode::Stk:
|
||||
case AddrMode::Sig8:
|
||||
case AddrMode::Imm8:
|
||||
case AddrMode::Rel:
|
||||
case AddrMode::RelLng:
|
||||
case AddrMode::Imm16:
|
||||
case AddrMode::BlkMov:
|
||||
case AddrMode::AbsLngJmp:
|
||||
case AddrMode::AbsLng:
|
||||
case AddrMode::ImmX:
|
||||
case AddrMode::ImmM:
|
||||
case AddrMode::AbsJmp:
|
||||
case SnesAddrMode::Acc:
|
||||
case SnesAddrMode::Imp:
|
||||
case SnesAddrMode::Stk:
|
||||
case SnesAddrMode::Sig8:
|
||||
case SnesAddrMode::Imm8:
|
||||
case SnesAddrMode::Rel:
|
||||
case SnesAddrMode::RelLng:
|
||||
case SnesAddrMode::Imm16:
|
||||
case SnesAddrMode::BlkMov:
|
||||
case SnesAddrMode::AbsLngJmp:
|
||||
case SnesAddrMode::AbsLng:
|
||||
case SnesAddrMode::ImmX:
|
||||
case SnesAddrMode::ImmM:
|
||||
case SnesAddrMode::AbsJmp:
|
||||
return false;
|
||||
|
||||
case AddrMode::DirIdxIndX:
|
||||
case AddrMode::DirIdxX:
|
||||
case AddrMode::DirIdxY:
|
||||
case AddrMode::DirIndIdxY:
|
||||
case AddrMode::DirIndLngIdxY:
|
||||
case AddrMode::DirIndLng:
|
||||
case AddrMode::DirInd:
|
||||
case AddrMode::Dir:
|
||||
case AddrMode::StkRel:
|
||||
case AddrMode::StkRelIndIdxY:
|
||||
case AddrMode::Abs:
|
||||
case AddrMode::AbsIdxXInd:
|
||||
case AddrMode::AbsIdxX:
|
||||
case AddrMode::AbsIdxY:
|
||||
case AddrMode::AbsLngIdxX:
|
||||
case AddrMode::AbsInd:
|
||||
case AddrMode::AbsIndLng:
|
||||
case SnesAddrMode::DirIdxIndX:
|
||||
case SnesAddrMode::DirIdxX:
|
||||
case SnesAddrMode::DirIdxY:
|
||||
case SnesAddrMode::DirIndIdxY:
|
||||
case SnesAddrMode::DirIndLngIdxY:
|
||||
case SnesAddrMode::DirIndLng:
|
||||
case SnesAddrMode::DirInd:
|
||||
case SnesAddrMode::Dir:
|
||||
case SnesAddrMode::StkRel:
|
||||
case SnesAddrMode::StkRelIndIdxY:
|
||||
case SnesAddrMode::Abs:
|
||||
case SnesAddrMode::AbsIdxXInd:
|
||||
case SnesAddrMode::AbsIdxX:
|
||||
case SnesAddrMode::AbsIdxY:
|
||||
case SnesAddrMode::AbsLngIdxX:
|
||||
case SnesAddrMode::AbsInd:
|
||||
case SnesAddrMode::AbsIndLng:
|
||||
return true;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Invalid mode");
|
||||
}
|
||||
|
||||
uint8_t SnesDisUtils::GetOpSize(AddrMode addrMode, uint8_t flags)
|
||||
uint8_t SnesDisUtils::GetOpSize(SnesAddrMode addrMode, uint8_t flags)
|
||||
{
|
||||
if(addrMode == AddrMode::ImmX) {
|
||||
if(addrMode == SnesAddrMode::ImmX) {
|
||||
return (flags & ProcFlags::IndexMode8) ? 2 : 3;
|
||||
} else if(addrMode == AddrMode::ImmM) {
|
||||
} else if(addrMode == SnesAddrMode::ImmM) {
|
||||
return (flags & ProcFlags::MemoryMode8) ? 2 : 3;
|
||||
}
|
||||
|
||||
|
@ -254,10 +253,10 @@ string SnesDisUtils::OpName[256] = {
|
|||
"BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC" // F
|
||||
};
|
||||
|
||||
typedef AddrMode M;
|
||||
AddrMode SnesDisUtils::OpMode[256] = {
|
||||
typedef SnesAddrMode M;
|
||||
SnesAddrMode SnesDisUtils::OpMode[256] = {
|
||||
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
M::Sig8, M::DirIdxIndX, M::Sig8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 0
|
||||
M::Imm8, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 0
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Dir, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Acc, M::Imp, M::Abs, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 1
|
||||
M::Abs, M::DirIdxIndX, M::AbsLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 2
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Acc, M::Imp, M::AbsIdxX, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 3
|
||||
|
|
|
@ -7,20 +7,20 @@ class LabelManager;
|
|||
class EmuSettings;
|
||||
struct SnesCpuState;
|
||||
enum class CpuType : uint8_t;
|
||||
enum class AddrMode : uint8_t;
|
||||
enum class SnesAddrMode : uint8_t;
|
||||
|
||||
class SnesDisUtils
|
||||
{
|
||||
private:
|
||||
static uint8_t OpSize[0x1F];
|
||||
static uint32_t GetOperandAddress(DisassemblyInfo &info, uint32_t memoryAddr);
|
||||
static uint8_t GetOpSize(AddrMode addrMode, uint8_t flags);
|
||||
static uint8_t GetOpSize(SnesAddrMode addrMode, uint8_t flags);
|
||||
|
||||
static bool HasEffectiveAddress(AddrMode addrMode);
|
||||
static bool HasEffectiveAddress(SnesAddrMode addrMode);
|
||||
|
||||
public:
|
||||
static string OpName[256];
|
||||
static AddrMode OpMode[256];
|
||||
static SnesAddrMode OpMode[256];
|
||||
|
||||
static void GetDisassembly(DisassemblyInfo &info, string &out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings);
|
||||
static uint8_t GetOpSize(uint8_t opCode, uint8_t flags);
|
||||
|
@ -31,7 +31,7 @@ public:
|
|||
static int32_t GetEffectiveAddress(DisassemblyInfo &info, SnesConsole* console, SnesCpuState &state, CpuType type);
|
||||
};
|
||||
|
||||
enum class AddrMode : uint8_t
|
||||
enum class SnesAddrMode : uint8_t
|
||||
{
|
||||
Sig8,
|
||||
Imm8,
|
||||
|
|
|
@ -32,7 +32,9 @@ void BaseVideoFilter::SetBaseFrameInfo(FrameInfo frameInfo)
|
|||
|
||||
FrameInfo BaseVideoFilter::GetFrameInfo()
|
||||
{
|
||||
//TODO this is originally snes-specific code
|
||||
int overscanMultiplier = _baseFrameInfo.Width == 512 ? 2 : 1;
|
||||
|
||||
FrameInfo frameInfo = _baseFrameInfo;
|
||||
OverscanDimensions overscan = GetOverscan();
|
||||
frameInfo.Width -= overscan.Left * overscanMultiplier + overscan.Right * overscanMultiplier;
|
||||
|
|
|
@ -178,13 +178,22 @@ namespace Mesen.Debugger.ViewModels
|
|||
public async Task<bool> ApplyChanges(Window assemblerWindow)
|
||||
{
|
||||
MemoryType memType = _cpuType.ToMemoryType();
|
||||
|
||||
|
||||
List<byte> bytes = new List<byte>(_bytes);
|
||||
if(OriginalByteCount > 0) {
|
||||
byte nopOpCode = _cpuType.GetNopOpCode();
|
||||
while(OriginalByteCount > bytes.Count) {
|
||||
//Pad data with NOPs as needed
|
||||
bytes.Add(nopOpCode);
|
||||
}
|
||||
}
|
||||
|
||||
string addrFormat = memType.GetFormatString();
|
||||
UInt32 endAddress = (uint)(StartAddress + _bytes.Count - 1);
|
||||
UInt32 endAddress = (uint)(StartAddress + bytes.Count - 1);
|
||||
|
||||
List<string> warningMessages = new List<string>();
|
||||
if(Errors.Count > 0) {
|
||||
warningMessages.Add("Warning: The code contains parsing errors - lines with errors will be ignored.");
|
||||
warningMessages.Add("Warning: The code contains errors - lines with errors will be ignored.");
|
||||
}
|
||||
|
||||
if(_originalAddress >= 0) {
|
||||
|
@ -199,14 +208,6 @@ namespace Mesen.Debugger.ViewModels
|
|||
return false;
|
||||
}
|
||||
|
||||
List<byte> bytes = new List<byte>(_bytes);
|
||||
if(OriginalByteCount > 0) {
|
||||
byte nopOpCode = _cpuType.GetNopOpCode();
|
||||
while(OriginalByteCount < bytes.Count) {
|
||||
//Pad data with NOPs as needed
|
||||
bytes.Add(nopOpCode);
|
||||
}
|
||||
}
|
||||
|
||||
DebugApi.SetMemoryValues(memType, (uint)StartAddress, bytes.ToArray(), bytes.Count);
|
||||
if(OriginalByteCount > 0) {
|
||||
|
|
|
@ -267,11 +267,15 @@ namespace Mesen.Debugger.ViewModels
|
|||
StringBuilder sb = new StringBuilder();
|
||||
int i = SelectionStart;
|
||||
int endAddress = 0;
|
||||
CodeLineData? prevLine = null;
|
||||
do {
|
||||
CodeLineData[] data = dp.GetCodeLines(i, 5000);
|
||||
|
||||
for(int j = 0; j < data.Length; j++) {
|
||||
CodeLineData lineData = data[j];
|
||||
if(prevLine?.Address == lineData.Address && prevLine?.Text == lineData.Text) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(lineData.Address > SelectionEnd) {
|
||||
i = lineData.Address;
|
||||
break;
|
||||
|
@ -284,10 +288,15 @@ namespace Mesen.Debugger.ViewModels
|
|||
if(getHeaders) {
|
||||
codeString = "--------" + codeString + "--------";
|
||||
} else {
|
||||
if(j == data.Length - 1) {
|
||||
i = lineData.Address;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
prevLine = lineData;
|
||||
|
||||
int padding = Math.Max(commentSpacingCharCount, codeString.Length);
|
||||
if(codeString.Length == 0) {
|
||||
padding = 0;
|
||||
|
|
|
@ -123,6 +123,7 @@ namespace Mesen.Interop
|
|||
case CpuType.Snes:
|
||||
case CpuType.Gameboy:
|
||||
case CpuType.Nes:
|
||||
case CpuType.Pce:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
|
|
@ -35,4 +35,21 @@ public:
|
|||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static string Trim(string str)
|
||||
{
|
||||
return TrimLeft(TrimRight(str));
|
||||
}
|
||||
|
||||
static string ToUpper(string str)
|
||||
{
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
|
||||
return str;
|
||||
}
|
||||
|
||||
static string ToLower(string str)
|
||||
{
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue