Debugger: Assembler rewrite/refactoring for NES/SNES/PCE

This commit is contained in:
Sour 2022-05-21 15:56:16 -04:00
parent 34414743da
commit 55b96c9ebc
23 changed files with 1039 additions and 985 deletions

View file

@ -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" />

View file

@ -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">

View 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);

View 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();
}
};

View file

@ -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);

View file

@ -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");

View file

@ -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> &currentPassLabels)
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;
}

View file

@ -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> &currentPassLabels);
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);
};

View 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;
}

View 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;
};

View file

@ -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()

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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() {}
};

View file

@ -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"

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -123,6 +123,7 @@ namespace Mesen.Interop
case CpuType.Snes:
case CpuType.Gameboy:
case CpuType.Nes:
case CpuType.Pce:
return true;
default:

View file

@ -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;
}
};