Merge branch 'reformat_code'
This commit is contained in:
commit
daf3b57e89
573 changed files with 82336 additions and 65195 deletions
|
@ -17,22 +17,28 @@ void AluMulDiv::Run(bool isRead)
|
|||
{
|
||||
uint64_t cpuCycle = _cpu->GetCycleCount();
|
||||
|
||||
if(isRead) {
|
||||
if (isRead)
|
||||
{
|
||||
//Run 1 cycle less for read operations, since they occur earlier within the CPU cycle, compared to a write
|
||||
cpuCycle--;
|
||||
}
|
||||
|
||||
if(_multCounter != 0 || _divCounter != 0) {
|
||||
if (_multCounter != 0 || _divCounter != 0)
|
||||
{
|
||||
uint64_t cyclesToRun = cpuCycle - _prevCpuCycle;
|
||||
while(cyclesToRun--) {
|
||||
if(!_multCounter && !_divCounter) {
|
||||
while (cyclesToRun--)
|
||||
{
|
||||
if (!_multCounter && !_divCounter)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(_multCounter > 0) {
|
||||
if (_multCounter > 0)
|
||||
{
|
||||
_multCounter--;
|
||||
|
||||
if(_state.DivResult & 0x01) {
|
||||
if (_state.DivResult & 0x01)
|
||||
{
|
||||
_state.MultOrRemainderResult += _shift;
|
||||
}
|
||||
|
||||
|
@ -40,19 +46,21 @@ void AluMulDiv::Run(bool isRead)
|
|||
_state.DivResult >>= 1;
|
||||
}
|
||||
|
||||
if(_divCounter > 0) {
|
||||
if (_divCounter > 0)
|
||||
{
|
||||
_divCounter--;
|
||||
_shift >>= 1;
|
||||
_state.DivResult <<= 1;
|
||||
|
||||
if(_state.MultOrRemainderResult >= _shift) {
|
||||
if (_state.MultOrRemainderResult >= _shift)
|
||||
{
|
||||
_state.MultOrRemainderResult -= _shift;
|
||||
_state.DivResult |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_prevCpuCycle = cpuCycle;
|
||||
}
|
||||
|
||||
|
@ -60,12 +68,13 @@ uint8_t AluMulDiv::Read(uint16_t addr)
|
|||
{
|
||||
Run(true);
|
||||
|
||||
switch(addr) {
|
||||
case 0x4214: return (uint8_t)_state.DivResult;
|
||||
case 0x4215: return (uint8_t)(_state.DivResult >> 8);
|
||||
switch (addr)
|
||||
{
|
||||
case 0x4214: return (uint8_t)_state.DivResult;
|
||||
case 0x4215: return (uint8_t)(_state.DivResult >> 8);
|
||||
|
||||
case 0x4216: return (uint8_t)_state.MultOrRemainderResult;
|
||||
case 0x4217: return (uint8_t)(_state.MultOrRemainderResult >> 8);
|
||||
case 0x4216: return (uint8_t)_state.MultOrRemainderResult;
|
||||
case 0x4217: return (uint8_t)(_state.MultOrRemainderResult >> 8);
|
||||
}
|
||||
|
||||
throw std::runtime_error("ALU: invalid address");
|
||||
|
@ -75,33 +84,39 @@ void AluMulDiv::Write(uint16_t addr, uint8_t value)
|
|||
{
|
||||
Run(false);
|
||||
|
||||
switch(addr) {
|
||||
case 0x4202: _state.MultOperand1 = value; break;
|
||||
case 0x4203:
|
||||
_state.MultOrRemainderResult = 0;
|
||||
if(!_divCounter && !_multCounter) {
|
||||
_multCounter = 8;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x4202: _state.MultOperand1 = value;
|
||||
break;
|
||||
case 0x4203:
|
||||
_state.MultOrRemainderResult = 0;
|
||||
if (!_divCounter && !_multCounter)
|
||||
{
|
||||
_multCounter = 8;
|
||||
|
||||
_state.MultOperand2 = value;
|
||||
_state.DivResult = (value << 8) | _state.MultOperand1;
|
||||
_shift = value;
|
||||
}
|
||||
break;
|
||||
_state.MultOperand2 = value;
|
||||
_state.DivResult = (value << 8) | _state.MultOperand1;
|
||||
_shift = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4204: _state.Dividend = (_state.Dividend & 0xFF00) | value; break;
|
||||
case 0x4205: _state.Dividend = (_state.Dividend & 0xFF) | (value << 8); break;
|
||||
|
||||
case 0x4206:
|
||||
_state.MultOrRemainderResult = _state.Dividend;
|
||||
case 0x4204: _state.Dividend = (_state.Dividend & 0xFF00) | value;
|
||||
break;
|
||||
case 0x4205: _state.Dividend = (_state.Dividend & 0xFF) | (value << 8);
|
||||
break;
|
||||
|
||||
if(!_divCounter && !_multCounter) {
|
||||
_divCounter = 16;
|
||||
_state.Divisor = value;
|
||||
_shift = (value << 16);
|
||||
}
|
||||
break;
|
||||
case 0x4206:
|
||||
_state.MultOrRemainderResult = _state.Dividend;
|
||||
|
||||
default: throw std::runtime_error("ALU: invalid address");
|
||||
if (!_divCounter && !_multCounter)
|
||||
{
|
||||
_divCounter = 16;
|
||||
_state.Divisor = value;
|
||||
_shift = (value << 16);
|
||||
}
|
||||
break;
|
||||
|
||||
default: throw std::runtime_error("ALU: invalid address");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,10 +125,11 @@ AluState AluMulDiv::GetState()
|
|||
return _state;
|
||||
}
|
||||
|
||||
void AluMulDiv::Serialize(Serializer &s)
|
||||
void AluMulDiv::Serialize(Serializer& s)
|
||||
{
|
||||
s.Stream(
|
||||
_state.MultOperand1, _state.MultOperand2, _state.MultOrRemainderResult, _state.Dividend, _state.Divisor, _state.DivResult,
|
||||
_state.MultOperand1, _state.MultOperand2, _state.MultOrRemainderResult, _state.Dividend, _state.Divisor,
|
||||
_state.DivResult,
|
||||
_divCounter, _multCounter, _shift, _prevCpuCycle
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ class Cpu;
|
|||
class AluMulDiv final : public ISerializable
|
||||
{
|
||||
private:
|
||||
Cpu *_cpu;
|
||||
Cpu* _cpu;
|
||||
|
||||
uint64_t _prevCpuCycle = 0;
|
||||
|
||||
|
@ -17,10 +17,10 @@ private:
|
|||
uint32_t _shift = 0;
|
||||
uint8_t _multCounter = 0;
|
||||
uint8_t _divCounter = 0;
|
||||
|
||||
|
||||
public:
|
||||
void Initialize(Cpu* cpu);
|
||||
|
||||
|
||||
void Run(bool isRead);
|
||||
|
||||
uint8_t Read(uint16_t addr);
|
||||
|
@ -28,5 +28,5 @@ public:
|
|||
|
||||
AluState GetState();
|
||||
|
||||
void Serialize(Serializer &s) override;
|
||||
};
|
||||
void Serialize(Serializer& s) override;
|
||||
};
|
||||
|
|
|
@ -9,60 +9,84 @@
|
|||
#include "DisassemblyInfo.h"
|
||||
#include "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 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);
|
||||
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 Assembler::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)
|
||||
void Assembler::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)
|
||||
{
|
||||
//Remove extra spaces as part of processing
|
||||
size_t offset = code.find_first_of(',', 0);
|
||||
if(offset != string::npos) {
|
||||
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) {
|
||||
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)) {
|
||||
if (std::regex_search(code, match, byteRegex))
|
||||
{
|
||||
vector<string> bytes = StringUtilities::Split(match.str(1) + match.str(3), ' ');
|
||||
for(string& byte : bytes) {
|
||||
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)) {
|
||||
}
|
||||
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()) {
|
||||
if (currentPassLabels.find(match.str(1)) != currentPassLabels.end())
|
||||
{
|
||||
output.push_back(AssemblerSpecialCodes::LabelRedefinition);
|
||||
} else {
|
||||
}
|
||||
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)) {
|
||||
}
|
||||
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) {
|
||||
}
|
||||
else if (std::regex_search(code, match, instRegex) && match.size() > 1)
|
||||
{
|
||||
LineData lineData;
|
||||
AssemblerSpecialCodes result = GetLineData(match, lineData, labels, firstPass);
|
||||
if(result == AssemblerSpecialCodes::OK) {
|
||||
if (result == AssemblerSpecialCodes::OK)
|
||||
{
|
||||
AssembleInstruction(lineData, instructionAddress, output, firstPass);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
output.push_back(result);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
output.push_back(AssemblerSpecialCodes::ParsingError);
|
||||
}
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes Assembler::GetLineData(std::smatch match, LineData& lineData, std::unordered_map<string, uint32_t>& labels, bool firstPass)
|
||||
AssemblerSpecialCodes Assembler::GetLineData(std::smatch match, LineData& lineData,
|
||||
std::unordered_map<string, uint32_t>& labels, bool firstPass)
|
||||
{
|
||||
bool isBinary = match.str(2).length() > 1 && match.str(2)[1] == '%'; //Immediate + binary: "#%"
|
||||
|
||||
|
@ -74,68 +98,93 @@ AssemblerSpecialCodes Assembler::GetLineData(std::smatch match, LineData& lineDa
|
|||
lineData.HasOpeningParenthesis = match.str(3) == "(";
|
||||
lineData.HasOpeningBracket = match.str(3) == "[";
|
||||
|
||||
std::transform(lineData.OperandSuffix.begin(), lineData.OperandSuffix.end(), lineData.OperandSuffix.begin(), ::toupper);
|
||||
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) {
|
||||
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'))) {
|
||||
}
|
||||
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') {
|
||||
}
|
||||
else if (isBinary && c != '0' && c != '1')
|
||||
{
|
||||
return AssemblerSpecialCodes::InvalidBinaryValue;
|
||||
}
|
||||
|
||||
lineData.Operand.push_back(c);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
foundSpace = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(isBinary) {
|
||||
if (isBinary)
|
||||
{
|
||||
//Convert the binary value to hex
|
||||
if(lineData.Operand.size() == 0) {
|
||||
if (lineData.Operand.size() == 0)
|
||||
{
|
||||
return AssemblerSpecialCodes::MissingOperand;
|
||||
} else if(lineData.Operand.size() <= 8) {
|
||||
}
|
||||
else if (lineData.Operand.size() <= 8)
|
||||
{
|
||||
lineData.IsHex = true;
|
||||
int value = 0;
|
||||
for(size_t i = 0; i < lineData.Operand.size(); i++) {
|
||||
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 {
|
||||
}
|
||||
else
|
||||
{
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
}
|
||||
|
||||
if(!lineData.HasComment && !match.str(9).empty()) {
|
||||
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) {
|
||||
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) {
|
||||
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')) {
|
||||
if ((lineData.Operand[i] < '0' || lineData.Operand[i] > '9'))
|
||||
{
|
||||
allNumeric = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allNumeric && !lineData.Operand.empty()) {
|
||||
if (allNumeric && !lineData.Operand.empty())
|
||||
{
|
||||
//Operand is not empty, and it only contains decimal values
|
||||
lineData.IsDecimal = true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
lineData.IsDecimal = false;
|
||||
}
|
||||
}
|
||||
|
@ -143,175 +192,292 @@ AssemblerSpecialCodes Assembler::GetLineData(std::smatch match, LineData& lineDa
|
|||
return GetAddrModeAndOperandSize(lineData, labels, firstPass);
|
||||
}
|
||||
|
||||
AssemblerSpecialCodes Assembler::GetAddrModeAndOperandSize(LineData& lineData, std::unordered_map<string, uint32_t>& labels, bool firstPass)
|
||||
AssemblerSpecialCodes Assembler::GetAddrModeAndOperandSize(LineData& 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) {
|
||||
if (lineData.IsHex)
|
||||
{
|
||||
if (operand.size() == 0)
|
||||
{
|
||||
return AssemblerSpecialCodes::MissingOperand;
|
||||
} else if(operand.size() <= 2) {
|
||||
}
|
||||
else if (operand.size() <= 2)
|
||||
{
|
||||
opSize = 1;
|
||||
} else if(operand.size() <= 4) {
|
||||
}
|
||||
else if (operand.size() <= 4)
|
||||
{
|
||||
opSize = 2;
|
||||
} else if(operand.size() <= 6) {
|
||||
}
|
||||
else if (operand.size() <= 6)
|
||||
{
|
||||
opSize = 3;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(lineData.IsDecimal) {
|
||||
}
|
||||
else if (lineData.IsDecimal)
|
||||
{
|
||||
int value = std::stoi(operand.c_str());
|
||||
if(value < -0x800000) {
|
||||
if (value < -0x800000)
|
||||
{
|
||||
//< -2^23 is invalid
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
} else if(value < -0x8000) {
|
||||
}
|
||||
else if (value < -0x8000)
|
||||
{
|
||||
opSize = 3;
|
||||
} else if(value < -0x80) {
|
||||
}
|
||||
else if (value < -0x80)
|
||||
{
|
||||
opSize = 2;
|
||||
} else if(value <= 0xFF) {
|
||||
}
|
||||
else if (value <= 0xFF)
|
||||
{
|
||||
opSize = 1;
|
||||
} else if(value <= 0xFFFF) {
|
||||
}
|
||||
else if (value <= 0xFFFF)
|
||||
{
|
||||
opSize = 2;
|
||||
} else if(value <= 0xFFFFFF) {
|
||||
}
|
||||
else if (value <= 0xFFFFFF)
|
||||
{
|
||||
opSize = 3;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//>= 2^23 is invalid
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(!operand.empty()) {
|
||||
}
|
||||
else if (!operand.empty())
|
||||
{
|
||||
//Check if the operand is a known label
|
||||
auto findResult = labels.find(operand);
|
||||
if(findResult != labels.end()) {
|
||||
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) {
|
||||
}
|
||||
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 {
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t addr = _labelManager->GetLabelRelativeAddress(operand);
|
||||
if(addr > 0xFFFF) {
|
||||
if (addr > 0xFFFF)
|
||||
{
|
||||
lineData.Operand = HexUtilities::ToHex24(addr);
|
||||
lineData.IsHex = true;
|
||||
opSize = 3;
|
||||
} else if(addr > 0xFF) {
|
||||
}
|
||||
else if (addr > 0xFF)
|
||||
{
|
||||
lineData.Operand = HexUtilities::ToHex((uint16_t)addr);
|
||||
lineData.IsHex = true;
|
||||
opSize = 2;
|
||||
} else if(addr >= 0) {
|
||||
}
|
||||
else if (addr >= 0)
|
||||
{
|
||||
lineData.Operand = HexUtilities::ToHex((uint8_t)addr);
|
||||
lineData.IsHex = true;
|
||||
opSize = 1;
|
||||
} else {
|
||||
if(firstPass) {
|
||||
}
|
||||
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 {
|
||||
}
|
||||
else
|
||||
{
|
||||
return AssemblerSpecialCodes::UnknownLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//No operand
|
||||
opSize = 0;
|
||||
}
|
||||
|
||||
if(lineData.Mode == AddrMode::Imp) {
|
||||
if(lineData.OperandSuffix.substr(0, 2) == ",$") {
|
||||
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) {
|
||||
}
|
||||
else if (lineData.IsImmediate)
|
||||
{
|
||||
if (lineData.HasOpeningParenthesis || lineData.HasOpeningBracket || opSize == 0)
|
||||
{
|
||||
invalid = true;
|
||||
} else if(opSize >= 3) {
|
||||
}
|
||||
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.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) {
|
||||
}
|
||||
else if (lineData.OperandSuffix == "],Y")
|
||||
{
|
||||
if (opSize == 1)
|
||||
{
|
||||
lineData.Mode = AddrMode::DirIndLngIdxY;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
} else if(lineData.HasOpeningParenthesis) {
|
||||
if(lineData.OperandSuffix == ")") {
|
||||
}
|
||||
else if (lineData.HasOpeningParenthesis)
|
||||
{
|
||||
if (lineData.OperandSuffix == ")")
|
||||
{
|
||||
lineData.Mode = opSize == 1 ? AddrMode::DirInd : AddrMode::AbsInd;
|
||||
} else if(lineData.OperandSuffix == ",X)") {
|
||||
}
|
||||
else if (lineData.OperandSuffix == ",X)")
|
||||
{
|
||||
lineData.Mode = opSize == 1 ? AddrMode::DirIdxIndX : AddrMode::AbsIdxXInd;
|
||||
} else if(lineData.OperandSuffix == "),Y") {
|
||||
if(opSize == 1) {
|
||||
}
|
||||
else if (lineData.OperandSuffix == "),Y")
|
||||
{
|
||||
if (opSize == 1)
|
||||
{
|
||||
lineData.Mode = AddrMode::DirIndIdxY;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(lineData.OperandSuffix == ",S),Y") {
|
||||
if(opSize == 1) {
|
||||
}
|
||||
else if (lineData.OperandSuffix == ",S),Y")
|
||||
{
|
||||
if (opSize == 1)
|
||||
{
|
||||
lineData.Mode = AddrMode::StkRelIndIdxY;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
invalid = true;
|
||||
}
|
||||
} else {
|
||||
if(lineData.OperandSuffix == ",X") {
|
||||
if(opSize == 3) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lineData.OperandSuffix == ",X")
|
||||
{
|
||||
if (opSize == 3)
|
||||
{
|
||||
lineData.Mode = AddrMode::AbsLngIdxX;
|
||||
} else if(opSize == 2) {
|
||||
}
|
||||
else if (opSize == 2)
|
||||
{
|
||||
lineData.Mode = AddrMode::AbsIdxX;
|
||||
} else if(opSize == 1) {
|
||||
}
|
||||
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 {
|
||||
}
|
||||
else
|
||||
{
|
||||
invalid = true;
|
||||
}
|
||||
} else if(lineData.OperandSuffix == ",Y") {
|
||||
if(opSize == 2) {
|
||||
}
|
||||
else if (lineData.OperandSuffix == ",Y")
|
||||
{
|
||||
if (opSize == 2)
|
||||
{
|
||||
lineData.Mode = AddrMode::AbsIdxY;
|
||||
} else if(opSize == 1) {
|
||||
}
|
||||
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 {
|
||||
}
|
||||
else
|
||||
{
|
||||
invalid = true;
|
||||
}
|
||||
} else if(lineData.OperandSuffix == ",S") {
|
||||
if(opSize == 1) {
|
||||
}
|
||||
else if (lineData.OperandSuffix == ",S")
|
||||
{
|
||||
if (opSize == 1)
|
||||
{
|
||||
lineData.Mode = AddrMode::StkRel;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return AssemblerSpecialCodes::OperandOutOfRange;
|
||||
}
|
||||
} else if(lineData.OperandSuffix.empty()) {
|
||||
if(opSize == 0) {
|
||||
}
|
||||
else if (lineData.OperandSuffix.empty())
|
||||
{
|
||||
if (opSize == 0)
|
||||
{
|
||||
lineData.Mode = AddrMode::Imp; //or Acc
|
||||
} else if(opSize == 3) {
|
||||
}
|
||||
else if (opSize == 3)
|
||||
{
|
||||
lineData.Mode = AddrMode::AbsLng;
|
||||
} else if(opSize == 2) {
|
||||
}
|
||||
else if (opSize == 2)
|
||||
{
|
||||
lineData.Mode = AddrMode::Abs;
|
||||
} else if(opSize == 1) {
|
||||
}
|
||||
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 {
|
||||
}
|
||||
else
|
||||
{
|
||||
invalid = true;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
|
@ -331,41 +497,60 @@ bool Assembler::IsOpModeAvailable(string& opCode, AddrMode mode)
|
|||
return _availableModesByOpName[opCode].find((int)mode) != _availableModesByOpName[opCode].end();
|
||||
}
|
||||
|
||||
void Assembler::AssembleInstruction(LineData& lineData, uint32_t& instructionAddress, vector<int16_t>& output, bool firstPass)
|
||||
void Assembler::AssembleInstruction(LineData& lineData, uint32_t& instructionAddress, vector<int16_t>& output,
|
||||
bool firstPass)
|
||||
{
|
||||
bool foundMatch = false;
|
||||
|
||||
for(int i = 0; i < 256; i++) {
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
AddrMode opMode = CpuDisUtils::OpMode[i];
|
||||
if(lineData.OpCode == CpuDisUtils::OpName[i]) {
|
||||
if (lineData.OpCode == CpuDisUtils::OpName[i])
|
||||
{
|
||||
bool modeMatch = opMode == lineData.Mode;
|
||||
if(!modeMatch) {
|
||||
if(lineData.Mode == AddrMode::Imp && (opMode == AddrMode::Acc || opMode == AddrMode::Stk)) {
|
||||
if (!modeMatch)
|
||||
{
|
||||
if (lineData.Mode == AddrMode::Imp && (opMode == AddrMode::Acc || opMode == AddrMode::Stk))
|
||||
{
|
||||
modeMatch = true;
|
||||
} else if(lineData.Mode == AddrMode::Imm8 && opMode == AddrMode::Sig8) {
|
||||
}
|
||||
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)) {
|
||||
}
|
||||
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)) {
|
||||
}
|
||||
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;
|
||||
|
||||
//Convert "absolute" jump to a relative jump
|
||||
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
|
||||
if(lineData.Mode == AddrMode::Abs) {
|
||||
if (lineData.Mode == AddrMode::Abs)
|
||||
{
|
||||
value |= (instructionAddress & 0xFF0000);
|
||||
}
|
||||
|
||||
int32_t addressGap;
|
||||
if(lineData.Mode == AddrMode::Dir) {
|
||||
if (lineData.Mode == AddrMode::Dir)
|
||||
{
|
||||
addressGap = (int8_t)value;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
addressGap = value - (instructionAddress + (lngBranch ? 3 : 2));
|
||||
}
|
||||
|
||||
if(addressGap > (lngBranch ? 32767 : 127) || addressGap < (lngBranch ? -32768 : -128)) {
|
||||
if (addressGap > (lngBranch ? 32767 : 127) || addressGap < (lngBranch ? -32768 : -128))
|
||||
{
|
||||
//Gap too long, can't jump that far
|
||||
if(!firstPass) {
|
||||
if (!firstPass)
|
||||
{
|
||||
//Pretend this is ok on first pass, we're just trying to find all labels
|
||||
output.push_back(AssemblerSpecialCodes::OutOfRangeJump);
|
||||
return;
|
||||
|
@ -375,22 +560,30 @@ void Assembler::AssembleInstruction(LineData& lineData, uint32_t& instructionAdd
|
|||
//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);
|
||||
lineData.Operand = lngBranch
|
||||
? HexUtilities::ToHex((uint16_t)addressGap)
|
||||
: HexUtilities::ToHex((uint8_t)addressGap);
|
||||
}
|
||||
}
|
||||
|
||||
if(modeMatch) {
|
||||
if (modeMatch)
|
||||
{
|
||||
output.push_back(i);
|
||||
instructionAddress += (lineData.OperandSize + 1);
|
||||
|
||||
if(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) {
|
||||
}
|
||||
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) {
|
||||
}
|
||||
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);
|
||||
|
@ -403,9 +596,12 @@ void Assembler::AssembleInstruction(LineData& lineData, uint32_t& instructionAdd
|
|||
}
|
||||
}
|
||||
|
||||
if(!foundMatch) {
|
||||
if (!foundMatch)
|
||||
{
|
||||
output.push_back(AssemblerSpecialCodes::InvalidInstruction);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
output.push_back(AssemblerSpecialCodes::EndOfLine);
|
||||
}
|
||||
}
|
||||
|
@ -421,8 +617,10 @@ Assembler::~Assembler()
|
|||
|
||||
uint32_t Assembler::AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode)
|
||||
{
|
||||
for(uint8_t i = 0; i < 255; i++) {
|
||||
if(_availableModesByOpName.find(CpuDisUtils::OpName[i]) == _availableModesByOpName.end()) {
|
||||
for (uint8_t i = 0; i < 255; i++)
|
||||
{
|
||||
if (_availableModesByOpName.find(CpuDisUtils::OpName[i]) == _availableModesByOpName.end())
|
||||
{
|
||||
_availableModesByOpName[CpuDisUtils::OpName[i]] = std::unordered_set<int>();
|
||||
}
|
||||
_availableModesByOpName[CpuDisUtils::OpName[i]].emplace((int)CpuDisUtils::OpMode[i]);
|
||||
|
@ -439,13 +637,17 @@ uint32_t Assembler::AssembleCode(string code, uint32_t startAddress, int16_t* as
|
|||
|
||||
vector<string> codeLines;
|
||||
codeLines.reserve(100);
|
||||
while(i < code.size()) {
|
||||
while (i < code.size())
|
||||
{
|
||||
size_t offset = code.find_first_of('\n', i);
|
||||
string line;
|
||||
if(offset != string::npos) {
|
||||
if (offset != string::npos)
|
||||
{
|
||||
line = code.substr(i, offset - i);
|
||||
i = offset + 1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
line = code.substr(i);
|
||||
i = code.size();
|
||||
}
|
||||
|
@ -454,15 +656,18 @@ uint32_t Assembler::AssembleCode(string code, uint32_t startAddress, int16_t* as
|
|||
|
||||
//Make 2 passes - first one to find all labels, second to assemble
|
||||
_needSecondPass = false;
|
||||
for(string& line : codeLines) {
|
||||
for (string& line : codeLines)
|
||||
{
|
||||
ProcessLine(line, startAddress, output, temporaryLabels, true, currentPassLabels);
|
||||
}
|
||||
|
||||
if(_needSecondPass) {
|
||||
if (_needSecondPass)
|
||||
{
|
||||
currentPassLabels.clear();
|
||||
output.clear();
|
||||
startAddress = originalStartAddr;
|
||||
for(string& line : codeLines) {
|
||||
for (string& line : codeLines)
|
||||
{
|
||||
ProcessLine(line, startAddress, output, temporaryLabels, false, currentPassLabels);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,9 +28,13 @@ private:
|
|||
bool _needSecondPass;
|
||||
|
||||
shared_ptr<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, LineData& lineData, std::unordered_map<string, uint32_t>& labels, bool firstPass);
|
||||
AssemblerSpecialCodes GetAddrModeAndOperandSize(LineData& lineData, std::unordered_map<string, uint32_t>& labels, bool firstPass);
|
||||
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, LineData& lineData,
|
||||
std::unordered_map<string, uint32_t>& labels, bool firstPass);
|
||||
AssemblerSpecialCodes GetAddrModeAndOperandSize(LineData& lineData, std::unordered_map<string, uint32_t>& labels,
|
||||
bool firstPass);
|
||||
void AssembleInstruction(LineData& lineData, uint32_t& instructionAddress, vector<int16_t>& output, bool firstPass);
|
||||
|
||||
bool IsOpModeAvailable(string& opCode, AddrMode mode);
|
||||
|
@ -40,4 +44,4 @@ public:
|
|||
virtual ~Assembler();
|
||||
|
||||
uint32_t AssembleCode(string code, uint32_t startAddress, int16_t* assembledCode);
|
||||
};
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,7 +23,7 @@ enum class ConsoleRegion;
|
|||
class BaseCartridge : public ISerializable
|
||||
{
|
||||
private:
|
||||
Console *_console;
|
||||
Console* _console;
|
||||
|
||||
vector<unique_ptr<IMemoryHandler>> _prgRomHandlers;
|
||||
vector<unique_ptr<IMemoryHandler>> _saveRamHandlers;
|
||||
|
@ -32,12 +32,12 @@ private:
|
|||
|
||||
bool _needCoprocSync = false;
|
||||
unique_ptr<BaseCoprocessor> _coprocessor;
|
||||
|
||||
NecDsp *_necDsp = nullptr;
|
||||
Sa1 *_sa1 = nullptr;
|
||||
Gsu *_gsu = nullptr;
|
||||
Cx4 *_cx4 = nullptr;
|
||||
SuperGameboy *_sgb = nullptr;
|
||||
|
||||
NecDsp* _necDsp = nullptr;
|
||||
Sa1* _sa1 = nullptr;
|
||||
Gsu* _gsu = nullptr;
|
||||
Cx4* _cx4 = nullptr;
|
||||
SuperGameboy* _sgb = nullptr;
|
||||
BsxCart* _bsx = nullptr;
|
||||
unique_ptr<BsxMemoryPack> _bsxMemPack;
|
||||
unique_ptr<Gameboy> _gameboy;
|
||||
|
@ -51,11 +51,11 @@ private:
|
|||
|
||||
uint8_t* _prgRom = nullptr;
|
||||
uint8_t* _saveRam = nullptr;
|
||||
|
||||
|
||||
uint32_t _prgRomSize = 0;
|
||||
uint32_t _saveRamSize = 0;
|
||||
uint32_t _coprocessorRamSize = 0;
|
||||
|
||||
|
||||
shared_ptr<SpcFileData> _spcData;
|
||||
vector<uint8_t> _embeddedFirmware;
|
||||
|
||||
|
@ -71,7 +71,7 @@ private:
|
|||
bool MapSpecificCarts(MemoryMappings& mm);
|
||||
void MapBsxMemoryPack(MemoryMappings& mm);
|
||||
void ApplyConfigOverrides();
|
||||
|
||||
|
||||
void LoadRom();
|
||||
void LoadSpc();
|
||||
bool LoadGameboy(VirtualFile& romFile, bool sgbEnabled);
|
||||
|
@ -85,13 +85,13 @@ private:
|
|||
public:
|
||||
virtual ~BaseCartridge();
|
||||
|
||||
static shared_ptr<BaseCartridge> CreateCartridge(Console* console, VirtualFile &romFile, VirtualFile &patchFile);
|
||||
static shared_ptr<BaseCartridge> CreateCartridge(Console* console, VirtualFile& romFile, VirtualFile& patchFile);
|
||||
|
||||
void Reset();
|
||||
|
||||
void SaveBattery();
|
||||
|
||||
void Init(MemoryMappings &mm);
|
||||
void Init(MemoryMappings& mm);
|
||||
|
||||
RomInfo GetRomInfo();
|
||||
vector<uint8_t> GetOriginalPrgRom();
|
||||
|
@ -100,7 +100,7 @@ public:
|
|||
string GetSha1Hash();
|
||||
CartFlags::CartFlags GetCartFlags();
|
||||
|
||||
void RegisterHandlers(MemoryMappings &mm);
|
||||
void RegisterHandlers(MemoryMappings& mm);
|
||||
|
||||
uint8_t* DebugGetPrgRom() { return _prgRom; }
|
||||
uint8_t* DebugGetSaveRam() { return _saveRam; }
|
||||
|
@ -117,10 +117,11 @@ public:
|
|||
Gameboy* GetGameboy();
|
||||
|
||||
void RunCoprocessors();
|
||||
|
||||
|
||||
__forceinline void SyncCoprocessors()
|
||||
{
|
||||
if(_needCoprocSync) {
|
||||
if (_needCoprocSync)
|
||||
{
|
||||
_coprocessor->Run();
|
||||
}
|
||||
}
|
||||
|
@ -132,5 +133,5 @@ public:
|
|||
|
||||
SpcFileData* GetSpcData();
|
||||
|
||||
void Serialize(Serializer &s) override;
|
||||
void Serialize(Serializer& s) override;
|
||||
};
|
||||
|
|
|
@ -44,7 +44,8 @@ bool BaseControlDevice::IsExpansionDevice()
|
|||
|
||||
void BaseControlDevice::StrobeProcessRead()
|
||||
{
|
||||
if(_strobe) {
|
||||
if (_strobe)
|
||||
{
|
||||
RefreshStateBuffer();
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +55,8 @@ void BaseControlDevice::StrobeProcessWrite(uint8_t value)
|
|||
bool prevStrobe = _strobe;
|
||||
_strobe = (value & 0x01) == 0x01;
|
||||
|
||||
if(prevStrobe && !_strobe) {
|
||||
if (prevStrobe && !_strobe)
|
||||
{
|
||||
RefreshStateBuffer();
|
||||
}
|
||||
}
|
||||
|
@ -82,17 +84,25 @@ void BaseControlDevice::SetTextState(string textState)
|
|||
auto lock = _stateLock.AcquireSafe();
|
||||
ClearState();
|
||||
|
||||
if(IsRawString()) {
|
||||
if (IsRawString())
|
||||
{
|
||||
_state.State.insert(_state.State.end(), textState.begin(), textState.end());
|
||||
} else {
|
||||
if(HasCoordinates()) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HasCoordinates())
|
||||
{
|
||||
vector<string> data = StringUtilities::Split(textState, ' ');
|
||||
if(data.size() >= 3) {
|
||||
if (data.size() >= 3)
|
||||
{
|
||||
MousePosition pos;
|
||||
try {
|
||||
try
|
||||
{
|
||||
pos.X = (int16_t)std::stol(data[0]);
|
||||
pos.Y = (int16_t)std::stol(data[1]);
|
||||
} catch(std::exception&) {
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
pos.X = -1;
|
||||
pos.Y = -1;
|
||||
}
|
||||
|
@ -102,10 +112,13 @@ void BaseControlDevice::SetTextState(string textState)
|
|||
}
|
||||
|
||||
int i = 0;
|
||||
for(char c : textState) {
|
||||
if(c != ':') {
|
||||
for (char c : textState)
|
||||
{
|
||||
if (c != ':')
|
||||
{
|
||||
//Ignore colons (used by multitap to separate inputs)
|
||||
if(c != '.') {
|
||||
if (c != '.')
|
||||
{
|
||||
SetBit(i);
|
||||
}
|
||||
i++;
|
||||
|
@ -117,24 +130,32 @@ void BaseControlDevice::SetTextState(string textState)
|
|||
string BaseControlDevice::GetTextState()
|
||||
{
|
||||
auto lock = _stateLock.AcquireSafe();
|
||||
if(IsRawString()) {
|
||||
if (IsRawString())
|
||||
{
|
||||
return string((char*)_state.State.data(), _state.State.size());
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
string keyNames = GetKeyNames();
|
||||
string output = "";
|
||||
|
||||
if(HasCoordinates()) {
|
||||
if (HasCoordinates())
|
||||
{
|
||||
MousePosition pos = GetCoordinates();
|
||||
output += std::to_string(pos.X) + " " + std::to_string(pos.Y) + " ";
|
||||
}
|
||||
|
||||
int keyNumber = 0;
|
||||
for(size_t i = 0; i < keyNames.size(); i++) {
|
||||
if(keyNames[i] != ':') {
|
||||
for (size_t i = 0; i < keyNames.size(); i++)
|
||||
{
|
||||
if (keyNames[i] != ':')
|
||||
{
|
||||
//Ignore colons in string (used by multitap to split controllers)
|
||||
output += IsPressed((uint8_t)keyNumber) ? keyNames[i] : '.';
|
||||
keyNumber++;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
output += ':';
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +170,8 @@ void BaseControlDevice::EnsureCapacity(int32_t minBitCount)
|
|||
uint32_t minByteCount = minBitCount / 8 + 1 + (HasCoordinates() ? 32 : 0);
|
||||
int32_t gap = minByteCount - (int32_t)_state.State.size();
|
||||
|
||||
if(gap > 0) {
|
||||
if (gap > 0)
|
||||
{
|
||||
_state.State.insert(_state.State.end(), gap, 0);
|
||||
}
|
||||
}
|
||||
|
@ -179,9 +201,12 @@ bool BaseControlDevice::IsPressed(uint8_t bit)
|
|||
|
||||
void BaseControlDevice::SetBitValue(uint8_t bit, bool set)
|
||||
{
|
||||
if(set) {
|
||||
if (set)
|
||||
{
|
||||
SetBit(bit);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearBit(bit);
|
||||
}
|
||||
}
|
||||
|
@ -204,23 +229,28 @@ void BaseControlDevice::ClearBit(uint8_t bit)
|
|||
|
||||
void BaseControlDevice::InvertBit(uint8_t bit)
|
||||
{
|
||||
if(IsPressed(bit)) {
|
||||
if (IsPressed(bit))
|
||||
{
|
||||
ClearBit(bit);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetBit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetPressedState(uint8_t bit, uint32_t keyCode)
|
||||
{
|
||||
if(KeyManager::IsKeyPressed(keyCode)) {
|
||||
if (KeyManager::IsKeyPressed(keyCode))
|
||||
{
|
||||
SetBit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetPressedState(uint8_t bit, bool enabled)
|
||||
{
|
||||
if(enabled) {
|
||||
if (enabled)
|
||||
{
|
||||
SetBit(bit);
|
||||
}
|
||||
}
|
||||
|
@ -252,17 +282,18 @@ void BaseControlDevice::SetMovement(MouseMovement mov)
|
|||
MouseMovement prev = GetMovement();
|
||||
mov.dx += prev.dx;
|
||||
mov.dy += prev.dy;
|
||||
SetCoordinates({ mov.dx, mov.dy });
|
||||
SetCoordinates({mov.dx, mov.dy});
|
||||
}
|
||||
|
||||
MouseMovement BaseControlDevice::GetMovement()
|
||||
{
|
||||
MousePosition pos = GetCoordinates();
|
||||
SetCoordinates({ 0, 0 });
|
||||
return { pos.X, pos.Y };
|
||||
SetCoordinates({0, 0});
|
||||
return {pos.X, pos.Y};
|
||||
}
|
||||
|
||||
void BaseControlDevice::SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2, uint8_t button2)
|
||||
void BaseControlDevice::SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1,
|
||||
shared_ptr<BaseControlDevice> state2, uint8_t button2)
|
||||
{
|
||||
bool pressed1 = state1->IsPressed(button1);
|
||||
bool pressed2 = state2->IsPressed(button2);
|
||||
|
@ -270,15 +301,17 @@ void BaseControlDevice::SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_
|
|||
state1->ClearBit(button1);
|
||||
state2->ClearBit(button2);
|
||||
|
||||
if(pressed1) {
|
||||
if (pressed1)
|
||||
{
|
||||
state2->SetBit(button2);
|
||||
}
|
||||
if(pressed2) {
|
||||
if (pressed2)
|
||||
{
|
||||
state1->SetBit(button1);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseControlDevice::Serialize(Serializer &s)
|
||||
void BaseControlDevice::Serialize(Serializer& s)
|
||||
{
|
||||
auto lock = _stateLock.AcquireSafe();
|
||||
s.Stream(_strobe);
|
||||
|
|
|
@ -20,8 +20,10 @@ protected:
|
|||
uint8_t _port;
|
||||
SimpleLock _stateLock;
|
||||
|
||||
virtual void RefreshStateBuffer() { }
|
||||
|
||||
virtual void RefreshStateBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void EnsureCapacity(int32_t minBitCount);
|
||||
uint32_t GetByteIndex(uint8_t bit);
|
||||
virtual bool HasCoordinates();
|
||||
|
@ -41,9 +43,9 @@ protected:
|
|||
|
||||
void SetMovement(MouseMovement mov);
|
||||
MouseMovement GetMovement();
|
||||
|
||||
|
||||
virtual void InternalSetStateFromInput();
|
||||
|
||||
|
||||
public:
|
||||
static constexpr uint8_t ExpDevicePort = 4;
|
||||
static constexpr uint8_t ConsoleInputPort = 5;
|
||||
|
@ -58,27 +60,31 @@ public:
|
|||
|
||||
bool IsPressed(uint8_t bit);
|
||||
MousePosition GetCoordinates();
|
||||
|
||||
|
||||
void ClearState();
|
||||
void SetBit(uint8_t bit);
|
||||
void ClearBit(uint8_t bit);
|
||||
void InvertBit(uint8_t bit);
|
||||
void SetBitValue(uint8_t bit, bool set);
|
||||
|
||||
|
||||
void SetTextState(string state);
|
||||
string GetTextState();
|
||||
|
||||
void SetStateFromInput();
|
||||
virtual void OnAfterSetState() { }
|
||||
|
||||
|
||||
virtual void OnAfterSetState()
|
||||
{
|
||||
}
|
||||
|
||||
void SetRawState(ControlDeviceState state);
|
||||
ControlDeviceState GetRawState();
|
||||
|
||||
virtual ControllerType GetControllerType() = 0;
|
||||
virtual uint8_t ReadRam(uint16_t addr) = 0;
|
||||
virtual void WriteRam(uint16_t addr, uint8_t value) = 0;
|
||||
|
||||
void static SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2, uint8_t button2);
|
||||
|
||||
void Serialize(Serializer &s) override;
|
||||
void static SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2,
|
||||
uint8_t button2);
|
||||
|
||||
void Serialize(Serializer& s) override;
|
||||
};
|
||||
|
|
|
@ -10,8 +10,19 @@ public:
|
|||
|
||||
virtual void Reset() = 0;
|
||||
|
||||
virtual void Run() { }
|
||||
virtual void ProcessEndOfFrame() { }
|
||||
virtual void LoadBattery() { }
|
||||
virtual void SaveBattery() { }
|
||||
};
|
||||
virtual void Run()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ProcessEndOfFrame()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void LoadBattery()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void SaveBattery()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,7 +9,8 @@ BaseRenderer::BaseRenderer(shared_ptr<Console> console, bool registerAsMessageMa
|
|||
{
|
||||
_console = console;
|
||||
|
||||
if(registerAsMessageManager) {
|
||||
if (registerAsMessageManager)
|
||||
{
|
||||
//Only display messages on the master CPU's screen
|
||||
MessageManager::RegisterMessageManager(this);
|
||||
}
|
||||
|
@ -37,34 +38,44 @@ void BaseRenderer::DrawToasts()
|
|||
|
||||
int counter = 0;
|
||||
int lastHeight = 5;
|
||||
for(shared_ptr<ToastInfo> toast : _toasts) {
|
||||
if(counter < 6) {
|
||||
for (shared_ptr<ToastInfo> toast : _toasts)
|
||||
{
|
||||
if (counter < 6)
|
||||
{
|
||||
DrawToast(toast, lastHeight);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring BaseRenderer::WrapText(string utf8Text, float maxLineWidth, uint32_t &lineCount)
|
||||
std::wstring BaseRenderer::WrapText(string utf8Text, float maxLineWidth, uint32_t& lineCount)
|
||||
{
|
||||
using std::wstring;
|
||||
wstring text = utf8::utf8::decode(utf8Text);
|
||||
wstring wrappedText;
|
||||
list<wstring> words;
|
||||
wstring currentWord;
|
||||
for(size_t i = 0, len = text.length(); i < len; i++) {
|
||||
if(text[i] == L' ' || text[i] == L'\n') {
|
||||
if(currentWord.length() > 0) {
|
||||
for (size_t i = 0, len = text.length(); i < len; i++)
|
||||
{
|
||||
if (text[i] == L' ' || text[i] == L'\n')
|
||||
{
|
||||
if (currentWord.length() > 0)
|
||||
{
|
||||
words.push_back(currentWord);
|
||||
currentWord.clear();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
currentWord += text[i];
|
||||
}
|
||||
}
|
||||
if(currentWord.length() > 0) {
|
||||
if (currentWord.length() > 0)
|
||||
{
|
||||
words.push_back(currentWord);
|
||||
}
|
||||
|
||||
|
@ -72,19 +83,25 @@ std::wstring BaseRenderer::WrapText(string utf8Text, float maxLineWidth, uint32_
|
|||
float spaceWidth = MeasureString(L" ");
|
||||
|
||||
float lineWidth = 0.0f;
|
||||
for(wstring word : words) {
|
||||
for(unsigned int i = 0; i < word.size(); i++) {
|
||||
if(!ContainsCharacter(word[i])) {
|
||||
for (wstring word : words)
|
||||
{
|
||||
for (unsigned int i = 0; i < word.size(); i++)
|
||||
{
|
||||
if (!ContainsCharacter(word[i]))
|
||||
{
|
||||
word[i] = L'?';
|
||||
}
|
||||
}
|
||||
|
||||
float wordWidth = MeasureString(word.c_str());
|
||||
|
||||
if(lineWidth + wordWidth < maxLineWidth) {
|
||||
if (lineWidth + wordWidth < maxLineWidth)
|
||||
{
|
||||
wrappedText += word + L" ";
|
||||
lineWidth += wordWidth + spaceWidth;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
wrappedText += L"\n" + word + L" ";
|
||||
lineWidth = wordWidth + spaceWidth;
|
||||
lineCount++;
|
||||
|
@ -94,10 +111,10 @@ std::wstring BaseRenderer::WrapText(string utf8Text, float maxLineWidth, uint32_
|
|||
return wrappedText;
|
||||
}
|
||||
|
||||
void BaseRenderer::DrawToast(shared_ptr<ToastInfo> toast, int &lastHeight)
|
||||
void BaseRenderer::DrawToast(shared_ptr<ToastInfo> toast, int& lastHeight)
|
||||
{
|
||||
//Get opacity for fade in/out effect
|
||||
uint8_t opacity = (uint8_t)(toast->GetOpacity()*255);
|
||||
uint8_t opacity = (uint8_t)(toast->GetOpacity() * 255);
|
||||
int textLeftMargin = 4;
|
||||
|
||||
int lineHeight = 25;
|
||||
|
@ -117,24 +134,31 @@ void BaseRenderer::DrawString(std::string message, int x, int y, uint8_t r, uint
|
|||
void BaseRenderer::ShowFpsCounter(int lineNumber)
|
||||
{
|
||||
int yPos = 13 + 24 * lineNumber;
|
||||
if(_fpsTimer.GetElapsedMS() > 1000) {
|
||||
if (_fpsTimer.GetElapsedMS() > 1000)
|
||||
{
|
||||
//Update fps every sec
|
||||
uint32_t frameCount = _console->GetFrameCount();
|
||||
if(_lastFrameCount > frameCount) {
|
||||
if (_lastFrameCount > frameCount)
|
||||
{
|
||||
_currentFPS = 0;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentFPS = (int)(std::round((double)(frameCount - _lastFrameCount) / (_fpsTimer.GetElapsedMS() / 1000)));
|
||||
_currentRenderedFPS = (int)(std::round((double)(_renderedFrameCount - _lastRenderedFrameCount) / (_fpsTimer.GetElapsedMS() / 1000)));
|
||||
_currentRenderedFPS = (int)(std::round(
|
||||
(double)(_renderedFrameCount - _lastRenderedFrameCount) / (_fpsTimer.GetElapsedMS() / 1000)));
|
||||
}
|
||||
_lastFrameCount = frameCount;
|
||||
_lastRenderedFrameCount = _renderedFrameCount;
|
||||
_fpsTimer.Reset();
|
||||
}
|
||||
|
||||
if(_currentFPS > 5000) {
|
||||
if (_currentFPS > 5000)
|
||||
{
|
||||
_currentFPS = 0;
|
||||
}
|
||||
if(_currentRenderedFPS > 5000) {
|
||||
if (_currentRenderedFPS > 5000)
|
||||
{
|
||||
_currentRenderedFPS = 0;
|
||||
}
|
||||
|
||||
|
@ -171,13 +195,16 @@ void BaseRenderer::DrawCounters()
|
|||
{
|
||||
int lineNumber = 0;
|
||||
PreferencesConfig cfg = _console->GetSettings()->GetPreferences();
|
||||
if(cfg.ShowGameTimer) {
|
||||
if (cfg.ShowGameTimer)
|
||||
{
|
||||
ShowGameTimer(lineNumber++);
|
||||
}
|
||||
if(cfg.ShowFps) {
|
||||
if (cfg.ShowFps)
|
||||
{
|
||||
ShowFpsCounter(lineNumber++);
|
||||
}
|
||||
if(cfg.ShowFrameCounter) {
|
||||
if (cfg.ShowFrameCounter)
|
||||
{
|
||||
ShowFrameCounter(lineNumber++);
|
||||
}
|
||||
}
|
||||
|
@ -185,4 +212,4 @@ void BaseRenderer::DrawCounters()
|
|||
bool BaseRenderer::IsMessageShown()
|
||||
{
|
||||
return !_toasts.empty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ private:
|
|||
uint32_t _currentRenderedFPS = 0;
|
||||
|
||||
void RemoveOldToasts();
|
||||
std::wstring WrapText(string utf8Text, float maxLineWidth, uint32_t &lineCount);
|
||||
std::wstring WrapText(string utf8Text, float maxLineWidth, uint32_t& lineCount);
|
||||
virtual float MeasureString(std::wstring text) = 0;
|
||||
virtual bool ContainsCharacter(wchar_t character) = 0;
|
||||
|
||||
|
@ -26,19 +26,20 @@ protected:
|
|||
|
||||
uint32_t _screenWidth = 0;
|
||||
uint32_t _screenHeight = 0;
|
||||
uint32_t _renderedFrameCount = 0;
|
||||
uint32_t _renderedFrameCount = 0;
|
||||
|
||||
BaseRenderer(shared_ptr<Console> console, bool registerAsMessageManager);
|
||||
virtual ~BaseRenderer();
|
||||
|
||||
bool IsMessageShown();
|
||||
bool IsMessageShown();
|
||||
|
||||
void DisplayMessage(string title, string message);
|
||||
void DrawToasts();
|
||||
|
||||
void DrawToast(shared_ptr<ToastInfo> toast, int &lastHeight);
|
||||
void DrawString(std::string message, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t opacity = 255);
|
||||
virtual void DrawString(std::wstring message, int x, int y, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255, uint8_t opacity = 255) = 0;
|
||||
|
||||
void DrawToast(shared_ptr<ToastInfo> toast, int& lastHeight);
|
||||
void DrawString(std::string message, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t opacity = 255);
|
||||
virtual void DrawString(std::wstring message, int x, int y, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255,
|
||||
uint8_t opacity = 255) = 0;
|
||||
|
||||
void ShowFpsCounter(int lineNumber);
|
||||
void ShowLagCounter(int lineNumber);
|
||||
|
|
|
@ -5,25 +5,31 @@ void BaseSoundManager::ProcessLatency(uint32_t readPosition, uint32_t writePosit
|
|||
{
|
||||
//Record latency between read & write cursors once per frame
|
||||
int32_t cursorGap;
|
||||
if(writePosition < readPosition) {
|
||||
if (writePosition < readPosition)
|
||||
{
|
||||
cursorGap = writePosition - readPosition + _bufferSize;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
cursorGap = writePosition - readPosition;
|
||||
}
|
||||
|
||||
_cursorGaps[_cursorGapIndex] = cursorGap;
|
||||
_cursorGapIndex = (_cursorGapIndex + 1) % 60;
|
||||
if(_cursorGapIndex == 0) {
|
||||
if (_cursorGapIndex == 0)
|
||||
{
|
||||
_cursorGapFilled = true;
|
||||
}
|
||||
|
||||
if(_cursorGapFilled) {
|
||||
if (_cursorGapFilled)
|
||||
{
|
||||
//Once we have 60+ frames worth of data to work with, adjust playback frequency by +/- 0.5%
|
||||
//To speed up or slow down playback in order to reach our latency goal.
|
||||
uint32_t bytesPerSample = _isStereo ? 4 : 2;
|
||||
|
||||
int32_t gapSum = 0;
|
||||
for(int i = 0; i < 60; i++) {
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
gapSum += _cursorGaps[i];
|
||||
}
|
||||
int32_t gapAverage = gapSum / 60;
|
||||
|
|
|
@ -36,8 +36,9 @@ FrameInfo BaseVideoFilter::GetFrameInfo()
|
|||
|
||||
void BaseVideoFilter::UpdateBufferSize()
|
||||
{
|
||||
uint32_t newBufferSize = GetFrameInfo().Width*GetFrameInfo().Height;
|
||||
if(_bufferSize != newBufferSize) {
|
||||
uint32_t newBufferSize = GetFrameInfo().Width * GetFrameInfo().Height;
|
||||
if (_bufferSize != newBufferSize)
|
||||
{
|
||||
_frameLock.Acquire();
|
||||
delete[] _outputBuffer;
|
||||
_bufferSize = newBufferSize;
|
||||
|
@ -65,7 +66,7 @@ uint32_t BaseVideoFilter::GetBufferSize()
|
|||
return _bufferSize * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber)
|
||||
void BaseVideoFilter::SendFrame(uint16_t* ppuOutputBuffer, uint32_t frameNumber)
|
||||
{
|
||||
_frameLock.Acquire();
|
||||
_overscan = _console->GetSettings()->GetOverscan();
|
||||
|
@ -91,14 +92,15 @@ uint32_t BaseVideoFilter::ApplyScanlineEffect(uint32_t argb, uint8_t scanlineInt
|
|||
return 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream)
|
||||
void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream* stream)
|
||||
{
|
||||
uint32_t* pngBuffer;
|
||||
FrameInfo frameInfo;
|
||||
uint32_t* frameBuffer = nullptr;
|
||||
{
|
||||
auto lock = _frameLock.AcquireSafe();
|
||||
if(_bufferSize == 0 || !GetOutputBuffer()) {
|
||||
if (_bufferSize == 0 || !GetOutputBuffer())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -110,14 +112,19 @@ void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename
|
|||
pngBuffer = frameBuffer;
|
||||
|
||||
shared_ptr<ScaleFilter> scaleFilter = ScaleFilter::GetScaleFilter(filterType);
|
||||
if(scaleFilter) {
|
||||
pngBuffer = scaleFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height, _console->GetSettings()->GetVideoConfig().ScanlineIntensity);
|
||||
if (scaleFilter)
|
||||
{
|
||||
pngBuffer = scaleFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height,
|
||||
_console->GetSettings()->GetVideoConfig().ScanlineIntensity);
|
||||
frameInfo = scaleFilter->GetFrameInfo(frameInfo);
|
||||
}
|
||||
|
||||
if(!filename.empty()) {
|
||||
|
||||
if (!filename.empty())
|
||||
{
|
||||
PNGHelper::WritePNG(filename, pngBuffer, frameInfo.Width, frameInfo.Height);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
PNGHelper::WritePNG(*stream, pngBuffer, frameInfo.Width, frameInfo.Height);
|
||||
}
|
||||
|
||||
|
@ -131,16 +138,21 @@ void BaseVideoFilter::TakeScreenshot(string romName, VideoFilterType filterType)
|
|||
int counter = 0;
|
||||
string baseFilename = FolderUtilities::CombinePath(FolderUtilities::GetScreenshotFolder(), romFilename);
|
||||
string ssFilename;
|
||||
while(true) {
|
||||
while (true)
|
||||
{
|
||||
string counterStr = std::to_string(counter);
|
||||
while(counterStr.length() < 3) {
|
||||
while (counterStr.length() < 3)
|
||||
{
|
||||
counterStr = "0" + counterStr;
|
||||
}
|
||||
ssFilename = baseFilename + "_" + counterStr + ".png";
|
||||
ifstream file(ssFilename, ios::in);
|
||||
if(file) {
|
||||
if (file)
|
||||
{
|
||||
file.close();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
|
@ -150,4 +162,3 @@ void BaseVideoFilter::TakeScreenshot(string romName, VideoFilterType filterType)
|
|||
|
||||
MessageManager::DisplayMessage("ScreenshotSaved", FolderUtilities::GetFilename(ssFilename, true));
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ protected:
|
|||
shared_ptr<Console> _console;
|
||||
FrameInfo _baseFrameInfo;
|
||||
|
||||
virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0;
|
||||
virtual void ApplyFilter(uint16_t* ppuOutputBuffer) = 0;
|
||||
virtual void OnBeforeApplyFilter();
|
||||
bool IsOddFrame();
|
||||
uint32_t GetBufferSize();
|
||||
|
@ -31,12 +31,12 @@ public:
|
|||
virtual ~BaseVideoFilter();
|
||||
|
||||
uint32_t* GetOutputBuffer();
|
||||
void SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber);
|
||||
void SendFrame(uint16_t* ppuOutputBuffer, uint32_t frameNumber);
|
||||
void TakeScreenshot(string romName, VideoFilterType filterType);
|
||||
void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr);
|
||||
void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream* stream = nullptr);
|
||||
|
||||
virtual OverscanDimensions GetOverscan();
|
||||
|
||||
|
||||
void SetBaseFrameInfo(FrameInfo frameInfo);
|
||||
virtual FrameInfo GetFrameInfo();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -31,7 +31,8 @@ void BatteryManager::SetBatteryRecorder(shared_ptr<IBatteryRecorder> recorder)
|
|||
|
||||
void BatteryManager::SaveBattery(string extension, uint8_t* data, uint32_t length)
|
||||
{
|
||||
if (_saveEnabled) {
|
||||
if (_saveEnabled)
|
||||
{
|
||||
#ifdef LIBRETRO
|
||||
if (extension == ".srm") {
|
||||
//Disable .srm files for libretro, let the frontend handle save ram
|
||||
|
@ -40,7 +41,8 @@ void BatteryManager::SaveBattery(string extension, uint8_t* data, uint32_t lengt
|
|||
#endif
|
||||
|
||||
ofstream out(GetBasePath() + extension, ios::binary);
|
||||
if (out) {
|
||||
if (out)
|
||||
{
|
||||
out.write((char*)data, length);
|
||||
}
|
||||
}
|
||||
|
@ -49,21 +51,27 @@ void BatteryManager::SaveBattery(string extension, uint8_t* data, uint32_t lengt
|
|||
vector<uint8_t> BatteryManager::LoadBattery(string extension)
|
||||
{
|
||||
shared_ptr<IBatteryProvider> provider = _provider.lock();
|
||||
|
||||
|
||||
vector<uint8_t> batteryData;
|
||||
if(provider) {
|
||||
if (provider)
|
||||
{
|
||||
//Used by movie player to provider initial state of ram at startup
|
||||
batteryData = provider->LoadBattery(extension);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
VirtualFile file = GetBasePath() + extension;
|
||||
if(file.IsValid()) {
|
||||
if (file.IsValid())
|
||||
{
|
||||
file.ReadFile(batteryData);
|
||||
}
|
||||
}
|
||||
|
||||
if(!batteryData.empty()) {
|
||||
if (!batteryData.empty())
|
||||
{
|
||||
shared_ptr<IBatteryRecorder> recorder = _recorder.lock();
|
||||
if(recorder) {
|
||||
if (recorder)
|
||||
{
|
||||
//Used by movies to record initial state of battery-backed ram at power on
|
||||
recorder->OnLoadBattery(extension, batteryData);
|
||||
}
|
||||
|
@ -76,4 +84,4 @@ void BatteryManager::LoadBattery(string extension, uint8_t* data, uint32_t lengt
|
|||
{
|
||||
vector<uint8_t> batteryData = LoadBattery(extension);
|
||||
memcpy(data, batteryData.data(), std::min((uint32_t)batteryData.size(), length));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@ public:
|
|||
|
||||
void SetBatteryProvider(shared_ptr<IBatteryProvider> provider);
|
||||
void SetBatteryRecorder(shared_ptr<IBatteryRecorder> recorder);
|
||||
|
||||
|
||||
void SaveBattery(string extension, uint8_t* data, uint32_t length);
|
||||
|
||||
|
||||
vector<uint8_t> LoadBattery(string extension);
|
||||
void LoadBattery(string extension, uint8_t* data, uint32_t length);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -3,22 +3,35 @@
|
|||
#include "DebugTypes.h"
|
||||
#include "DebugUtilities.h"
|
||||
|
||||
bool Breakpoint::Matches(uint32_t memoryAddr, AddressInfo &info)
|
||||
bool Breakpoint::Matches(uint32_t memoryAddr, AddressInfo& info)
|
||||
{
|
||||
if(memoryType <= DebugUtilities::GetLastCpuMemoryType() && !DebugUtilities::IsPpuMemory(info.Type)) {
|
||||
if(startAddr == -1) {
|
||||
if (memoryType <= DebugUtilities::GetLastCpuMemoryType() && !DebugUtilities::IsPpuMemory(info.Type))
|
||||
{
|
||||
if (startAddr == -1)
|
||||
{
|
||||
return true;
|
||||
} else if(endAddr == -1) {
|
||||
}
|
||||
else if (endAddr == -1)
|
||||
{
|
||||
return (int32_t)memoryAddr == startAddr;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int32_t)memoryAddr >= startAddr && (int32_t)memoryAddr <= endAddr;
|
||||
}
|
||||
} else if(memoryType == info.Type) {
|
||||
if(startAddr == -1) {
|
||||
}
|
||||
else if (memoryType == info.Type)
|
||||
{
|
||||
if (startAddr == -1)
|
||||
{
|
||||
return true;
|
||||
} else if(endAddr == -1) {
|
||||
}
|
||||
else if (endAddr == -1)
|
||||
{
|
||||
return info.Address == startAddr;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return info.Address >= startAddr && info.Address <= endAddr;
|
||||
}
|
||||
}
|
||||
|
@ -28,11 +41,12 @@ bool Breakpoint::Matches(uint32_t memoryAddr, AddressInfo &info)
|
|||
|
||||
bool Breakpoint::HasBreakpointType(BreakpointType bpType)
|
||||
{
|
||||
switch(bpType) {
|
||||
default:
|
||||
case BreakpointType::Execute: return ((uint8_t)type & (uint8_t)BreakpointTypeFlags::Execute) != 0;
|
||||
case BreakpointType::Read: return ((uint8_t)type & (uint8_t)BreakpointTypeFlags::Read) != 0;
|
||||
case BreakpointType::Write: return ((uint8_t)type & (uint8_t)BreakpointTypeFlags::Write) != 0;
|
||||
switch (bpType)
|
||||
{
|
||||
default:
|
||||
case BreakpointType::Execute: return ((uint8_t)type & (uint8_t)BreakpointTypeFlags::Execute) != 0;
|
||||
case BreakpointType::Read: return ((uint8_t)type & (uint8_t)BreakpointTypeFlags::Read) != 0;
|
||||
case BreakpointType::Write: return ((uint8_t)type & (uint8_t)BreakpointTypeFlags::Write) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
enum class CpuType : uint8_t;
|
||||
enum class CpuType : uint8_t;
|
||||
enum class SnesMemoryType;
|
||||
struct AddressInfo;
|
||||
enum class BreakpointType;
|
||||
|
@ -11,7 +11,7 @@ enum class BreakpointCategory;
|
|||
class Breakpoint
|
||||
{
|
||||
public:
|
||||
bool Matches(uint32_t memoryAddr, AddressInfo &info);
|
||||
bool Matches(uint32_t memoryAddr, AddressInfo& info);
|
||||
bool HasBreakpointType(BreakpointType bpType);
|
||||
string GetCondition();
|
||||
bool HasCondition();
|
||||
|
@ -28,4 +28,4 @@ public:
|
|||
bool enabled;
|
||||
bool markEvent;
|
||||
char condition[1000];
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "ExpressionEvaluator.h"
|
||||
#include "EventManager.h"
|
||||
|
||||
BreakpointManager::BreakpointManager(Debugger *debugger, CpuType cpuType, IEventManager* eventManager)
|
||||
BreakpointManager::BreakpointManager(Debugger* debugger, CpuType cpuType, IEventManager* eventManager)
|
||||
{
|
||||
_debugger = debugger;
|
||||
_cpuType = cpuType;
|
||||
|
@ -18,7 +18,8 @@ BreakpointManager::BreakpointManager(Debugger *debugger, CpuType cpuType, IEvent
|
|||
void BreakpointManager::SetBreakpoints(Breakpoint breakpoints[], uint32_t count)
|
||||
{
|
||||
_hasBreakpoint = false;
|
||||
for(int i = 0; i < BreakpointManager::BreakpointTypeCount; i++) {
|
||||
for (int i = 0; i < BreakpointManager::BreakpointTypeCount; i++)
|
||||
{
|
||||
_breakpoints[i].clear();
|
||||
_rpnList[i].clear();
|
||||
_hasBreakpointType[i] = false;
|
||||
|
@ -26,27 +27,34 @@ void BreakpointManager::SetBreakpoints(Breakpoint breakpoints[], uint32_t count)
|
|||
|
||||
_bpExpEval.reset(new ExpressionEvaluator(_debugger, _cpuType));
|
||||
|
||||
for(uint32_t j = 0; j < count; j++) {
|
||||
Breakpoint &bp = breakpoints[j];
|
||||
for(int i = 0; i < BreakpointManager::BreakpointTypeCount; i++) {
|
||||
for (uint32_t j = 0; j < count; j++)
|
||||
{
|
||||
Breakpoint& bp = breakpoints[j];
|
||||
for (int i = 0; i < BreakpointManager::BreakpointTypeCount; i++)
|
||||
{
|
||||
BreakpointType bpType = (BreakpointType)i;
|
||||
if((bp.IsMarked() || bp.IsEnabled()) && bp.HasBreakpointType(bpType)) {
|
||||
if ((bp.IsMarked() || bp.IsEnabled()) && bp.HasBreakpointType(bpType))
|
||||
{
|
||||
CpuType cpuType = bp.GetCpuType();
|
||||
if(_cpuType != cpuType) {
|
||||
if (_cpuType != cpuType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int curIndex = _breakpoints[i].size();
|
||||
_breakpoints[i].insert(std::make_pair(curIndex, bp));
|
||||
|
||||
if(bp.HasCondition()) {
|
||||
if (bp.HasCondition())
|
||||
{
|
||||
bool success = true;
|
||||
ExpressionData data = _bpExpEval->GetRpnList(bp.GetCondition(), success);
|
||||
_rpnList[i].insert(std::make_pair(curIndex, success ? data : ExpressionData()));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_rpnList[i].insert(std::make_pair(curIndex, ExpressionData()));
|
||||
}
|
||||
|
||||
|
||||
_hasBreakpoint = true;
|
||||
_hasBreakpointType[i] = true;
|
||||
}
|
||||
|
@ -60,61 +68,76 @@ void BreakpointManager::GetBreakpoints(Breakpoint* breakpoints, int& execs, int&
|
|||
reads = _breakpoints[static_cast<int>(BreakpointType::Read)].size();
|
||||
writes = _breakpoints[static_cast<int>(BreakpointType::Write)].size();
|
||||
|
||||
if (breakpoints == NULL) {
|
||||
if (breakpoints == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
for (auto it = _breakpoints[static_cast<int>(BreakpointType::Execute)].cbegin(); it != _breakpoints[static_cast<int>(BreakpointType::Execute)].cend(); it++) {
|
||||
for (auto it = _breakpoints[static_cast<int>(BreakpointType::Execute)].cbegin(); it != _breakpoints[static_cast<int>(
|
||||
BreakpointType::Execute)].cend(); it++)
|
||||
{
|
||||
breakpoints[offset++] = it->second;
|
||||
}
|
||||
|
||||
for (auto it = _breakpoints[static_cast<int>(BreakpointType::Read)].cbegin(); it != _breakpoints[static_cast<int>(BreakpointType::Read)].cend(); it++) {
|
||||
for (auto it = _breakpoints[static_cast<int>(BreakpointType::Read)].cbegin(); it != _breakpoints[static_cast<int>(
|
||||
BreakpointType::Read)].cend(); it++)
|
||||
{
|
||||
breakpoints[offset++] = it->second;
|
||||
}
|
||||
|
||||
for (auto it = _breakpoints[static_cast<int>(BreakpointType::Write)].cbegin(); it != _breakpoints[static_cast<int>(BreakpointType::Write)].cend(); it++) {
|
||||
for (auto it = _breakpoints[static_cast<int>(BreakpointType::Write)].cbegin(); it != _breakpoints[static_cast<int>(
|
||||
BreakpointType::Write)].cend(); it++)
|
||||
{
|
||||
breakpoints[offset++] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
BreakpointType BreakpointManager::GetBreakpointType(MemoryOperationType type)
|
||||
{
|
||||
switch(type) {
|
||||
default:
|
||||
case MemoryOperationType::ExecOperand:
|
||||
case MemoryOperationType::ExecOpCode:
|
||||
return BreakpointType::Execute;
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case MemoryOperationType::ExecOperand:
|
||||
case MemoryOperationType::ExecOpCode:
|
||||
return BreakpointType::Execute;
|
||||
|
||||
case MemoryOperationType::DmaRead:
|
||||
case MemoryOperationType::Read:
|
||||
return BreakpointType::Read;
|
||||
case MemoryOperationType::DmaRead:
|
||||
case MemoryOperationType::Read:
|
||||
return BreakpointType::Read;
|
||||
|
||||
case MemoryOperationType::DmaWrite:
|
||||
case MemoryOperationType::Write:
|
||||
return BreakpointType::Write;
|
||||
case MemoryOperationType::DmaWrite:
|
||||
case MemoryOperationType::Write:
|
||||
return BreakpointType::Write;
|
||||
}
|
||||
}
|
||||
|
||||
int BreakpointManager::InternalCheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo &address)
|
||||
int BreakpointManager::InternalCheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo& address)
|
||||
{
|
||||
BreakpointType type = GetBreakpointType(operationInfo.Type);
|
||||
|
||||
if(!_hasBreakpointType[(int)type]) {
|
||||
if (!_hasBreakpointType[(int)type])
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
DebugState state;
|
||||
_debugger->GetState(state, false);
|
||||
EvalResultType resultType;
|
||||
unordered_map<int, Breakpoint> &breakpoints = _breakpoints[(int)type];
|
||||
for (auto it = breakpoints.begin(); it != breakpoints.end(); it++) {
|
||||
if (it->second.Matches(operationInfo.Address, address)) {
|
||||
if (!it->second.HasCondition() || _bpExpEval->Evaluate(_rpnList[(int)type][it->first], state, resultType, operationInfo)) {
|
||||
if (it->second.IsMarked()) {
|
||||
unordered_map<int, Breakpoint>& breakpoints = _breakpoints[(int)type];
|
||||
for (auto it = breakpoints.begin(); it != breakpoints.end(); it++)
|
||||
{
|
||||
if (it->second.Matches(operationInfo.Address, address))
|
||||
{
|
||||
if (!it->second.HasCondition() || _bpExpEval->Evaluate(_rpnList[(int)type][it->first], state, resultType,
|
||||
operationInfo))
|
||||
{
|
||||
if (it->second.IsMarked())
|
||||
{
|
||||
_eventManager->AddEvent(DebugEventType::Breakpoint, operationInfo, it->first);
|
||||
}
|
||||
if (it->second.IsEnabled()) {
|
||||
if (it->second.IsEnabled())
|
||||
{
|
||||
return it->first;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,10 @@ class BreakpointManager
|
|||
private:
|
||||
static constexpr int BreakpointTypeCount = 3; //Read, Write, Exec
|
||||
|
||||
Debugger *_debugger;
|
||||
Debugger* _debugger;
|
||||
CpuType _cpuType;
|
||||
IEventManager *_eventManager;
|
||||
|
||||
IEventManager* _eventManager;
|
||||
|
||||
unordered_map<int, Breakpoint> _breakpoints[BreakpointTypeCount];
|
||||
unordered_map<int, ExpressionData> _rpnList[BreakpointTypeCount];
|
||||
bool _hasBreakpoint;
|
||||
|
@ -27,20 +27,21 @@ private:
|
|||
unique_ptr<ExpressionEvaluator> _bpExpEval;
|
||||
|
||||
BreakpointType GetBreakpointType(MemoryOperationType type);
|
||||
int InternalCheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo &address);
|
||||
int InternalCheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo& address);
|
||||
|
||||
public:
|
||||
BreakpointManager(Debugger *debugger, CpuType cpuType, IEventManager* eventManager = nullptr);
|
||||
BreakpointManager(Debugger* debugger, CpuType cpuType, IEventManager* eventManager = nullptr);
|
||||
|
||||
void SetBreakpoints(Breakpoint breakpoints[], uint32_t count);
|
||||
void GetBreakpoints(Breakpoint* breakpoints, int& execs, int& reads, int& writes);
|
||||
__forceinline int CheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo &address);
|
||||
__forceinline int CheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo& address);
|
||||
};
|
||||
|
||||
__forceinline int BreakpointManager::CheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo &address)
|
||||
__forceinline int BreakpointManager::CheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo& address)
|
||||
{
|
||||
if(!_hasBreakpoint) {
|
||||
if (!_hasBreakpoint)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return InternalCheckBreakpoint(operationInfo, address);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,13 +28,15 @@ BsxCart::BsxCart(Console* console, BsxMemoryPack* memPack) : BaseCoprocessor(Sne
|
|||
_psRam = new uint8_t[_psRamSize];
|
||||
console->GetSettings()->InitializeRam(_psRam, _psRamSize);
|
||||
|
||||
for(uint32_t i = 0; i < _psRamSize / 0x1000; i++) {
|
||||
_psRamHandlers.push_back(unique_ptr<IMemoryHandler>(new RamHandler(_psRam, i * 0x1000, _psRamSize, SnesMemoryType::BsxPsRam)));
|
||||
for (uint32_t i = 0; i < _psRamSize / 0x1000; i++)
|
||||
{
|
||||
_psRamHandlers.push_back(
|
||||
unique_ptr<IMemoryHandler>(new RamHandler(_psRam, i * 0x1000, _psRamSize, SnesMemoryType::BsxPsRam)));
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
|
||||
BsxCart::~BsxCart()
|
||||
{
|
||||
delete[] _psRam;
|
||||
|
@ -43,14 +45,20 @@ BsxCart::~BsxCart()
|
|||
uint8_t BsxCart::Read(uint32_t addr)
|
||||
{
|
||||
uint8_t openBus = _memoryManager->GetOpenBus();
|
||||
if((addr & 0xFFFF) != 0x5000) {
|
||||
if ((addr & 0xFFFF) != 0x5000)
|
||||
{
|
||||
return openBus;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t reg = (addr >> 16) & 0x0F;
|
||||
|
||||
if(reg <= 0x0D) {
|
||||
|
||||
if (reg <= 0x0D)
|
||||
{
|
||||
return (_regs[reg] << 7) | (openBus & 0x7F);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//E & F are write-only
|
||||
return openBus & 0x7F;
|
||||
}
|
||||
|
@ -59,20 +67,26 @@ uint8_t BsxCart::Read(uint32_t addr)
|
|||
|
||||
void BsxCart::Write(uint32_t addr, uint8_t value)
|
||||
{
|
||||
if((addr & 0xFFFF) != 0x5000) {
|
||||
if ((addr & 0xFFFF) != 0x5000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t reg = (addr >> 16) & 0x0F;
|
||||
if(reg == 0x0E) {
|
||||
if(_dirty) {
|
||||
if (reg == 0x0E)
|
||||
{
|
||||
if (_dirty)
|
||||
{
|
||||
memcpy(_regs, _dirtyRegs, sizeof(_regs));
|
||||
UpdateMemoryMappings();
|
||||
_dirty = false;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t regValue = (value >> 7);
|
||||
if(_regs[reg] != regValue) {
|
||||
if (_regs[reg] != regValue)
|
||||
{
|
||||
_dirtyRegs[reg] = regValue;
|
||||
_dirty = true;
|
||||
}
|
||||
|
@ -88,7 +102,8 @@ void BsxCart::UpdateMemoryMappings()
|
|||
uint8_t unmappedBank = (_regs[0x0B] << 5);
|
||||
uint8_t psRamBank = (_regs[0x05] << 4) | (_regs[0x06] << 5);
|
||||
|
||||
if(!_regs[0x02]) {
|
||||
if (!_regs[0x02])
|
||||
{
|
||||
//LoROM
|
||||
|
||||
//Memory pack mapping
|
||||
|
@ -99,28 +114,34 @@ void BsxCart::UpdateMemoryMappings()
|
|||
|
||||
//Memory hole mapping
|
||||
uint16_t unmappedAddr = _regs[0x0B] ? 0x0000 : 0x8000;
|
||||
if(_regs[0x09]) {
|
||||
if (_regs[0x09])
|
||||
{
|
||||
mm->RegisterHandler(0x00 | (unmappedBank << 1), 0x1F | (unmappedBank << 1), unmappedAddr, 0xFFFF, nullptr);
|
||||
}
|
||||
|
||||
if(_regs[0x0A]) {
|
||||
if (_regs[0x0A])
|
||||
{
|
||||
mm->RegisterHandler(0x80 | (unmappedBank << 1), 0x9F | (unmappedBank << 1), unmappedAddr, 0xFFFF, nullptr);
|
||||
}
|
||||
|
||||
//PSRAM mapping
|
||||
uint16_t psRamAddr = (psRamBank & 0x20) ? 0x0000 : 0x8000;
|
||||
if(_regs[0x03]) {
|
||||
if (_regs[0x03])
|
||||
{
|
||||
mm->RegisterHandler(0x00 | (psRamBank << 1), 0x0F | (psRamBank << 1), psRamAddr, 0xFFFF, _psRamHandlers);
|
||||
mm->RegisterHandler(0x70, 0x7D, 0x0000, 0x7FFF, _psRamHandlers);
|
||||
}
|
||||
|
||||
if(_regs[0x04]) {
|
||||
if (_regs[0x04])
|
||||
{
|
||||
mm->RegisterHandler(0x80 | (psRamBank << 1), 0x8F | (psRamBank << 1), psRamAddr, 0xFFFF, _psRamHandlers);
|
||||
mm->RegisterHandler(0xF0, 0xFF, 0x0000, 0x7FFF, _psRamHandlers);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//HiROM
|
||||
|
||||
|
||||
//Memory pack mapping
|
||||
mm->RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, memPackHandlers, 8);
|
||||
mm->RegisterHandler(0x40, 0x7D, 0x0000, 0xFFFF, memPackHandlers, 0);
|
||||
|
@ -128,25 +149,29 @@ void BsxCart::UpdateMemoryMappings()
|
|||
mm->RegisterHandler(0xC0, 0xFF, 0x0000, 0xFFFF, memPackHandlers, 0);
|
||||
|
||||
//Memory hole mapping
|
||||
if(_regs[0x09]) {
|
||||
if (_regs[0x09])
|
||||
{
|
||||
mm->RegisterHandler(0x00 | unmappedBank, 0x0F | unmappedBank, 0x8000, 0xFFFF, nullptr);
|
||||
mm->RegisterHandler(0x40 | unmappedBank, 0x4F | unmappedBank, 0x0000, 0xFFFF, nullptr);
|
||||
}
|
||||
|
||||
if(_regs[0x0A]) {
|
||||
if (_regs[0x0A])
|
||||
{
|
||||
mm->RegisterHandler(0x80 | unmappedBank, 0x8F | unmappedBank, 0x8000, 0xFFFF, nullptr);
|
||||
mm->RegisterHandler(0xC0 | unmappedBank, 0xCF | unmappedBank, 0x0000, 0xFFFF, nullptr);
|
||||
}
|
||||
|
||||
//PSRAM mapping
|
||||
if(_regs[0x03]) {
|
||||
if (_regs[0x03])
|
||||
{
|
||||
//Lower Banks (0x00-0x7D)
|
||||
mm->RegisterHandler(0x00 | psRamBank, 0x07 | psRamBank, 0x8000, 0xFFFF, _psRamHandlers, 8);
|
||||
mm->RegisterHandler(0x40 | psRamBank, 0x47 | psRamBank, 0x0000, 0xFFFF, _psRamHandlers);
|
||||
mm->RegisterHandler(0x20, 0x3F, 0x6000, 0x7FFF, _psRamHandlers, 6);
|
||||
}
|
||||
|
||||
if(_regs[0x04]) {
|
||||
if (_regs[0x04])
|
||||
{
|
||||
//Higher Banks (0x80-0xFF)
|
||||
mm->RegisterHandler(0x80 | psRamBank, 0x87 | psRamBank, 0x8000, 0xFFFF, _psRamHandlers, 8);
|
||||
mm->RegisterHandler(0xC0 | psRamBank, 0xC7 | psRamBank, 0x0000, 0xFFFF, _psRamHandlers);
|
||||
|
@ -155,17 +180,20 @@ void BsxCart::UpdateMemoryMappings()
|
|||
}
|
||||
|
||||
//BS-X BIOS mapping (can override other mappings above)
|
||||
if(_regs[0x07]) {
|
||||
if (_regs[0x07])
|
||||
{
|
||||
mm->RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, prgHandlers);
|
||||
}
|
||||
if(_regs[0x08]) {
|
||||
if (_regs[0x08])
|
||||
{
|
||||
mm->RegisterHandler(0x80, 0xBF, 0x8000, 0xFFFF, prgHandlers);
|
||||
}
|
||||
}
|
||||
|
||||
void BsxCart::Reset()
|
||||
{
|
||||
for(int i = 0; i < 0x10; i++) {
|
||||
for (int i = 0; i < 0x10; i++)
|
||||
{
|
||||
_regs[i] = true;
|
||||
}
|
||||
|
||||
|
@ -184,13 +212,14 @@ void BsxCart::Reset()
|
|||
|
||||
void BsxCart::Serialize(Serializer& s)
|
||||
{
|
||||
ArrayInfo<uint8_t> psRam = { _psRam, _psRamSize };
|
||||
ArrayInfo<uint8_t> regs = { _regs, 0x10 };
|
||||
ArrayInfo<uint8_t> dirtyRegs = { _dirtyRegs, 0x10 };
|
||||
ArrayInfo<uint8_t> psRam = {_psRam, _psRamSize};
|
||||
ArrayInfo<uint8_t> regs = {_regs, 0x10};
|
||||
ArrayInfo<uint8_t> dirtyRegs = {_dirtyRegs, 0x10};
|
||||
s.Stream(psRam, regs, dirtyRegs, _dirty);
|
||||
s.Stream(_satellaview.get());
|
||||
|
||||
if(!s.IsSaving()) {
|
||||
if (!s.IsSaving())
|
||||
{
|
||||
UpdateMemoryMappings();
|
||||
}
|
||||
}
|
||||
|
@ -207,7 +236,7 @@ void BsxCart::PeekBlock(uint32_t addr, uint8_t* output)
|
|||
|
||||
AddressInfo BsxCart::GetAbsoluteAddress(uint32_t address)
|
||||
{
|
||||
return { -1, SnesMemoryType::Register };
|
||||
return {-1, SnesMemoryType::Register};
|
||||
}
|
||||
|
||||
uint8_t* BsxCart::DebugGetPsRam()
|
||||
|
|
|
@ -22,7 +22,7 @@ private:
|
|||
uint8_t _regs[0x10] = {};
|
||||
uint8_t _dirtyRegs[0x10] = {};
|
||||
bool _dirty = false;
|
||||
|
||||
|
||||
void UpdateMemoryMappings();
|
||||
|
||||
public:
|
||||
|
@ -41,4 +41,4 @@ public:
|
|||
|
||||
uint8_t* DebugGetPsRam();
|
||||
uint32_t DebugGetPsRamSize();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -15,7 +15,8 @@ BsxMemoryPack::BsxMemoryPack(Console* console, vector<uint8_t>& data, bool persi
|
|||
|
||||
_calculatedSize = std::min<uint8_t>(0x0C, (uint8_t)log2(_dataSize >> 10));
|
||||
|
||||
for(uint32_t i = 0; i < _dataSize / 0x1000; i++) {
|
||||
for (uint32_t i = 0; i < _dataSize / 0x1000; i++)
|
||||
{
|
||||
_handlers.push_back(unique_ptr<BsxMemoryPackHandler>(new BsxMemoryPackHandler(this, i * 0x1000)));
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +28,8 @@ BsxMemoryPack::~BsxMemoryPack()
|
|||
|
||||
void BsxMemoryPack::SaveBattery()
|
||||
{
|
||||
if(_persistFlash) {
|
||||
if (_persistFlash)
|
||||
{
|
||||
_console->GetBatteryManager()->SaveBattery(".bs", _data, _dataSize);
|
||||
}
|
||||
}
|
||||
|
@ -35,20 +37,24 @@ void BsxMemoryPack::SaveBattery()
|
|||
void BsxMemoryPack::Serialize(Serializer& s)
|
||||
{
|
||||
s.Stream(_enableCsr, _enableEsr, _enableVendorInfo, _writeByte, _command);
|
||||
|
||||
if(s.IsSaving()) {
|
||||
|
||||
if (s.IsSaving())
|
||||
{
|
||||
//Save content of memory pack as an IPS patch
|
||||
vector<uint8_t> newData(_data, _data + _dataSize);
|
||||
vector<uint8_t> ipsData = IpsPatcher::CreatePatch(_orgData, newData);
|
||||
VectorInfo<uint8_t> data { &ipsData };
|
||||
VectorInfo<uint8_t> data{&ipsData};
|
||||
s.Stream(data);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//Apply IPS patch to original data and overwrite the current data
|
||||
vector<uint8_t> ipsData;
|
||||
VectorInfo<uint8_t> data { &ipsData };
|
||||
VectorInfo<uint8_t> data{&ipsData};
|
||||
s.Stream(data);
|
||||
|
||||
if(ipsData.size() > 8) {
|
||||
if (ipsData.size() > 8)
|
||||
{
|
||||
vector<uint8_t> output;
|
||||
IpsPatcher::PatchBuffer(ipsData, _orgData, output);
|
||||
memcpy(_data, output.data(), _dataSize);
|
||||
|
@ -60,27 +66,34 @@ void BsxMemoryPack::ProcessCommand(uint8_t value, uint32_t page)
|
|||
{
|
||||
_command = (_command << 8) | value;
|
||||
|
||||
switch(value) {
|
||||
case 0x00:
|
||||
case 0xFF:
|
||||
_enableCsr = false;
|
||||
_enableEsr = false;
|
||||
_enableVendorInfo = false;
|
||||
break;
|
||||
switch (value)
|
||||
{
|
||||
case 0x00:
|
||||
case 0xFF:
|
||||
_enableCsr = false;
|
||||
_enableEsr = false;
|
||||
_enableVendorInfo = false;
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
case 0x40:
|
||||
_writeByte = true;
|
||||
break;
|
||||
case 0x10:
|
||||
case 0x40:
|
||||
_writeByte = true;
|
||||
break;
|
||||
|
||||
case 0x70: _enableCsr = true; break;
|
||||
case 0x71: _enableEsr = true; break;
|
||||
case 0x75: _enableVendorInfo = true; break;
|
||||
case 0x70: _enableCsr = true;
|
||||
break;
|
||||
case 0x71: _enableEsr = true;
|
||||
break;
|
||||
case 0x75: _enableVendorInfo = true;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(_command) {
|
||||
case 0x20D0: memset(_data + page * 0x10000, 0xFF, 0x10000); break; //Page erase
|
||||
case 0xA7D0: memset(_data, 0xFF, _dataSize); break; //Chip erase
|
||||
switch (_command)
|
||||
{
|
||||
case 0x20D0: memset(_data + page * 0x10000, 0xFF, 0x10000);
|
||||
break; //Page erase
|
||||
case 0xA7D0: memset(_data, 0xFF, _dataSize);
|
||||
break; //Chip erase
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +121,8 @@ uint32_t BsxMemoryPack::DebugGetMemoryPackSize()
|
|||
return _dataSize;
|
||||
}
|
||||
|
||||
BsxMemoryPack::BsxMemoryPackHandler::BsxMemoryPackHandler(BsxMemoryPack* memPack, uint32_t offset) : RamHandler(memPack->_data, offset, memPack->_dataSize, SnesMemoryType::BsxMemoryPack)
|
||||
BsxMemoryPack::BsxMemoryPackHandler::BsxMemoryPackHandler(BsxMemoryPack* memPack, uint32_t offset) : RamHandler(
|
||||
memPack->_data, offset, memPack->_dataSize, SnesMemoryType::BsxMemoryPack)
|
||||
{
|
||||
_memPack = memPack;
|
||||
_page = offset / 0x10000;
|
||||
|
@ -116,30 +130,35 @@ BsxMemoryPack::BsxMemoryPackHandler::BsxMemoryPackHandler(BsxMemoryPack* memPack
|
|||
|
||||
uint8_t BsxMemoryPack::BsxMemoryPackHandler::Read(uint32_t addr)
|
||||
{
|
||||
if(_offset == 0 && _memPack->_enableEsr) {
|
||||
switch(addr & 0xFFF) {
|
||||
case 0x0002: return 0xC0;
|
||||
case 0x0004: return 0x82;
|
||||
if (_offset == 0 && _memPack->_enableEsr)
|
||||
{
|
||||
switch (addr & 0xFFF)
|
||||
{
|
||||
case 0x0002: return 0xC0;
|
||||
case 0x0004: return 0x82;
|
||||
}
|
||||
}
|
||||
|
||||
if(_memPack->_enableCsr) {
|
||||
if (_memPack->_enableCsr)
|
||||
{
|
||||
_memPack->_enableCsr = false;
|
||||
return 0x80;
|
||||
}
|
||||
|
||||
if(_memPack->_enableVendorInfo && ((addr & 0x7FFF) >= 0x7F00) && ((addr & 0x7FFF) <= 0x7F13)) {
|
||||
if (_memPack->_enableVendorInfo && ((addr & 0x7FFF) >= 0x7F00) && ((addr & 0x7FFF) <= 0x7F13))
|
||||
{
|
||||
//Flash cartridge vendor information
|
||||
switch(addr & 0xFF) {
|
||||
case 0x00: return 0x4d;
|
||||
case 0x01: return 0x00;
|
||||
case 0x02: return 0x50;
|
||||
case 0x03: return 0x00;
|
||||
case 0x04: return 0x00;
|
||||
case 0x05: return 0x00;
|
||||
case 0x06: return 0x10 | _memPack->_calculatedSize; //Memory Pack Type 1
|
||||
case 0x07: return 0x00;
|
||||
default: return 0x00;
|
||||
switch (addr & 0xFF)
|
||||
{
|
||||
case 0x00: return 0x4d;
|
||||
case 0x01: return 0x00;
|
||||
case 0x02: return 0x50;
|
||||
case 0x03: return 0x00;
|
||||
case 0x04: return 0x00;
|
||||
case 0x05: return 0x00;
|
||||
case 0x06: return 0x10 | _memPack->_calculatedSize; //Memory Pack Type 1
|
||||
case 0x07: return 0x00;
|
||||
default: return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,11 +167,14 @@ uint8_t BsxMemoryPack::BsxMemoryPackHandler::Read(uint32_t addr)
|
|||
|
||||
void BsxMemoryPack::BsxMemoryPackHandler::Write(uint32_t addr, uint8_t value)
|
||||
{
|
||||
if(_memPack->_writeByte) {
|
||||
if (_memPack->_writeByte)
|
||||
{
|
||||
uint8_t currentByte = RamHandler::Read(addr);
|
||||
RamHandler::Write(addr, value & currentByte);
|
||||
_memPack->_writeByte = false;
|
||||
} else if(_offset == 0 && (addr & 0xFFF) == 0) {
|
||||
}
|
||||
else if (_offset == 0 && (addr & 0xFFF) == 0)
|
||||
{
|
||||
_memPack->ProcessCommand(value, _page);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,11 +35,11 @@ public:
|
|||
|
||||
void ProcessCommand(uint8_t value, uint32_t page);
|
||||
void Reset();
|
||||
|
||||
|
||||
vector<unique_ptr<IMemoryHandler>>& GetMemoryHandlers();
|
||||
uint8_t* DebugGetMemoryPack();
|
||||
uint32_t DebugGetMemoryPackSize();
|
||||
|
||||
|
||||
class BsxMemoryPackHandler : public RamHandler
|
||||
{
|
||||
BsxMemoryPack* _memPack;
|
||||
|
|
|
@ -21,9 +21,12 @@ void BsxSatellaview::Reset()
|
|||
_extOutput = 0xFF;
|
||||
|
||||
time_t resetDate;
|
||||
if(_customDate >= 0) {
|
||||
if (_customDate >= 0)
|
||||
{
|
||||
resetDate = (time_t)_customDate;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//Use the current date/time as the BS-X date/time
|
||||
time(&resetDate);
|
||||
}
|
||||
|
@ -35,35 +38,37 @@ void BsxSatellaview::Reset()
|
|||
uint8_t BsxSatellaview::Read(uint32_t addr)
|
||||
{
|
||||
addr &= 0xFFFF;
|
||||
if(addr >= 0x2188 && addr <= 0x219F) {
|
||||
if (addr >= 0x2188 && addr <= 0x219F)
|
||||
{
|
||||
//Handle BS-X $2188-219F registers
|
||||
ProcessClocks();
|
||||
|
||||
switch(addr) {
|
||||
case 0x2188: return _stream[0].GetChannel() & 0xFF;
|
||||
case 0x2189: return (_stream[0].GetChannel()) >> 8;
|
||||
case 0x218A: return _stream[0].GetPrefixCount();
|
||||
case 0x218B: return _stream[0].GetPrefix();
|
||||
case 0x218C: return _stream[0].GetData();
|
||||
case 0x218D: return _stream[0].GetStatus((_streamReg & 0x01) != 0);
|
||||
|
||||
case 0x218E: return _stream[1].GetChannel() & 0xFF;
|
||||
case 0x218F: return (_stream[1].GetChannel()) >> 8;
|
||||
case 0x2190: return _stream[1].GetPrefixCount();
|
||||
case 0x2191: return _stream[1].GetPrefix();
|
||||
case 0x2192: return _stream[1].GetData();
|
||||
case 0x2193: return _stream[1].GetStatus((_streamReg & 0x01) != 0);
|
||||
switch (addr)
|
||||
{
|
||||
case 0x2188: return _stream[0].GetChannel() & 0xFF;
|
||||
case 0x2189: return (_stream[0].GetChannel()) >> 8;
|
||||
case 0x218A: return _stream[0].GetPrefixCount();
|
||||
case 0x218B: return _stream[0].GetPrefix();
|
||||
case 0x218C: return _stream[0].GetData();
|
||||
case 0x218D: return _stream[0].GetStatus((_streamReg & 0x01) != 0);
|
||||
|
||||
case 0x2194: return _streamReg; //LED and Stream register
|
||||
case 0x2195: return 0; //Unknown
|
||||
case 0x2196: return 0x10; //Satellaview status
|
||||
case 0x2197: return _extOutput; //Soundlink / EXT output
|
||||
case 0x2198: return 0x80; //Serial IO (Serial number)
|
||||
case 0x2199: return 0x01; //Serial IO (???)
|
||||
case 0x219A: return 0x10; //Unknown
|
||||
case 0x218E: return _stream[1].GetChannel() & 0xFF;
|
||||
case 0x218F: return (_stream[1].GetChannel()) >> 8;
|
||||
case 0x2190: return _stream[1].GetPrefixCount();
|
||||
case 0x2191: return _stream[1].GetPrefix();
|
||||
case 0x2192: return _stream[1].GetData();
|
||||
case 0x2193: return _stream[1].GetStatus((_streamReg & 0x01) != 0);
|
||||
|
||||
case 0x2194: return _streamReg; //LED and Stream register
|
||||
case 0x2195: return 0; //Unknown
|
||||
case 0x2196: return 0x10; //Satellaview status
|
||||
case 0x2197: return _extOutput; //Soundlink / EXT output
|
||||
case 0x2198: return 0x80; //Serial IO (Serial number)
|
||||
case 0x2199: return 0x01; //Serial IO (???)
|
||||
case 0x219A: return 0x10; //Unknown
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Use regular B-bus handler for everything else
|
||||
return _bBusHandler->Read(addr);
|
||||
}
|
||||
|
@ -71,25 +76,39 @@ uint8_t BsxSatellaview::Read(uint32_t addr)
|
|||
void BsxSatellaview::Write(uint32_t addr, uint8_t value)
|
||||
{
|
||||
addr &= 0xFFFF;
|
||||
if(addr >= 0x2188 && addr <= 0x219F) {
|
||||
if (addr >= 0x2188 && addr <= 0x219F)
|
||||
{
|
||||
//Handle BS-X register writes
|
||||
ProcessClocks();
|
||||
|
||||
switch(addr) {
|
||||
case 0x2188: _stream[0].SetChannelLow(value); break;
|
||||
case 0x2189: _stream[0].SetChannelHigh(value); break;
|
||||
case 0x218B: _stream[0].SetPrefixLatch(value); break;
|
||||
case 0x218C: _stream[0].SetDataLatch(value); break;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x2188: _stream[0].SetChannelLow(value);
|
||||
break;
|
||||
case 0x2189: _stream[0].SetChannelHigh(value);
|
||||
break;
|
||||
case 0x218B: _stream[0].SetPrefixLatch(value);
|
||||
break;
|
||||
case 0x218C: _stream[0].SetDataLatch(value);
|
||||
break;
|
||||
|
||||
case 0x218E: _stream[1].SetChannelLow(value); break;
|
||||
case 0x218F: _stream[1].SetChannelHigh(value); break;
|
||||
case 0x2191: _stream[1].SetPrefixLatch(value); break;
|
||||
case 0x2192: _stream[1].SetDataLatch(value); break;
|
||||
case 0x218E: _stream[1].SetChannelLow(value);
|
||||
break;
|
||||
case 0x218F: _stream[1].SetChannelHigh(value);
|
||||
break;
|
||||
case 0x2191: _stream[1].SetPrefixLatch(value);
|
||||
break;
|
||||
case 0x2192: _stream[1].SetDataLatch(value);
|
||||
break;
|
||||
|
||||
case 0x2194: _streamReg = value; break;
|
||||
case 0x2197: _extOutput = value; break;
|
||||
case 0x2194: _streamReg = value;
|
||||
break;
|
||||
case 0x2197: _extOutput = value;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//Regular B-bus handler
|
||||
_bBusHandler->Write(addr, value);
|
||||
}
|
||||
|
@ -97,13 +116,16 @@ void BsxSatellaview::Write(uint32_t addr, uint8_t value)
|
|||
|
||||
void BsxSatellaview::ProcessClocks()
|
||||
{
|
||||
if(_stream[0].NeedUpdate() || _stream[1].NeedUpdate()) {
|
||||
if (_stream[0].NeedUpdate() || _stream[1].NeedUpdate())
|
||||
{
|
||||
uint64_t gap = _memoryManager->GetMasterClock() - _prevMasterClock;
|
||||
uint64_t clocksPerFrame = _console->GetMasterClockRate() / 1000; //1000 frames/sec (224kbits/sec)
|
||||
|
||||
while(gap >= clocksPerFrame) {
|
||||
while (gap >= clocksPerFrame)
|
||||
{
|
||||
bool needUpdate = _stream[0].FillQueues() || _stream[1].FillQueues();
|
||||
if(!needUpdate) {
|
||||
if (!needUpdate)
|
||||
{
|
||||
gap = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -111,7 +133,9 @@ void BsxSatellaview::ProcessClocks()
|
|||
}
|
||||
|
||||
_prevMasterClock = _memoryManager->GetMasterClock() - gap;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_prevMasterClock = _memoryManager->GetMasterClock();
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +152,7 @@ void BsxSatellaview::PeekBlock(uint32_t addr, uint8_t* output)
|
|||
|
||||
AddressInfo BsxSatellaview::GetAbsoluteAddress(uint32_t address)
|
||||
{
|
||||
return { -1, SnesMemoryType::Register };
|
||||
return {-1, SnesMemoryType::Register};
|
||||
}
|
||||
|
||||
void BsxSatellaview::Serialize(Serializer& s)
|
||||
|
|
|
@ -24,7 +24,7 @@ private:
|
|||
void ProcessClocks();
|
||||
|
||||
public:
|
||||
BsxSatellaview(Console* console, IMemoryHandler *bBusHandler);
|
||||
BsxSatellaview(Console* console, IMemoryHandler* bBusHandler);
|
||||
|
||||
void Reset();
|
||||
|
||||
|
@ -35,4 +35,4 @@ public:
|
|||
AddressInfo GetAbsoluteAddress(uint32_t address) override;
|
||||
|
||||
void Serialize(Serializer& s) override;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -53,12 +53,15 @@ bool BsxStream::NeedUpdate()
|
|||
|
||||
bool BsxStream::FillQueues()
|
||||
{
|
||||
if(_queueLength > 0) {
|
||||
if (_queueLength > 0)
|
||||
{
|
||||
_queueLength--;
|
||||
if(_prefixLatch && _prefixQueueLength < 0x80) {
|
||||
if (_prefixLatch && _prefixQueueLength < 0x80)
|
||||
{
|
||||
_prefixQueueLength++;
|
||||
}
|
||||
if(_dataLatch && _dataQueueLength < 0x80) {
|
||||
if (_dataLatch && _dataQueueLength < 0x80)
|
||||
{
|
||||
_dataQueueLength++;
|
||||
}
|
||||
}
|
||||
|
@ -80,18 +83,23 @@ bool BsxStream::LoadStreamFile()
|
|||
|
||||
OpenStreamFile();
|
||||
|
||||
if(_file) {
|
||||
if (_file)
|
||||
{
|
||||
_firstPacket = true;
|
||||
_file.seekg(0, ios::end);
|
||||
_queueLength = (uint16_t)std::ceil(_file.tellg() / 22.0);
|
||||
_file.seekg(0, ios::beg);
|
||||
_fileIndex++;
|
||||
return true;
|
||||
} else {
|
||||
if(_fileIndex > 0) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_fileIndex > 0)
|
||||
{
|
||||
//Go back to file #0 and try again
|
||||
_fileIndex = 0;
|
||||
if(LoadStreamFile()) {
|
||||
if (LoadStreamFile())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -104,19 +112,24 @@ bool BsxStream::LoadStreamFile()
|
|||
|
||||
uint8_t BsxStream::GetPrefixCount()
|
||||
{
|
||||
if(!_prefixLatch || !_dataLatch) {
|
||||
if (!_prefixLatch || !_dataLatch)
|
||||
{
|
||||
//Stream is disabled
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(_prefixQueueLength == 0 && _dataQueueLength == 0) {
|
||||
if (_prefixQueueLength == 0 && _dataQueueLength == 0)
|
||||
{
|
||||
//Queue is empty, try to load in new data
|
||||
_fileOffset = 0;
|
||||
if(_channel == 0) {
|
||||
if (_channel == 0)
|
||||
{
|
||||
//Time channel
|
||||
_queueLength = 1;
|
||||
_firstPacket = true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadStreamFile();
|
||||
}
|
||||
}
|
||||
|
@ -126,19 +139,23 @@ uint8_t BsxStream::GetPrefixCount()
|
|||
|
||||
uint8_t BsxStream::GetPrefix()
|
||||
{
|
||||
if(!_prefixLatch) {
|
||||
if (!_prefixLatch)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(_prefixQueueLength > 0) {
|
||||
if (_prefixQueueLength > 0)
|
||||
{
|
||||
_prefix = 0;
|
||||
if(_firstPacket) {
|
||||
if (_firstPacket)
|
||||
{
|
||||
_prefix |= 0x10;
|
||||
_firstPacket = false;
|
||||
}
|
||||
|
||||
_prefixQueueLength--;
|
||||
if(_queueLength == 0 && _prefixQueueLength == 0) {
|
||||
if (_queueLength == 0 && _prefixQueueLength == 0)
|
||||
{
|
||||
//Last packet
|
||||
_prefix |= 0x80;
|
||||
}
|
||||
|
@ -151,15 +168,20 @@ uint8_t BsxStream::GetPrefix()
|
|||
|
||||
uint8_t BsxStream::GetData()
|
||||
{
|
||||
if(!_dataLatch) {
|
||||
if (!_dataLatch)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(_dataQueueLength > 0) {
|
||||
if(_channel == 0) {
|
||||
if (_dataQueueLength > 0)
|
||||
{
|
||||
if (_channel == 0)
|
||||
{
|
||||
//Return Time
|
||||
_data = GetTime();
|
||||
} else if(_file) {
|
||||
}
|
||||
else if (_file)
|
||||
{
|
||||
//Read byte from stream file
|
||||
char byte;
|
||||
_file.get(byte);
|
||||
|
@ -167,7 +189,8 @@ uint8_t BsxStream::GetData()
|
|||
}
|
||||
|
||||
_fileOffset++;
|
||||
if(_fileOffset % 22 == 0) {
|
||||
if (_fileOffset % 22 == 0)
|
||||
{
|
||||
//Finished reading current packet
|
||||
_dataQueueLength--;
|
||||
}
|
||||
|
@ -179,7 +202,8 @@ uint8_t BsxStream::GetData()
|
|||
uint8_t BsxStream::GetStatus(bool reset)
|
||||
{
|
||||
uint8_t status = _status;
|
||||
if(reset) {
|
||||
if (reset)
|
||||
{
|
||||
_status = 0;
|
||||
}
|
||||
return status;
|
||||
|
@ -187,7 +211,8 @@ uint8_t BsxStream::GetStatus(bool reset)
|
|||
|
||||
void BsxStream::SetChannelLow(uint8_t value)
|
||||
{
|
||||
if((_channel & 0xFF) != 0xFF) {
|
||||
if ((_channel & 0xFF) != 0xFF)
|
||||
{
|
||||
_fileIndex = 0;
|
||||
}
|
||||
_channel = (_channel & 0xFF00) | value;
|
||||
|
@ -195,7 +220,8 @@ void BsxStream::SetChannelLow(uint8_t value)
|
|||
|
||||
void BsxStream::SetChannelHigh(uint8_t value)
|
||||
{
|
||||
if((_channel >> 8) != (value & 0x3F)) {
|
||||
if ((_channel >> 8) != (value & 0x3F))
|
||||
{
|
||||
_fileIndex = 0;
|
||||
}
|
||||
_channel = (_channel & 0xFF) | ((value & 0x3F) << 8);
|
||||
|
@ -215,7 +241,8 @@ void BsxStream::SetDataLatch(uint8_t value)
|
|||
|
||||
void BsxStream::InitTimeStruct()
|
||||
{
|
||||
time_t dateTime = _resetDate + ((_memoryManager->GetMasterClock() - _resetMasterClock) / _console->GetMasterClockRate());
|
||||
time_t dateTime = _resetDate + ((_memoryManager->GetMasterClock() - _resetMasterClock) / _console->
|
||||
GetMasterClockRate());
|
||||
|
||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||
localtime_s(&_tm, &dateTime);
|
||||
|
@ -230,30 +257,32 @@ void BsxStream::InitTimeStruct()
|
|||
|
||||
uint8_t BsxStream::GetTime()
|
||||
{
|
||||
if(_fileOffset == 0) {
|
||||
if (_fileOffset == 0)
|
||||
{
|
||||
InitTimeStruct();
|
||||
}
|
||||
|
||||
switch(_fileOffset) {
|
||||
case 0: return 0x00; //Data Group ID / Repetition
|
||||
case 1: return 0x00; //Data Group Link / Continuity
|
||||
case 2: return 0x00; //Data Group Size (24-bit)
|
||||
case 3: return 0x00;
|
||||
case 4: return 0x10;
|
||||
case 5: return 0x01; //Must be 0x01
|
||||
case 6: return 0x01; //Amount of packets (1)
|
||||
case 7: return 0x00; //Offset (24-bit)
|
||||
case 8: return 0x00;
|
||||
case 9: return 0x00;
|
||||
case 10: return _tm.tm_sec;
|
||||
case 11: return _tm.tm_min;
|
||||
case 12: return _tm.tm_hour;
|
||||
case 13: return _tm.tm_wday;
|
||||
case 14: return _tm.tm_mday;
|
||||
case 15: return _tm.tm_mon;
|
||||
case 16: return _tm.tm_year >> 0;
|
||||
case 17: return _tm.tm_year >> 8;
|
||||
default: return 0x00;
|
||||
switch (_fileOffset)
|
||||
{
|
||||
case 0: return 0x00; //Data Group ID / Repetition
|
||||
case 1: return 0x00; //Data Group Link / Continuity
|
||||
case 2: return 0x00; //Data Group Size (24-bit)
|
||||
case 3: return 0x00;
|
||||
case 4: return 0x10;
|
||||
case 5: return 0x01; //Must be 0x01
|
||||
case 6: return 0x01; //Amount of packets (1)
|
||||
case 7: return 0x00; //Offset (24-bit)
|
||||
case 8: return 0x00;
|
||||
case 9: return 0x00;
|
||||
case 10: return _tm.tm_sec;
|
||||
case 11: return _tm.tm_min;
|
||||
case 12: return _tm.tm_hour;
|
||||
case 13: return _tm.tm_wday;
|
||||
case 14: return _tm.tm_mday;
|
||||
case 15: return _tm.tm_mon;
|
||||
case 16: return _tm.tm_year >> 0;
|
||||
case 17: return _tm.tm_year >> 8;
|
||||
default: return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,14 +290,17 @@ void BsxStream::Serialize(Serializer& s)
|
|||
{
|
||||
s.Stream(
|
||||
_channel, _prefix, _data, _status, _prefixLatch, _dataLatch, _firstPacket, _fileOffset, _fileIndex,
|
||||
_queueLength, _prefixQueueLength, _dataQueueLength, _resetDate, _resetMasterClock, _activeChannel, _activeFileIndex
|
||||
_queueLength, _prefixQueueLength, _dataQueueLength, _resetDate, _resetMasterClock, _activeChannel,
|
||||
_activeFileIndex
|
||||
);
|
||||
|
||||
if(!s.IsSaving()) {
|
||||
if (!s.IsSaving())
|
||||
{
|
||||
InitTimeStruct();
|
||||
OpenStreamFile();
|
||||
|
||||
if(_file) {
|
||||
if (_file)
|
||||
{
|
||||
//Seek back to the previous location
|
||||
_file.seekg(_fileOffset, ios::beg);
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@ CallstackManager::~CallstackManager()
|
|||
{
|
||||
}
|
||||
|
||||
void CallstackManager::Push(AddressInfo &src, uint32_t srcAddr, AddressInfo& dest, uint32_t destAddr, AddressInfo& ret, uint32_t returnAddress, StackFrameFlags flags)
|
||||
void CallstackManager::Push(AddressInfo& src, uint32_t srcAddr, AddressInfo& dest, uint32_t destAddr, AddressInfo& ret,
|
||||
uint32_t returnAddress, StackFrameFlags flags)
|
||||
{
|
||||
if(_callstack.size() >= 511) {
|
||||
if (_callstack.size() >= 511)
|
||||
{
|
||||
//Ensure callstack stays below 512 entries - games can use various tricks that could keep making the callstack grow
|
||||
_callstack.pop_front();
|
||||
}
|
||||
|
@ -36,7 +38,8 @@ void CallstackManager::Push(AddressInfo &src, uint32_t srcAddr, AddressInfo& des
|
|||
|
||||
void CallstackManager::Pop(AddressInfo& dest, uint32_t destAddress)
|
||||
{
|
||||
if(_callstack.empty()) {
|
||||
if (_callstack.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -46,14 +49,18 @@ void CallstackManager::Pop(AddressInfo& dest, uint32_t destAddress)
|
|||
|
||||
uint32_t returnAddr = prevFrame.Return;
|
||||
|
||||
if(!_callstack.empty() && destAddress != returnAddr) {
|
||||
if (!_callstack.empty() && destAddress != returnAddr)
|
||||
{
|
||||
//Mismatch, pop that stack frame and add the new one
|
||||
bool foundMatch = false;
|
||||
for(int i = (int)_callstack.size() - 1; i >= 0; i--) {
|
||||
if(destAddress == _callstack[i].Return) {
|
||||
for (int i = (int)_callstack.size() - 1; i >= 0; i--)
|
||||
{
|
||||
if (destAddress == _callstack[i].Return)
|
||||
{
|
||||
//Found a matching stack frame, unstack until that point
|
||||
foundMatch = true;
|
||||
for(int j = (int)_callstack.size() - i - 1; j >= 0; j--) {
|
||||
for (int j = (int)_callstack.size() - i - 1; j >= 0; j--)
|
||||
{
|
||||
_callstack.pop_back();
|
||||
_profiler->UnstackFunction();
|
||||
}
|
||||
|
@ -61,18 +68,21 @@ void CallstackManager::Pop(AddressInfo& dest, uint32_t destAddress)
|
|||
}
|
||||
}
|
||||
|
||||
if(!foundMatch) {
|
||||
if (!foundMatch)
|
||||
{
|
||||
//Couldn't find a matching frame, replace the current one
|
||||
Push(prevFrame.AbsReturn, returnAddr, dest, destAddress, prevFrame.AbsReturn, returnAddr, StackFrameFlags::None);
|
||||
Push(prevFrame.AbsReturn, returnAddr, dest, destAddress, prevFrame.AbsReturn, returnAddr,
|
||||
StackFrameFlags::None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CallstackManager::GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize)
|
||||
void CallstackManager::GetCallstack(StackFrameInfo* callstackArray, uint32_t& callstackSize)
|
||||
{
|
||||
DebugBreakHelper helper(_debugger);
|
||||
int i = 0;
|
||||
for(StackFrameInfo &info : _callstack) {
|
||||
for (StackFrameInfo& info : _callstack)
|
||||
{
|
||||
callstackArray[i] = info;
|
||||
i++;
|
||||
}
|
||||
|
@ -82,7 +92,8 @@ void CallstackManager::GetCallstack(StackFrameInfo* callstackArray, uint32_t &ca
|
|||
int32_t CallstackManager::GetReturnAddress()
|
||||
{
|
||||
DebugBreakHelper helper(_debugger);
|
||||
if(_callstack.empty()) {
|
||||
if (_callstack.empty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return _callstack.back().Return;
|
||||
|
|
|
@ -16,10 +16,11 @@ public:
|
|||
CallstackManager(Debugger* debugger);
|
||||
~CallstackManager();
|
||||
|
||||
void Push(AddressInfo& src, uint32_t srcAddr, AddressInfo& dest, uint32_t destAddr, AddressInfo& ret, uint32_t returnAddress, StackFrameFlags flags);
|
||||
void Push(AddressInfo& src, uint32_t srcAddr, AddressInfo& dest, uint32_t destAddr, AddressInfo& ret,
|
||||
uint32_t returnAddress, StackFrameFlags flags);
|
||||
void Pop(AddressInfo& dest, uint32_t destAddr);
|
||||
|
||||
void GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize);
|
||||
void GetCallstack(StackFrameInfo* callstackArray, uint32_t& callstackSize);
|
||||
int32_t GetReturnAddress();
|
||||
Profiler* GetProfiler();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -17,11 +17,13 @@ void CheatManager::AddCheat(CheatCode code)
|
|||
_hasCheats = true;
|
||||
_bankHasCheats[code.Address >> 16] = true;
|
||||
|
||||
if(code.Address >= 0x7E0000 && code.Address < 0x7E2000) {
|
||||
if (code.Address >= 0x7E0000 && code.Address < 0x7E2000)
|
||||
{
|
||||
//Mirror codes for the first 2kb of workram across all workram mirrors
|
||||
CheatCode mirror;
|
||||
mirror.Value = code.Value;
|
||||
for(int i = 0; i < 0x3F; i++) {
|
||||
for (int i = 0; i < 0x3F; i++)
|
||||
{
|
||||
mirror.Address = (i << 16) | (code.Address & 0xFFFF);
|
||||
AddCheat(mirror);
|
||||
mirror.Address |= 0x800000;
|
||||
|
@ -36,15 +38,21 @@ void CheatManager::SetCheats(vector<CheatCode> codes)
|
|||
|
||||
bool hasCheats = !_cheats.empty();
|
||||
ClearCheats(false);
|
||||
for(CheatCode &code : codes) {
|
||||
for (CheatCode& code : codes)
|
||||
{
|
||||
AddCheat(code);
|
||||
}
|
||||
|
||||
if(codes.size() > 1) {
|
||||
if (codes.size() > 1)
|
||||
{
|
||||
MessageManager::DisplayMessage("Cheats", "CheatsApplied", std::to_string(codes.size()));
|
||||
} else if(codes.size() == 1) {
|
||||
}
|
||||
else if (codes.size() == 1)
|
||||
{
|
||||
MessageManager::DisplayMessage("Cheats", "CheatApplied");
|
||||
} else if(hasCheats) {
|
||||
}
|
||||
else if (hasCheats)
|
||||
{
|
||||
MessageManager::DisplayMessage("Cheats", "CheatsDisabled");
|
||||
}
|
||||
|
||||
|
@ -55,7 +63,8 @@ void CheatManager::SetCheats(uint32_t codes[], uint32_t length)
|
|||
{
|
||||
vector<CheatCode> cheats;
|
||||
cheats.reserve(length);
|
||||
for(uint32_t i = 0; i < length; i++) {
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
{
|
||||
CheatCode code;
|
||||
code.Address = codes[i] >> 8;
|
||||
code.Value = codes[i] & 0xFF;
|
||||
|
@ -75,7 +84,8 @@ void CheatManager::ClearCheats(bool showMessage)
|
|||
_hasCheats = false;
|
||||
memset(_bankHasCheats, 0, sizeof(_bankHasCheats));
|
||||
|
||||
if(showMessage && hadCheats) {
|
||||
if (showMessage && hadCheats)
|
||||
{
|
||||
MessageManager::DisplayMessage("Cheats", "CheatsDisabled");
|
||||
|
||||
//Used by net play
|
||||
|
@ -86,18 +96,22 @@ void CheatManager::ClearCheats(bool showMessage)
|
|||
void CheatManager::AddStringCheat(string code)
|
||||
{
|
||||
static string _convertTable = "DF4709156BC8A23E";
|
||||
|
||||
|
||||
auto lock = _console->AcquireLock();
|
||||
|
||||
|
||||
std::transform(code.begin(), code.end(), code.begin(), ::toupper);
|
||||
|
||||
if(code.size() == 9 && code[4] == '-') {
|
||||
if (code.size() == 9 && code[4] == '-')
|
||||
{
|
||||
uint32_t rawValue = 0;
|
||||
for(int i = 0; i < (int)code.size(); i++) {
|
||||
if(code[i] != '-') {
|
||||
for (int i = 0; i < (int)code.size(); i++)
|
||||
{
|
||||
if (code[i] != '-')
|
||||
{
|
||||
rawValue <<= 4;
|
||||
size_t pos = _convertTable.find_first_of(code[i]);
|
||||
if(pos == string::npos) {
|
||||
if (pos == string::npos)
|
||||
{
|
||||
//Invalid code
|
||||
return;
|
||||
}
|
||||
|
@ -119,9 +133,13 @@ void CheatManager::AddStringCheat(string code)
|
|||
cheat.Value = rawValue >> 24;
|
||||
|
||||
AddCheat(cheat);
|
||||
} else if(code.size() == 8) {
|
||||
for(int i = 0; i < (int)code.size(); i++) {
|
||||
if((code[i] < 'A' || code[i] > 'F') && (code[i] < '0' || code[i] > '9')) {
|
||||
}
|
||||
else if (code.size() == 8)
|
||||
{
|
||||
for (int i = 0; i < (int)code.size(); i++)
|
||||
{
|
||||
if ((code[i] < 'A' || code[i] > 'F') && (code[i] < '0' || code[i] > '9'))
|
||||
{
|
||||
//Invalid code
|
||||
return;
|
||||
}
|
||||
|
@ -138,4 +156,4 @@ void CheatManager::AddStringCheat(string code)
|
|||
vector<CheatCode> CheatManager::GetCheats()
|
||||
{
|
||||
return _cheats;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ private:
|
|||
bool _bankHasCheats[0x100] = {};
|
||||
vector<CheatCode> _cheats;
|
||||
unordered_map<uint32_t, CheatCode> _cheatsByAddress;
|
||||
|
||||
|
||||
void AddCheat(CheatCode code);
|
||||
|
||||
public:
|
||||
|
@ -31,14 +31,16 @@ public:
|
|||
|
||||
vector<CheatCode> GetCheats();
|
||||
|
||||
__forceinline void ApplyCheat(uint32_t addr, uint8_t &value);
|
||||
__forceinline void ApplyCheat(uint32_t addr, uint8_t& value);
|
||||
};
|
||||
|
||||
__forceinline void CheatManager::ApplyCheat(uint32_t addr, uint8_t &value)
|
||||
__forceinline void CheatManager::ApplyCheat(uint32_t addr, uint8_t& value)
|
||||
{
|
||||
if(_hasCheats && _bankHasCheats[addr >> 16]) {
|
||||
if (_hasCheats && _bankHasCheats[addr >> 16])
|
||||
{
|
||||
auto result = _cheatsByAddress.find(addr);
|
||||
if(result != _cheatsByAddress.end()) {
|
||||
if (result != _cheatsByAddress.end())
|
||||
{
|
||||
value = result->second.Value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ public:
|
|||
string PlayerName;
|
||||
bool Spectator;
|
||||
|
||||
ClientConnectionData() {}
|
||||
ClientConnectionData()
|
||||
{
|
||||
}
|
||||
|
||||
ClientConnectionData(string host, uint16_t port, string password, string playerName, bool spectator) :
|
||||
Host(host), Port(port), Password(password), PlayerName(playerName), Spectator(spectator)
|
||||
|
@ -21,4 +23,4 @@ public:
|
|||
~ClientConnectionData()
|
||||
{
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -30,29 +30,37 @@ uint32_t CodeDataLogger::GetPrgSize()
|
|||
bool CodeDataLogger::LoadCdlFile(string cdlFilepath, bool autoResetCdl, uint32_t romCrc)
|
||||
{
|
||||
VirtualFile cdlFile = cdlFilepath;
|
||||
if(cdlFile.IsValid()) {
|
||||
if (cdlFile.IsValid())
|
||||
{
|
||||
uint32_t fileSize = (uint32_t)cdlFile.GetSize();
|
||||
vector<uint8_t> cdlData;
|
||||
cdlFile.ReadFile(cdlData);
|
||||
|
||||
if(fileSize >= _prgSize) {
|
||||
if (fileSize >= _prgSize)
|
||||
{
|
||||
Reset();
|
||||
|
||||
constexpr int headerSize = 9; //"CDLv2" + 4-byte CRC32 value
|
||||
if(memcmp(cdlData.data(), "CDLv2", 5) == 0) {
|
||||
if (memcmp(cdlData.data(), "CDLv2", 5) == 0)
|
||||
{
|
||||
uint32_t savedCrc = cdlData[5] | (cdlData[6] << 8) | (cdlData[7] << 16) | (cdlData[8] << 24);
|
||||
if((autoResetCdl && savedCrc != romCrc) || fileSize < _prgSize + headerSize) {
|
||||
if ((autoResetCdl && savedCrc != romCrc) || fileSize < _prgSize + headerSize)
|
||||
{
|
||||
memset(_cdlData, 0, _prgSize);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(_cdlData, cdlData.data() + headerSize, _prgSize);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//Older CRC-less CDL file, use as-is without checking CRC to avoid data loss
|
||||
memcpy(_cdlData, cdlData.data(), _prgSize);
|
||||
}
|
||||
|
||||
CalculateStats();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +70,8 @@ bool CodeDataLogger::LoadCdlFile(string cdlFilepath, bool autoResetCdl, uint32_t
|
|||
bool CodeDataLogger::SaveCdlFile(string cdlFilepath, uint32_t romCrc)
|
||||
{
|
||||
ofstream cdlFile(cdlFilepath, ios::out | ios::binary);
|
||||
if(cdlFile) {
|
||||
if (cdlFile)
|
||||
{
|
||||
cdlFile.write("CDLv2", 5);
|
||||
cdlFile.put(romCrc & 0xFF);
|
||||
cdlFile.put((romCrc >> 8) & 0xFF);
|
||||
|
@ -80,10 +89,14 @@ void CodeDataLogger::CalculateStats()
|
|||
uint32_t codeSize = 0;
|
||||
uint32_t dataSize = 0;
|
||||
|
||||
for(int i = 0, len = _prgSize; i < len; i++) {
|
||||
if(IsCode(i)) {
|
||||
for (int i = 0, len = _prgSize; i < len; i++)
|
||||
{
|
||||
if (IsCode(i))
|
||||
{
|
||||
codeSize++;
|
||||
} else if(IsData(i)) {
|
||||
}
|
||||
else if (IsData(i))
|
||||
{
|
||||
dataSize++;
|
||||
}
|
||||
}
|
||||
|
@ -94,15 +107,24 @@ void CodeDataLogger::CalculateStats()
|
|||
|
||||
void CodeDataLogger::SetFlags(int32_t absoluteAddr, uint8_t flags)
|
||||
{
|
||||
if(absoluteAddr >= 0 && absoluteAddr < (int32_t)_prgSize) {
|
||||
if((_cdlData[absoluteAddr] & flags) != flags) {
|
||||
if(flags & CdlFlags::Code) {
|
||||
_cdlData[absoluteAddr] = flags | (_cdlData[absoluteAddr] & ~(CdlFlags::Data | CdlFlags::IndexMode8 | CdlFlags::MemoryMode8));
|
||||
} else if(flags & CdlFlags::Data) {
|
||||
if(!IsCode(absoluteAddr)) {
|
||||
if (absoluteAddr >= 0 && absoluteAddr < (int32_t)_prgSize)
|
||||
{
|
||||
if ((_cdlData[absoluteAddr] & flags) != flags)
|
||||
{
|
||||
if (flags & CdlFlags::Code)
|
||||
{
|
||||
_cdlData[absoluteAddr] = flags | (_cdlData[absoluteAddr] & ~(CdlFlags::Data | CdlFlags::IndexMode8 |
|
||||
CdlFlags::MemoryMode8));
|
||||
}
|
||||
else if (flags & CdlFlags::Data)
|
||||
{
|
||||
if (!IsCode(absoluteAddr))
|
||||
{
|
||||
_cdlData[absoluteAddr] |= flags;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_cdlData[absoluteAddr] |= flags;
|
||||
}
|
||||
}
|
||||
|
@ -147,25 +169,31 @@ uint8_t CodeDataLogger::GetCpuFlags(uint32_t absoluteAddr)
|
|||
|
||||
CpuType CodeDataLogger::GetCpuType(uint32_t absoluteAddr)
|
||||
{
|
||||
if(_cpuType == CpuType::Gameboy) {
|
||||
if (_cpuType == CpuType::Gameboy)
|
||||
{
|
||||
return _cpuType;
|
||||
} else if(_cdlData[absoluteAddr] & CdlFlags::Gsu) {
|
||||
}
|
||||
else if (_cdlData[absoluteAddr] & CdlFlags::Gsu)
|
||||
{
|
||||
return CpuType::Gsu;
|
||||
} else if(_cdlData[absoluteAddr] & CdlFlags::Cx4) {
|
||||
}
|
||||
else if (_cdlData[absoluteAddr] & CdlFlags::Cx4)
|
||||
{
|
||||
return CpuType::Cx4;
|
||||
}
|
||||
|
||||
return CpuType::Cpu;
|
||||
}
|
||||
|
||||
void CodeDataLogger::SetCdlData(uint8_t *cdlData, uint32_t length)
|
||||
void CodeDataLogger::SetCdlData(uint8_t* cdlData, uint32_t length)
|
||||
{
|
||||
if(length <= _prgSize) {
|
||||
if (length <= _prgSize)
|
||||
{
|
||||
memcpy(_cdlData, cdlData, length);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeDataLogger::GetCdlData(uint32_t offset, uint32_t length, uint8_t *cdlData)
|
||||
void CodeDataLogger::GetCdlData(uint32_t offset, uint32_t length, uint8_t* cdlData)
|
||||
{
|
||||
memcpy(cdlData, _cdlData + offset, length);
|
||||
}
|
||||
|
@ -177,24 +205,32 @@ uint8_t CodeDataLogger::GetFlags(uint32_t addr)
|
|||
|
||||
void CodeDataLogger::MarkBytesAs(uint32_t start, uint32_t end, uint8_t flags)
|
||||
{
|
||||
for(uint32_t i = start; i <= end; i++) {
|
||||
for (uint32_t i = start; i <= end; i++)
|
||||
{
|
||||
_cdlData[i] = (_cdlData[i] & 0xFC) | (int)flags;
|
||||
}
|
||||
}
|
||||
|
||||
void CodeDataLogger::StripData(uint8_t* romBuffer, CdlStripOption flag)
|
||||
{
|
||||
if(flag == CdlStripOption::StripUnused) {
|
||||
for(uint32_t i = 0; i < _prgSize; i++) {
|
||||
if(_cdlData[i] == 0) {
|
||||
romBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
} else if(flag == CdlStripOption::StripUsed) {
|
||||
for(uint32_t i = 0; i < _prgSize; i++) {
|
||||
if(_cdlData[i] != 0) {
|
||||
if (flag == CdlStripOption::StripUnused)
|
||||
{
|
||||
for (uint32_t i = 0; i < _prgSize; i++)
|
||||
{
|
||||
if (_cdlData[i] == 0)
|
||||
{
|
||||
romBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (flag == CdlStripOption::StripUsed)
|
||||
{
|
||||
for (uint32_t i = 0; i < _prgSize; i++)
|
||||
{
|
||||
if (_cdlData[i] != 0)
|
||||
{
|
||||
romBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ private:
|
|||
uint32_t _prgSize = 0;
|
||||
uint32_t _codeSize = 0;
|
||||
uint32_t _dataSize = 0;
|
||||
|
||||
|
||||
void CalculateStats();
|
||||
|
||||
public:
|
||||
|
@ -34,10 +34,10 @@ public:
|
|||
uint8_t GetCpuFlags(uint32_t absoluteAddr);
|
||||
CpuType GetCpuType(uint32_t absoluteAddr);
|
||||
|
||||
void SetCdlData(uint8_t *cdlData, uint32_t length);
|
||||
void GetCdlData(uint32_t offset, uint32_t length, uint8_t *cdlData);
|
||||
void SetCdlData(uint8_t* cdlData, uint32_t length);
|
||||
void GetCdlData(uint32_t offset, uint32_t length, uint8_t* cdlData);
|
||||
uint8_t GetFlags(uint32_t addr);
|
||||
|
||||
void MarkBytesAs(uint32_t start, uint32_t end, uint8_t flags);
|
||||
void StripData(uint8_t* romBuffer, CdlStripOption flag);
|
||||
};
|
||||
};
|
||||
|
|
359
Core/Console.cpp
359
Core/Console.cpp
|
@ -82,7 +82,7 @@ void Console::Release()
|
|||
|
||||
_videoDecoder->StopThread();
|
||||
_videoRenderer->StopThread();
|
||||
|
||||
|
||||
_videoDecoder.reset();
|
||||
_videoRenderer.reset();
|
||||
_debugHud.reset();
|
||||
|
@ -97,13 +97,18 @@ void Console::Release()
|
|||
void Console::RunFrame()
|
||||
{
|
||||
_frameRunning = true;
|
||||
if(_settings->CheckFlag(EmulationFlags::GameboyMode)) {
|
||||
if (_settings->CheckFlag(EmulationFlags::GameboyMode))
|
||||
{
|
||||
Gameboy* gameboy = _cart->GetGameboy();
|
||||
while(_frameRunning) {
|
||||
while (_frameRunning)
|
||||
{
|
||||
gameboy->Exec();
|
||||
}
|
||||
} else {
|
||||
while(_frameRunning) {
|
||||
}
|
||||
else
|
||||
{
|
||||
while (_frameRunning)
|
||||
{
|
||||
_cpu->Exec();
|
||||
}
|
||||
}
|
||||
|
@ -111,10 +116,11 @@ void Console::RunFrame()
|
|||
|
||||
void Console::Run()
|
||||
{
|
||||
if(!_cpu) {
|
||||
if (!_cpu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
auto emulationLock = _emulationLock.AcquireSafe();
|
||||
auto lock = _runLock.AcquireSafe();
|
||||
|
||||
|
@ -134,14 +140,20 @@ void Console::Run()
|
|||
_frameLimiter.reset(new FrameLimiter(_frameDelay));
|
||||
_lastFrameTimer.Reset();
|
||||
|
||||
while(!_stopFlag) {
|
||||
bool useRunAhead = _settings->GetEmulationConfig().RunAheadFrames > 0 && !_debugger && !_rewindManager->IsRewinding() && _settings->GetEmulationSpeed() > 0 && _settings->GetEmulationSpeed() <= 100;
|
||||
if(useRunAhead) {
|
||||
while (!_stopFlag)
|
||||
{
|
||||
bool useRunAhead = _settings->GetEmulationConfig().RunAheadFrames > 0 && !_debugger && !_rewindManager->
|
||||
IsRewinding() && _settings->GetEmulationSpeed() > 0 && _settings->GetEmulationSpeed() <= 100;
|
||||
if (useRunAhead)
|
||||
{
|
||||
RunFrameWithRunAhead();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
RunFrame();
|
||||
|
||||
if (_historyViewer) {
|
||||
if (_historyViewer)
|
||||
{
|
||||
_historyViewer->ProcessEndOfFrame();
|
||||
}
|
||||
_rewindManager->ProcessEndOfFrame();
|
||||
|
@ -150,16 +162,19 @@ void Console::Run()
|
|||
|
||||
WaitForLock();
|
||||
|
||||
if(_pauseOnNextFrame) {
|
||||
if (_pauseOnNextFrame)
|
||||
{
|
||||
_pauseOnNextFrame = false;
|
||||
_paused = true;
|
||||
}
|
||||
|
||||
if(_paused && !_stopFlag && !_debugger) {
|
||||
if (_paused && !_stopFlag && !_debugger)
|
||||
{
|
||||
WaitForPauseEnd();
|
||||
}
|
||||
}
|
||||
|
||||
if(_memoryManager->GetMasterClock() == 0) {
|
||||
if (_memoryManager->GetMasterClock() == 0)
|
||||
{
|
||||
//After a reset or power cycle, run the PPU/etc ahead of the CPU (simulates delay CPU takes to get out of reset)
|
||||
_memoryManager->IncMasterClockStartup();
|
||||
}
|
||||
|
@ -174,10 +189,13 @@ void Console::Run()
|
|||
|
||||
bool Console::ProcessSystemActions()
|
||||
{
|
||||
if(_controlManager->GetSystemActionManager()->IsResetPressed()) {
|
||||
if (_controlManager->GetSystemActionManager()->IsResetPressed())
|
||||
{
|
||||
Reset();
|
||||
return true;
|
||||
} else if(_controlManager->GetSystemActionManager()->IsPowerCyclePressed()) {
|
||||
}
|
||||
else if (_controlManager->GetSystemActionManager()->IsPowerCyclePressed())
|
||||
{
|
||||
PowerCycle();
|
||||
return true;
|
||||
}
|
||||
|
@ -194,7 +212,8 @@ void Console::RunFrameWithRunAhead()
|
|||
RunFrame();
|
||||
Serialize(runAheadState, 0);
|
||||
|
||||
while(frameCount > 1) {
|
||||
while (frameCount > 1)
|
||||
{
|
||||
//Run extra frames if the requested run ahead frame count is higher than 1
|
||||
frameCount--;
|
||||
RunFrame();
|
||||
|
@ -204,9 +223,10 @@ void Console::RunFrameWithRunAhead()
|
|||
//Run one frame normally (with audio/video output)
|
||||
RunFrame();
|
||||
_rewindManager->ProcessEndOfFrame();
|
||||
|
||||
|
||||
bool wasReset = ProcessSystemActions();
|
||||
if(!wasReset) {
|
||||
if (!wasReset)
|
||||
{
|
||||
//Load the state we saved earlier
|
||||
_isRunAheadFrame = true;
|
||||
Deserialize(runAheadState, SaveStateManager::FileFormatVersion, false);
|
||||
|
@ -218,27 +238,33 @@ void Console::ProcessEndOfFrame()
|
|||
{
|
||||
#ifndef LIBRETRO
|
||||
_cart->RunCoprocessors();
|
||||
if(_cart->GetCoprocessor()) {
|
||||
if (_cart->GetCoprocessor())
|
||||
{
|
||||
_cart->GetCoprocessor()->ProcessEndOfFrame();
|
||||
}
|
||||
|
||||
if(!_isRunAheadFrame) {
|
||||
if (!_isRunAheadFrame)
|
||||
{
|
||||
_frameLimiter->ProcessFrame();
|
||||
while(_frameLimiter->WaitForNextFrame()) {
|
||||
if(_stopFlag || _frameDelay != GetFrameDelay() || _paused || _pauseOnNextFrame || _lockCounter > 0) {
|
||||
while (_frameLimiter->WaitForNextFrame())
|
||||
{
|
||||
if (_stopFlag || _frameDelay != GetFrameDelay() || _paused || _pauseOnNextFrame || _lockCounter > 0)
|
||||
{
|
||||
//Need to process another event, stop sleeping
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double newFrameDelay = GetFrameDelay();
|
||||
if(newFrameDelay != _frameDelay) {
|
||||
if (newFrameDelay != _frameDelay)
|
||||
{
|
||||
_frameDelay = newFrameDelay;
|
||||
_frameLimiter->SetDelay(_frameDelay);
|
||||
}
|
||||
|
||||
PreferencesConfig cfg = _settings->GetPreferences();
|
||||
if(cfg.ShowDebugInfo) {
|
||||
if (cfg.ShowDebugInfo)
|
||||
{
|
||||
double lastFrameTime = _lastFrameTimer.GetElapsedMS();
|
||||
_lastFrameTimer.Reset();
|
||||
_stats->DisplayStats(this, lastFrameTime);
|
||||
|
@ -264,7 +290,8 @@ void Console::RunSingleFrame()
|
|||
RunFrame();
|
||||
|
||||
_cart->RunCoprocessors();
|
||||
if(_cart->GetCoprocessor()) {
|
||||
if (_cart->GetCoprocessor())
|
||||
{
|
||||
_cart->GetCoprocessor()->ProcessEndOfFrame();
|
||||
}
|
||||
|
||||
|
@ -278,24 +305,28 @@ void Console::Stop(bool sendNotification)
|
|||
_notificationManager->SendNotification(ConsoleNotificationType::BeforeGameUnload);
|
||||
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
if (debugger)
|
||||
{
|
||||
debugger->SuspendDebugger(false);
|
||||
debugger->Run();
|
||||
}
|
||||
|
||||
_emulationLock.WaitForRelease();
|
||||
|
||||
if(_emuThread) {
|
||||
if (_emuThread)
|
||||
{
|
||||
_emuThread->join();
|
||||
_emuThread.release();
|
||||
}
|
||||
|
||||
if(_cart && !_settings->GetPreferences().DisableGameSelectionScreen) {
|
||||
if (_cart && !_settings->GetPreferences().DisableGameSelectionScreen)
|
||||
{
|
||||
RomInfo romInfo = _cart->GetRomInfo();
|
||||
_saveStateManager->SaveRecentGame(romInfo.RomFile.GetFileName(), romInfo.RomFile, romInfo.PatchFile);
|
||||
}
|
||||
|
||||
if(sendNotification) {
|
||||
if (sendNotification)
|
||||
{
|
||||
_notificationManager->SendNotification(ConsoleNotificationType::BeforeEmulationStop);
|
||||
}
|
||||
|
||||
|
@ -321,7 +352,8 @@ void Console::Stop(bool sendNotification)
|
|||
|
||||
_soundMixer->StopAudio(true);
|
||||
|
||||
if(sendNotification) {
|
||||
if (sendNotification)
|
||||
{
|
||||
_notificationManager->SendNotification(ConsoleNotificationType::EmulationStopped);
|
||||
}
|
||||
}
|
||||
|
@ -347,28 +379,34 @@ void Console::Reset()
|
|||
_notificationManager->SendNotification(ConsoleNotificationType::GameReset);
|
||||
ProcessEvent(EventType::Reset);
|
||||
|
||||
if(_cart->GetSpcData()) {
|
||||
if (_cart->GetSpcData())
|
||||
{
|
||||
_spc->LoadSpcFile(_cart->GetSpcData());
|
||||
_spcHud.reset(new SpcHud(this, _cart->GetSpcData()));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_spcHud.reset();
|
||||
}
|
||||
|
||||
if(debugger) {
|
||||
if (debugger)
|
||||
{
|
||||
//Debugger was suspended in SystemActionManager::Reset(), resume debugger here
|
||||
debugger->SuspendDebugger(true);
|
||||
}
|
||||
|
||||
_runLock.Release();
|
||||
_runLock.Release();
|
||||
_lockCounter--;
|
||||
}
|
||||
|
||||
void Console::ReloadRom(bool forPowerCycle)
|
||||
{
|
||||
shared_ptr<BaseCartridge> cart = _cart;
|
||||
if(cart) {
|
||||
if (cart)
|
||||
{
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
if (debugger)
|
||||
{
|
||||
debugger->Run();
|
||||
}
|
||||
|
||||
|
@ -386,27 +424,32 @@ void Console::PowerCycle()
|
|||
|
||||
bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom, bool forPowerCycle)
|
||||
{
|
||||
if(_cart) {
|
||||
if (_cart)
|
||||
{
|
||||
//Make sure the battery is saved to disk before we load another game (or reload the same game)
|
||||
_cart->SaveBattery();
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
EmulationConfig orgConfig = _settings->GetEmulationConfig(); //backup emulation config (can be temporarily overriden to control the power on RAM state)
|
||||
EmulationConfig orgConfig = _settings->GetEmulationConfig();
|
||||
//backup emulation config (can be temporarily overriden to control the power on RAM state)
|
||||
shared_ptr<BaseCartridge> cart = forPowerCycle ? _cart : BaseCartridge::CreateCartridge(this, romFile, patchFile);
|
||||
if(cart) {
|
||||
if (cart)
|
||||
{
|
||||
bool debuggerActive = _debugger != nullptr;
|
||||
if(stopRom) {
|
||||
if (stopRom)
|
||||
{
|
||||
KeyManager::UpdateDevices();
|
||||
Stop(false);
|
||||
}
|
||||
|
||||
_cheatManager->ClearCheats(false);
|
||||
|
||||
|
||||
_cart = cart;
|
||||
|
||||
|
||||
auto lock = _debuggerLock.AcquireSafe();
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
//Reset debugger if it was running before
|
||||
_debugger->Release();
|
||||
_debugger.reset();
|
||||
|
@ -425,10 +468,13 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
|
|||
|
||||
_msu1.reset(Msu1::Init(romFile, _spc.get()));
|
||||
|
||||
if(_cart->GetSpcData()) {
|
||||
if (_cart->GetSpcData())
|
||||
{
|
||||
_spc->LoadSpcFile(_cart->GetSpcData());
|
||||
_spcHud.reset(new SpcHud(this, _cart->GetSpcData()));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_spcHud.reset();
|
||||
}
|
||||
|
||||
|
@ -436,16 +482,20 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
|
|||
_memoryManager->Initialize(this);
|
||||
_internalRegisters->Initialize(this);
|
||||
|
||||
if(_cart->GetCoprocessor() == nullptr && _cart->GetGameboy()) {
|
||||
if (_cart->GetCoprocessor() == nullptr && _cart->GetGameboy())
|
||||
{
|
||||
_cart->GetGameboy()->PowerOn();
|
||||
_consoleType = _cart->GetGameboy()->IsCgb() ? ConsoleType::GameboyColor : ConsoleType::Gameboy;
|
||||
_settings->SetFlag(EmulationFlags::GameboyMode);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_consoleType = ConsoleType::Snes;
|
||||
_settings->ClearFlag(EmulationFlags::GameboyMode);
|
||||
}
|
||||
|
||||
if(debuggerActive) {
|
||||
if (debuggerActive)
|
||||
{
|
||||
GetDebugger();
|
||||
}
|
||||
|
||||
|
@ -456,26 +506,31 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
|
|||
_notificationManager->RegisterNotificationListener(_rewindManager);
|
||||
|
||||
_controlManager->UpdateControlDevices();
|
||||
|
||||
|
||||
UpdateRegion();
|
||||
|
||||
_notificationManager->SendNotification(ConsoleNotificationType::GameLoaded, (void*)forPowerCycle);
|
||||
|
||||
_paused = false;
|
||||
|
||||
if(!forPowerCycle) {
|
||||
if (!forPowerCycle)
|
||||
{
|
||||
string modelName = _region == ConsoleRegion::Pal ? "PAL" : "NTSC";
|
||||
string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")";
|
||||
MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetRomInfo().RomFile.GetFileName(), false));
|
||||
MessageManager::DisplayMessage(messageTitle,
|
||||
FolderUtilities::GetFilename(GetRomInfo().RomFile.GetFileName(), false));
|
||||
}
|
||||
|
||||
if(stopRom) {
|
||||
#ifndef LIBRETRO
|
||||
if (stopRom)
|
||||
{
|
||||
#ifndef LIBRETRO
|
||||
_emuThread.reset(new thread(&Console::Run, this));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
result = true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageManager::DisplayMessage("Error", "CouldNotLoadFile", romFile.GetFileName());
|
||||
}
|
||||
|
||||
|
@ -486,18 +541,24 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
|
|||
RomInfo Console::GetRomInfo()
|
||||
{
|
||||
shared_ptr<BaseCartridge> cart = _cart;
|
||||
if(cart) {
|
||||
if (cart)
|
||||
{
|
||||
return cart->GetRomInfo();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Console::GetMasterClock()
|
||||
{
|
||||
if(_settings->CheckFlag(EmulationFlags::GameboyMode) && _cart->GetGameboy()) {
|
||||
if (_settings->CheckFlag(EmulationFlags::GameboyMode) && _cart->GetGameboy())
|
||||
{
|
||||
return _cart->GetGameboy()->GetCycleCount();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return _memoryManager->GetMasterClock();
|
||||
}
|
||||
}
|
||||
|
@ -519,12 +580,16 @@ ConsoleType Console::GetConsoleType()
|
|||
|
||||
void Console::UpdateRegion()
|
||||
{
|
||||
switch(_settings->GetEmulationConfig().Region) {
|
||||
case ConsoleRegion::Auto: _region = _cart->GetRegion(); break;
|
||||
switch (_settings->GetEmulationConfig().Region)
|
||||
{
|
||||
case ConsoleRegion::Auto: _region = _cart->GetRegion();
|
||||
break;
|
||||
|
||||
default:
|
||||
case ConsoleRegion::Ntsc: _region = ConsoleRegion::Ntsc; break;
|
||||
case ConsoleRegion::Pal: _region = ConsoleRegion::Pal; break;
|
||||
default:
|
||||
case ConsoleRegion::Ntsc: _region = ConsoleRegion::Ntsc;
|
||||
break;
|
||||
case ConsoleRegion::Pal: _region = ConsoleRegion::Pal;
|
||||
break;
|
||||
}
|
||||
|
||||
_masterClockRate = _region == ConsoleRegion::Pal ? 21281370 : 21477270;
|
||||
|
@ -532,12 +597,18 @@ void Console::UpdateRegion()
|
|||
|
||||
double Console::GetFps()
|
||||
{
|
||||
if(_settings->CheckFlag(EmulationFlags::GameboyMode)) {
|
||||
if (_settings->CheckFlag(EmulationFlags::GameboyMode))
|
||||
{
|
||||
return 59.72750056960583;
|
||||
} else {
|
||||
if(_region == ConsoleRegion::Ntsc) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_region == ConsoleRegion::Ntsc)
|
||||
{
|
||||
return _settings->GetVideoConfig().IntegerFpsMode ? 60.0 : 60.0988118623484;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return _settings->GetVideoConfig().IntegerFpsMode ? 50.0 : 50.00697796826829;
|
||||
}
|
||||
}
|
||||
|
@ -547,17 +618,28 @@ double Console::GetFrameDelay()
|
|||
{
|
||||
uint32_t emulationSpeed = _settings->GetEmulationSpeed();
|
||||
double frameDelay;
|
||||
if(emulationSpeed == 0) {
|
||||
if (emulationSpeed == 0)
|
||||
{
|
||||
frameDelay = 0;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateRegion();
|
||||
if(_settings->CheckFlag(EmulationFlags::GameboyMode)) {
|
||||
if (_settings->CheckFlag(EmulationFlags::GameboyMode))
|
||||
{
|
||||
frameDelay = 16.74270629882813;
|
||||
} else {
|
||||
switch(_region) {
|
||||
default:
|
||||
case ConsoleRegion::Ntsc: frameDelay = _settings->GetVideoConfig().IntegerFpsMode ? 16.6666666666666666667 : 16.63926405550947; break;
|
||||
case ConsoleRegion::Pal: frameDelay = _settings->GetVideoConfig().IntegerFpsMode ? 20 : 19.99720882631146; break;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (_region)
|
||||
{
|
||||
default:
|
||||
case ConsoleRegion::Ntsc: frameDelay = _settings->GetVideoConfig().IntegerFpsMode
|
||||
? 16.6666666666666666667
|
||||
: 16.63926405550947;
|
||||
break;
|
||||
case ConsoleRegion::Pal: frameDelay = _settings->GetVideoConfig().IntegerFpsMode ? 20 : 19.99720882631146;
|
||||
break;
|
||||
}
|
||||
}
|
||||
frameDelay /= (emulationSpeed / 100.0);
|
||||
|
@ -587,13 +669,19 @@ void Console::CopyRewindData(shared_ptr<Console> sourceConsole)
|
|||
void Console::PauseOnNextFrame()
|
||||
{
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
if(_settings->CheckFlag(EmulationFlags::GameboyMode)) {
|
||||
if (debugger)
|
||||
{
|
||||
if (_settings->CheckFlag(EmulationFlags::GameboyMode))
|
||||
{
|
||||
debugger->Step(CpuType::Gameboy, 144, StepType::SpecificScanline);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
debugger->Step(CpuType::Cpu, 240, StepType::SpecificScanline);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_pauseOnNextFrame = true;
|
||||
_paused = false;
|
||||
}
|
||||
|
@ -602,13 +690,19 @@ void Console::PauseOnNextFrame()
|
|||
void Console::Pause()
|
||||
{
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
if(_settings->CheckFlag(EmulationFlags::GameboyMode)) {
|
||||
if (debugger)
|
||||
{
|
||||
if (_settings->CheckFlag(EmulationFlags::GameboyMode))
|
||||
{
|
||||
debugger->Step(CpuType::Gameboy, 1, StepType::Step);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
debugger->Step(CpuType::Cpu, 1, StepType::Step);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_paused = true;
|
||||
}
|
||||
}
|
||||
|
@ -616,9 +710,12 @@ void Console::Pause()
|
|||
void Console::Resume()
|
||||
{
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
if (debugger)
|
||||
{
|
||||
debugger->Run();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_paused = false;
|
||||
}
|
||||
}
|
||||
|
@ -626,9 +723,12 @@ void Console::Resume()
|
|||
bool Console::IsPaused()
|
||||
{
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
if (debugger)
|
||||
{
|
||||
return debugger->IsExecutionStopped();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return _paused;
|
||||
}
|
||||
}
|
||||
|
@ -643,14 +743,16 @@ void Console::WaitForPauseEnd()
|
|||
|
||||
PlatformUtilities::EnableScreensaver();
|
||||
PlatformUtilities::RestoreTimerResolution();
|
||||
while(_paused && !_stopFlag && !_debugger) {
|
||||
while (_paused && !_stopFlag && !_debugger)
|
||||
{
|
||||
//Sleep until emulation is resumed
|
||||
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(30));
|
||||
}
|
||||
|
||||
PlatformUtilities::DisableScreensaver();
|
||||
_runLock.Acquire();
|
||||
if(!_stopFlag) {
|
||||
if (!_stopFlag)
|
||||
{
|
||||
_notificationManager->SendNotification(ConsoleNotificationType::GameResumed);
|
||||
}
|
||||
}
|
||||
|
@ -663,7 +765,8 @@ ConsoleLock Console::AcquireLock()
|
|||
void Console::Lock()
|
||||
{
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
if (debugger)
|
||||
{
|
||||
debugger->SuspendDebugger(false);
|
||||
}
|
||||
|
||||
|
@ -674,7 +777,8 @@ void Console::Lock()
|
|||
void Console::Unlock()
|
||||
{
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
if (debugger)
|
||||
{
|
||||
debugger->SuspendDebugger(true);
|
||||
}
|
||||
|
||||
|
@ -689,18 +793,24 @@ bool Console::IsThreadPaused()
|
|||
|
||||
void Console::WaitForLock()
|
||||
{
|
||||
if(_lockCounter > 0) {
|
||||
if (_lockCounter > 0)
|
||||
{
|
||||
//Need to temporarely pause the emu (to save/load a state, etc.)
|
||||
_runLock.Release();
|
||||
|
||||
_threadPaused = true;
|
||||
|
||||
//Spin wait until we are allowed to start again
|
||||
while(_lockCounter > 0) {}
|
||||
while (_lockCounter > 0)
|
||||
{
|
||||
}
|
||||
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(debugger) {
|
||||
while(debugger->HasBreakRequest()) {}
|
||||
if (debugger)
|
||||
{
|
||||
while (debugger->HasBreakRequest())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
_threadPaused = false;
|
||||
|
@ -709,12 +819,13 @@ void Console::WaitForLock()
|
|||
}
|
||||
}
|
||||
|
||||
void Console::Serialize(ostream &out, int compressionLevel)
|
||||
void Console::Serialize(ostream& out, int compressionLevel)
|
||||
{
|
||||
Serializer serializer(SaveStateManager::FileFormatVersion);
|
||||
bool isGameboyMode = _settings->CheckFlag(EmulationFlags::GameboyMode);
|
||||
|
||||
if(!isGameboyMode) {
|
||||
if (!isGameboyMode)
|
||||
{
|
||||
serializer.Stream(_cpu.get());
|
||||
serializer.Stream(_memoryManager.get());
|
||||
serializer.Stream(_ppu.get());
|
||||
|
@ -723,22 +834,26 @@ void Console::Serialize(ostream &out, int compressionLevel)
|
|||
serializer.Stream(_cart.get());
|
||||
serializer.Stream(_controlManager.get());
|
||||
serializer.Stream(_spc.get());
|
||||
if(_msu1) {
|
||||
if (_msu1)
|
||||
{
|
||||
serializer.Stream(_msu1.get());
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
serializer.Stream(_cart.get());
|
||||
serializer.Stream(_controlManager.get());
|
||||
}
|
||||
serializer.Save(out, compressionLevel);
|
||||
}
|
||||
|
||||
void Console::Deserialize(istream &in, uint32_t fileFormatVersion, bool compressed)
|
||||
void Console::Deserialize(istream& in, uint32_t fileFormatVersion, bool compressed)
|
||||
{
|
||||
Serializer serializer(in, fileFormatVersion, compressed);
|
||||
bool isGameboyMode = _settings->CheckFlag(EmulationFlags::GameboyMode);
|
||||
|
||||
if(!isGameboyMode) {
|
||||
if (!isGameboyMode)
|
||||
{
|
||||
serializer.Stream(_cpu.get());
|
||||
serializer.Stream(_memoryManager.get());
|
||||
serializer.Stream(_ppu.get());
|
||||
|
@ -747,10 +862,13 @@ void Console::Deserialize(istream &in, uint32_t fileFormatVersion, bool compress
|
|||
serializer.Stream(_cart.get());
|
||||
serializer.Stream(_controlManager.get());
|
||||
serializer.Stream(_spc.get());
|
||||
if(_msu1) {
|
||||
if (_msu1)
|
||||
{
|
||||
serializer.Stream(_msu1.get());
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
serializer.Stream(_cart.get());
|
||||
serializer.Stream(_controlManager.get());
|
||||
}
|
||||
|
@ -860,11 +978,13 @@ shared_ptr<Msu1> Console::GetMsu1()
|
|||
shared_ptr<Debugger> Console::GetDebugger(bool autoStart)
|
||||
{
|
||||
shared_ptr<Debugger> debugger = _debugger;
|
||||
if(!debugger && autoStart) {
|
||||
if (!debugger && autoStart)
|
||||
{
|
||||
//Lock to make sure we don't try to start debuggers in 2 separate threads at once
|
||||
auto lock = _debuggerLock.AcquireSafe();
|
||||
debugger = _debugger;
|
||||
if(!debugger) {
|
||||
if (!debugger)
|
||||
{
|
||||
debugger.reset(new Debugger(shared_from_this()));
|
||||
_debugger = debugger;
|
||||
}
|
||||
|
@ -908,37 +1028,44 @@ bool Console::IsRunAheadFrame()
|
|||
uint32_t Console::GetFrameCount()
|
||||
{
|
||||
shared_ptr<BaseCartridge> cart = _cart;
|
||||
if(_settings->CheckFlag(EmulationFlags::GameboyMode) && cart->GetGameboy()) {
|
||||
if (_settings->CheckFlag(EmulationFlags::GameboyMode) && cart->GetGameboy())
|
||||
{
|
||||
GbPpu* ppu = cart->GetGameboy()->GetPpu();
|
||||
return ppu ? ppu->GetFrameCount() : 0;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
shared_ptr<Ppu> ppu = _ppu;
|
||||
return ppu ? ppu->GetFrameCount() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<CpuType type>
|
||||
template <CpuType type>
|
||||
void Console::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->ProcessInterrupt<type>(originalPc, currentPc, forNmi);
|
||||
}
|
||||
}
|
||||
|
||||
void Console::ProcessEvent(EventType type)
|
||||
{
|
||||
if(type == EventType::EndFrame && _spcHud) {
|
||||
if (type == EventType::EndFrame && _spcHud)
|
||||
{
|
||||
_spcHud->Draw(GetFrameCount());
|
||||
}
|
||||
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->ProcessEvent(type);
|
||||
}
|
||||
}
|
||||
|
||||
void Console::BreakImmediately(BreakSource source)
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->BreakImmediately(source);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ private:
|
|||
shared_ptr<InternalRegisters> _internalRegisters;
|
||||
shared_ptr<ControlManager> _controlManager;
|
||||
shared_ptr<DmaController> _dmaController;
|
||||
|
||||
|
||||
shared_ptr<Msu1> _msu1;
|
||||
|
||||
shared_ptr<Debugger> _debugger;
|
||||
|
@ -73,7 +73,7 @@ private:
|
|||
shared_ptr<SpcHud> _spcHud;
|
||||
|
||||
thread::id _emulationThreadId;
|
||||
|
||||
|
||||
atomic<uint32_t> _lockCounter;
|
||||
SimpleLock _runLock;
|
||||
SimpleLock _emulationLock;
|
||||
|
@ -124,7 +124,7 @@ public:
|
|||
void PowerCycle();
|
||||
|
||||
void PauseOnNextFrame();
|
||||
|
||||
|
||||
void Pause();
|
||||
void Resume();
|
||||
bool IsPaused();
|
||||
|
@ -141,8 +141,8 @@ public:
|
|||
void Unlock();
|
||||
bool IsThreadPaused();
|
||||
|
||||
void Serialize(ostream &out, int compressionLevel = 1);
|
||||
void Deserialize(istream &in, uint32_t fileFormatVersion, bool compressed = true);
|
||||
void Serialize(ostream& out, int compressionLevel = 1);
|
||||
void Deserialize(istream& in, uint32_t fileFormatVersion, bool compressed = true);
|
||||
|
||||
shared_ptr<SoundMixer> GetSoundMixer();
|
||||
shared_ptr<VideoRenderer> GetVideoRenderer();
|
||||
|
@ -172,72 +172,84 @@ public:
|
|||
bool IsDebugging();
|
||||
|
||||
thread::id GetEmulationThreadId();
|
||||
|
||||
|
||||
bool IsRunning();
|
||||
bool IsRunAheadFrame();
|
||||
|
||||
uint32_t GetFrameCount();
|
||||
uint32_t GetFrameCount();
|
||||
double GetFps();
|
||||
|
||||
void CopyRewindData(shared_ptr<Console> sourceConsole);
|
||||
|
||||
template<CpuType type> __forceinline void ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType)
|
||||
template <CpuType type>
|
||||
__forceinline void ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType)
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->ProcessMemoryRead<type>(addr, value, opType);
|
||||
}
|
||||
}
|
||||
|
||||
template<CpuType type> __forceinline void ProcessMemoryWrite(uint32_t addr, uint8_t value, MemoryOperationType opType)
|
||||
template <CpuType type>
|
||||
__forceinline void ProcessMemoryWrite(uint32_t addr, uint8_t value, MemoryOperationType opType)
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->ProcessMemoryWrite<type>(addr, value, opType);
|
||||
}
|
||||
}
|
||||
|
||||
__forceinline void ProcessPpuRead(uint32_t addr, uint8_t value, SnesMemoryType memoryType)
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->ProcessPpuRead(addr, value, memoryType);
|
||||
}
|
||||
}
|
||||
|
||||
__forceinline void ProcessPpuWrite(uint32_t addr, uint8_t value, SnesMemoryType memoryType)
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->ProcessPpuWrite(addr, value, memoryType);
|
||||
}
|
||||
}
|
||||
|
||||
__forceinline void ProcessWorkRamRead(uint32_t addr, uint8_t value)
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->ProcessWorkRamRead(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
__forceinline void ProcessWorkRamWrite(uint32_t addr, uint8_t value)
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->ProcessWorkRamWrite(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
template<CpuType cpuType> __forceinline void ProcessPpuCycle()
|
||||
|
||||
template <CpuType cpuType>
|
||||
__forceinline void ProcessPpuCycle()
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->ProcessPpuCycle<cpuType>();
|
||||
}
|
||||
}
|
||||
|
||||
__forceinline void DebugLog(string log)
|
||||
{
|
||||
if(_debugger) {
|
||||
if (_debugger)
|
||||
{
|
||||
_debugger->Log(log);
|
||||
}
|
||||
}
|
||||
|
||||
template<CpuType type> void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);
|
||||
template <CpuType type>
|
||||
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);
|
||||
void ProcessEvent(EventType type);
|
||||
void BreakImmediately(BreakSource source);
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "Console.h"
|
||||
#include "Debugger.h"
|
||||
|
||||
ConsoleLock::ConsoleLock(Console *console)
|
||||
ConsoleLock::ConsoleLock(Console* console)
|
||||
{
|
||||
_console = console;
|
||||
_console->Lock();
|
||||
|
|
|
@ -7,9 +7,9 @@ class Debugger;
|
|||
class ConsoleLock
|
||||
{
|
||||
private:
|
||||
Console *_console = nullptr;
|
||||
Console* _console = nullptr;
|
||||
|
||||
public:
|
||||
ConsoleLock(Console *console);
|
||||
ConsoleLock(Console* console);
|
||||
~ConsoleLock();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@ struct ControlDeviceState
|
|||
{
|
||||
vector<uint8_t> State;
|
||||
|
||||
bool operator!=(ControlDeviceState &other)
|
||||
bool operator!=(ControlDeviceState& other)
|
||||
{
|
||||
return State.size() != other.State.size() || memcmp(State.data(), other.State.data(), State.size()) != 0;
|
||||
}
|
||||
|
@ -17,4 +17,4 @@ struct ControllerData
|
|||
{
|
||||
ControllerType Type;
|
||||
ControlDeviceState State;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -37,7 +37,7 @@ void ControlManager::RegisterInputProvider(IInputProvider* provider)
|
|||
void ControlManager::UnregisterInputProvider(IInputProvider* provider)
|
||||
{
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
vector<IInputProvider*> &vec = _inputProviders;
|
||||
vector<IInputProvider*>& vec = _inputProviders;
|
||||
vec.erase(std::remove(vec.begin(), vec.end(), provider), vec.end());
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ void ControlManager::RegisterInputRecorder(IInputRecorder* provider)
|
|||
void ControlManager::UnregisterInputRecorder(IInputRecorder* provider)
|
||||
{
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
vector<IInputRecorder*> &vec = _inputRecorders;
|
||||
vector<IInputRecorder*>& vec = _inputRecorders;
|
||||
vec.erase(std::remove(vec.begin(), vec.end(), provider), vec.end());
|
||||
}
|
||||
|
||||
|
@ -59,12 +59,16 @@ vector<ControllerData> ControlManager::GetPortStates()
|
|||
auto lock = _deviceLock.AcquireSafe();
|
||||
|
||||
vector<ControllerData> states;
|
||||
for(int i = 0; i < 2; i++) {
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
shared_ptr<BaseControlDevice> device = GetControlDevice(i);
|
||||
if(device) {
|
||||
states.push_back({ device->GetControllerType(), device->GetRawState() });
|
||||
} else {
|
||||
states.push_back({ ControllerType::None, ControlDeviceState()});
|
||||
if (device)
|
||||
{
|
||||
states.push_back({device->GetControllerType(), device->GetRawState()});
|
||||
}
|
||||
else
|
||||
{
|
||||
states.push_back({ControllerType::None, ControlDeviceState()});
|
||||
}
|
||||
}
|
||||
return states;
|
||||
|
@ -79,8 +83,13 @@ shared_ptr<BaseControlDevice> ControlManager::GetControlDevice(uint8_t port)
|
|||
{
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
|
||||
auto result = std::find_if(_controlDevices.begin(), _controlDevices.end(), [port](const shared_ptr<BaseControlDevice> control) { return control->GetPort() == port; });
|
||||
if(result != _controlDevices.end()) {
|
||||
auto result = std::find_if(_controlDevices.begin(), _controlDevices.end(),
|
||||
[port](const shared_ptr<BaseControlDevice> control)
|
||||
{
|
||||
return control->GetPort() == port;
|
||||
});
|
||||
if (result != _controlDevices.end())
|
||||
{
|
||||
return *result;
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -101,35 +110,46 @@ ControllerType ControlManager::GetControllerType(uint8_t port)
|
|||
return _console->GetSettings()->GetInputConfig().Controllers[port].Type;
|
||||
}
|
||||
|
||||
shared_ptr<BaseControlDevice> ControlManager::CreateControllerDevice(ControllerType type, uint8_t port, Console* console)
|
||||
shared_ptr<BaseControlDevice> ControlManager::CreateControllerDevice(ControllerType type, uint8_t port,
|
||||
Console* console)
|
||||
{
|
||||
shared_ptr<BaseControlDevice> device;
|
||||
|
||||
|
||||
InputConfig cfg = console->GetSettings()->GetInputConfig();
|
||||
|
||||
switch(type) {
|
||||
case ControllerType::None: break;
|
||||
case ControllerType::SnesController: device.reset(new SnesController(console, port, cfg.Controllers[port].Keys)); break;
|
||||
case ControllerType::SnesMouse: device.reset(new SnesMouse(console, port)); break;
|
||||
case ControllerType::SuperScope: device.reset(new SuperScope(console, port, cfg.Controllers[port].Keys)); break;
|
||||
case ControllerType::Multitap: device.reset(new Multitap(console, port, cfg.Controllers[port].Keys, cfg.Controllers[2].Keys, cfg.Controllers[3].Keys, cfg.Controllers[4].Keys)); break;
|
||||
switch (type)
|
||||
{
|
||||
case ControllerType::None: break;
|
||||
case ControllerType::SnesController: device.reset(new SnesController(console, port, cfg.Controllers[port].Keys));
|
||||
break;
|
||||
case ControllerType::SnesMouse: device.reset(new SnesMouse(console, port));
|
||||
break;
|
||||
case ControllerType::SuperScope: device.reset(new SuperScope(console, port, cfg.Controllers[port].Keys));
|
||||
break;
|
||||
case ControllerType::Multitap: device.reset(new Multitap(console, port, cfg.Controllers[port].Keys,
|
||||
cfg.Controllers[2].Keys, cfg.Controllers[3].Keys,
|
||||
cfg.Controllers[4].Keys));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
void ControlManager::UpdateControlDevices()
|
||||
{
|
||||
uint32_t version = _console->GetSettings()->GetInputConfigVersion();
|
||||
if(_inputConfigVersion != version) {
|
||||
if (_inputConfigVersion != version)
|
||||
{
|
||||
_inputConfigVersion = version;
|
||||
|
||||
auto lock = _deviceLock.AcquireSafe();
|
||||
_controlDevices.clear();
|
||||
RegisterControlDevice(_systemActionManager);
|
||||
for(int i = 0; i < 2; i++) {
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
shared_ptr<BaseControlDevice> device = CreateControllerDevice(GetControllerType(i), i, _console);
|
||||
if(device) {
|
||||
if (device)
|
||||
{
|
||||
RegisterControlDevice(device);
|
||||
}
|
||||
}
|
||||
|
@ -143,13 +163,16 @@ void ControlManager::UpdateInputState()
|
|||
auto lock = _deviceLock.AcquireSafe();
|
||||
|
||||
//string log = "F: " + std::to_string(_console->GetPpu()->GetFrameCount()) + " C:" + std::to_string(_pollCounter) + " ";
|
||||
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
|
||||
for (shared_ptr<BaseControlDevice>& device : _controlDevices)
|
||||
{
|
||||
device->ClearState();
|
||||
device->SetStateFromInput();
|
||||
|
||||
for(size_t i = 0; i < _inputProviders.size(); i++) {
|
||||
for (size_t i = 0; i < _inputProviders.size(); i++)
|
||||
{
|
||||
IInputProvider* provider = _inputProviders[i];
|
||||
if(provider->SetInput(device.get())) {
|
||||
if (provider->SetInput(device.get()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -159,12 +182,15 @@ void ControlManager::UpdateInputState()
|
|||
}
|
||||
|
||||
shared_ptr<Debugger> debugger = _console->GetDebugger(false);
|
||||
if(debugger) {
|
||||
if (debugger)
|
||||
{
|
||||
debugger->ProcessEvent(EventType::InputPolled);
|
||||
}
|
||||
|
||||
if(!_console->IsRunAheadFrame()) {
|
||||
for(IInputRecorder* recorder : _inputRecorders) {
|
||||
if (!_console->IsRunAheadFrame())
|
||||
{
|
||||
for (IInputRecorder* recorder : _inputRecorders)
|
||||
{
|
||||
recorder->RecordInput(_controlDevices);
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +213,8 @@ void ControlManager::SetPollCounter(uint32_t value)
|
|||
uint8_t ControlManager::Read(uint16_t addr)
|
||||
{
|
||||
uint8_t value = _console->GetMemoryManager()->GetOpenBus() & (addr == 0x4016 ? 0xFC : 0xE0);
|
||||
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
|
||||
for (shared_ptr<BaseControlDevice>& device : _controlDevices)
|
||||
{
|
||||
value |= device->ReadRam(addr);
|
||||
}
|
||||
|
||||
|
@ -196,21 +223,25 @@ uint8_t ControlManager::Read(uint16_t addr)
|
|||
|
||||
void ControlManager::Write(uint16_t addr, uint8_t value)
|
||||
{
|
||||
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
|
||||
for (shared_ptr<BaseControlDevice>& device : _controlDevices)
|
||||
{
|
||||
device->WriteRam(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
void ControlManager::Serialize(Serializer &s)
|
||||
void ControlManager::Serialize(Serializer& s)
|
||||
{
|
||||
InputConfig cfg = _console->GetSettings()->GetInputConfig();
|
||||
s.Stream(cfg.Controllers[0].Type, cfg.Controllers[1].Type, cfg.Controllers[2].Type, cfg.Controllers[3].Type, cfg.Controllers[4].Type);
|
||||
if(!s.IsSaving()) {
|
||||
s.Stream(cfg.Controllers[0].Type, cfg.Controllers[1].Type, cfg.Controllers[2].Type, cfg.Controllers[3].Type,
|
||||
cfg.Controllers[4].Type);
|
||||
if (!s.IsSaving())
|
||||
{
|
||||
_console->GetSettings()->SetInputConfig(cfg);
|
||||
UpdateControlDevices();
|
||||
}
|
||||
|
||||
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
|
||||
for (shared_ptr<BaseControlDevice>& device : _controlDevices)
|
||||
{
|
||||
s.Stream(device.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class ControlManager : public ISerializable
|
|||
private:
|
||||
vector<IInputRecorder*> _inputRecorders;
|
||||
vector<IInputProvider*> _inputProviders;
|
||||
|
||||
|
||||
uint32_t _pollCounter;
|
||||
uint32_t _inputConfigVersion;
|
||||
|
||||
|
@ -54,11 +54,11 @@ public:
|
|||
SystemActionManager* GetSystemActionManager();
|
||||
shared_ptr<BaseControlDevice> GetControlDevice(uint8_t port);
|
||||
vector<shared_ptr<BaseControlDevice>> GetControlDevices();
|
||||
|
||||
|
||||
static shared_ptr<BaseControlDevice> CreateControllerDevice(ControllerType type, uint8_t port, Console* console);
|
||||
|
||||
uint8_t Read(uint16_t addr);
|
||||
void Write(uint16_t addr, uint8_t value);
|
||||
|
||||
void Serialize(Serializer &s) override;
|
||||
void Serialize(Serializer& s) override;
|
||||
};
|
||||
|
|
|
@ -4,28 +4,36 @@ Add/substract operations
|
|||
void Cpu::Add8(uint8_t value)
|
||||
{
|
||||
uint32_t result;
|
||||
if(CheckFlag(ProcFlags::Decimal)) {
|
||||
if (CheckFlag(ProcFlags::Decimal))
|
||||
{
|
||||
result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
|
||||
if(result > 0x09) result += 0x06;
|
||||
if (result > 0x09) result += 0x06;
|
||||
result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (_state.A & 0xFF) + value + (_state.PS & ProcFlags::Carry);
|
||||
}
|
||||
|
||||
if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) {
|
||||
if (~(_state.A ^ value) & (_state.A ^ result) & 0x80)
|
||||
{
|
||||
SetFlags(ProcFlags::Overflow);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Overflow);
|
||||
}
|
||||
|
||||
if(CheckFlag(ProcFlags::Decimal) && result > 0x9F) {
|
||||
if (CheckFlag(ProcFlags::Decimal) && result > 0x9F)
|
||||
{
|
||||
result += 0x60;
|
||||
}
|
||||
|
||||
ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
|
||||
SetZeroNegativeFlags((uint8_t)result);
|
||||
|
||||
if(result > 0xFF) {
|
||||
if (result > 0xFF)
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
}
|
||||
|
||||
|
@ -35,35 +43,43 @@ void Cpu::Add8(uint8_t value)
|
|||
void Cpu::Add16(uint16_t value)
|
||||
{
|
||||
uint32_t result;
|
||||
if(CheckFlag(ProcFlags::Decimal)) {
|
||||
if (CheckFlag(ProcFlags::Decimal))
|
||||
{
|
||||
result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
|
||||
|
||||
if(result > 0x09) result += 0x06;
|
||||
if (result > 0x09) result += 0x06;
|
||||
result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
|
||||
|
||||
if(result > 0x9F) result += 0x60;
|
||||
if (result > 0x9F) result += 0x60;
|
||||
result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF);
|
||||
|
||||
if(result > 0x9FF) result += 0x600;
|
||||
if (result > 0x9FF) result += 0x600;
|
||||
result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _state.A + value + (_state.PS & ProcFlags::Carry);
|
||||
}
|
||||
|
||||
if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) {
|
||||
if (~(_state.A ^ value) & (_state.A ^ result) & 0x8000)
|
||||
{
|
||||
SetFlags(ProcFlags::Overflow);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Overflow);
|
||||
}
|
||||
|
||||
if(CheckFlag(ProcFlags::Decimal) && result > 0x9FFF) {
|
||||
if (CheckFlag(ProcFlags::Decimal) && result > 0x9FFF)
|
||||
{
|
||||
result += 0x6000;
|
||||
}
|
||||
|
||||
ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
|
||||
SetZeroNegativeFlags((uint16_t)result);
|
||||
|
||||
if(result > 0xFFFF) {
|
||||
if (result > 0xFFFF)
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
}
|
||||
|
||||
|
@ -72,9 +88,12 @@ void Cpu::Add16(uint16_t value)
|
|||
|
||||
void Cpu::ADC()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
Add8(GetByteValue());
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Add16(GetWordValue());
|
||||
}
|
||||
}
|
||||
|
@ -82,28 +101,36 @@ void Cpu::ADC()
|
|||
void Cpu::Sub8(uint8_t value)
|
||||
{
|
||||
int32_t result;
|
||||
if(CheckFlag(ProcFlags::Decimal)) {
|
||||
if (CheckFlag(ProcFlags::Decimal))
|
||||
{
|
||||
result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
|
||||
if(result <= 0x0F) result -= 0x06;
|
||||
if (result <= 0x0F) result -= 0x06;
|
||||
result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (_state.A & 0xFF) + value + (_state.PS & ProcFlags::Carry);
|
||||
}
|
||||
|
||||
if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) {
|
||||
if (~(_state.A ^ value) & (_state.A ^ result) & 0x80)
|
||||
{
|
||||
SetFlags(ProcFlags::Overflow);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Overflow);
|
||||
}
|
||||
|
||||
if(CheckFlag(ProcFlags::Decimal) && result <= 0xFF) {
|
||||
if (CheckFlag(ProcFlags::Decimal) && result <= 0xFF)
|
||||
{
|
||||
result -= 0x60;
|
||||
}
|
||||
|
||||
ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
|
||||
SetZeroNegativeFlags((uint8_t)result);
|
||||
|
||||
if(result > 0xFF) {
|
||||
if (result > 0xFF)
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
}
|
||||
|
||||
|
@ -113,35 +140,43 @@ void Cpu::Sub8(uint8_t value)
|
|||
void Cpu::Sub16(uint16_t value)
|
||||
{
|
||||
int32_t result;
|
||||
if(CheckFlag(ProcFlags::Decimal)) {
|
||||
if (CheckFlag(ProcFlags::Decimal))
|
||||
{
|
||||
result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
|
||||
|
||||
if(result <= 0x0F) result -= 0x06;
|
||||
if (result <= 0x0F) result -= 0x06;
|
||||
result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
|
||||
|
||||
if(result <= 0xFF) result -= 0x60;
|
||||
if (result <= 0xFF) result -= 0x60;
|
||||
result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF);
|
||||
|
||||
if(result <= 0xFFF) result -= 0x600;
|
||||
if (result <= 0xFFF) result -= 0x600;
|
||||
result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _state.A + value + (_state.PS & ProcFlags::Carry);
|
||||
}
|
||||
|
||||
if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) {
|
||||
if (~(_state.A ^ value) & (_state.A ^ result) & 0x8000)
|
||||
{
|
||||
SetFlags(ProcFlags::Overflow);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Overflow);
|
||||
}
|
||||
|
||||
if(CheckFlag(ProcFlags::Decimal) && result <= 0xFFFF) {
|
||||
if (CheckFlag(ProcFlags::Decimal) && result <= 0xFFFF)
|
||||
{
|
||||
result -= 0x6000;
|
||||
}
|
||||
|
||||
ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
|
||||
SetZeroNegativeFlags((uint16_t)result);
|
||||
|
||||
if(result > 0xFFFF) {
|
||||
if (result > 0xFFFF)
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
}
|
||||
|
||||
|
@ -150,9 +185,12 @@ void Cpu::Sub16(uint16_t value)
|
|||
|
||||
void Cpu::SBC()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
Sub8(~GetByteValue());
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Sub16(~GetWordValue());
|
||||
}
|
||||
}
|
||||
|
@ -213,10 +251,12 @@ void Cpu::BVS()
|
|||
|
||||
void Cpu::BranchRelative(bool branch)
|
||||
{
|
||||
if(branch) {
|
||||
if (branch)
|
||||
{
|
||||
int8_t offset = _operand;
|
||||
Idle();
|
||||
if(_state.EmulationMode && ((uint16_t)(_state.PC + offset) & 0xFF00) != (_state.PC & 0xFF00)) {
|
||||
if (_state.EmulationMode && ((uint16_t)(_state.PC + offset) & 0xFF00) != (_state.PC & 0xFF00))
|
||||
{
|
||||
//Extra cycle in emulation mode if crossing a page
|
||||
Idle();
|
||||
}
|
||||
|
@ -273,7 +313,8 @@ void Cpu::SEP()
|
|||
{
|
||||
Idle();
|
||||
SetFlags((uint8_t)_operand);
|
||||
if(CheckFlag(ProcFlags::IndexMode8)) {
|
||||
if (CheckFlag(ProcFlags::IndexMode8))
|
||||
{
|
||||
//Truncate X/Y when 8-bit indexes are enabled
|
||||
_state.Y &= 0xFF;
|
||||
_state.X &= 0xFF;
|
||||
|
@ -323,19 +364,22 @@ void Cpu::INC_Acc()
|
|||
SetRegister(_state.A, _state.A + 1, CheckFlag(ProcFlags::MemoryMode8));
|
||||
}
|
||||
|
||||
void Cpu::IncDecReg(uint16_t ®, int8_t offset)
|
||||
void Cpu::IncDecReg(uint16_t& reg, int8_t offset)
|
||||
{
|
||||
SetRegister(reg, reg + offset, CheckFlag(ProcFlags::IndexMode8));
|
||||
}
|
||||
|
||||
void Cpu::IncDec(int8_t offset)
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
uint8_t value = GetByteValue() + offset;
|
||||
SetZeroNegativeFlags(value);
|
||||
Idle();
|
||||
Write(_operand, value);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t value = GetWordValue() + offset;
|
||||
SetZeroNegativeFlags(value);
|
||||
Idle();
|
||||
|
@ -348,20 +392,29 @@ Compare instructions
|
|||
*********************/
|
||||
void Cpu::Compare(uint16_t reg, bool eightBitMode)
|
||||
{
|
||||
if(eightBitMode) {
|
||||
if (eightBitMode)
|
||||
{
|
||||
uint8_t value = GetByteValue();
|
||||
if((uint8_t)reg >= value) {
|
||||
if ((uint8_t)reg >= value)
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Carry);
|
||||
}
|
||||
uint8_t result = (uint8_t)reg - value;
|
||||
SetZeroNegativeFlags(result);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t value = GetWordValue();
|
||||
if(reg >= value) {
|
||||
if (reg >= value)
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Carry);
|
||||
}
|
||||
|
||||
|
@ -423,10 +476,13 @@ void Cpu::RTI()
|
|||
Idle();
|
||||
Idle();
|
||||
|
||||
if(_state.EmulationMode) {
|
||||
if (_state.EmulationMode)
|
||||
{
|
||||
SetPS(PopByte());
|
||||
_state.PC = PopWord();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPS(PopByte());
|
||||
_state.PC = PopWord();
|
||||
_state.K = PopByte();
|
||||
|
@ -461,13 +517,15 @@ Interrupts
|
|||
***********/
|
||||
void Cpu::ProcessInterrupt(uint16_t vector, bool forHardwareInterrupt)
|
||||
{
|
||||
if(forHardwareInterrupt) {
|
||||
if (forHardwareInterrupt)
|
||||
{
|
||||
//IRQ/NMI waste 2 cycles here. BRK/COP do not (because they do those 2 cycles while loading the OP code + signature byte)
|
||||
ReadCode(_state.PC);
|
||||
Idle();
|
||||
}
|
||||
|
||||
if(_state.EmulationMode) {
|
||||
if (_state.EmulationMode)
|
||||
{
|
||||
PushWord(_state.PC);
|
||||
PushByte(_state.PS | 0x20);
|
||||
|
||||
|
@ -476,7 +534,9 @@ void Cpu::ProcessInterrupt(uint16_t vector, bool forHardwareInterrupt)
|
|||
|
||||
_state.K = 0;
|
||||
_state.PC = ReadVector(vector);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
PushByte(_state.K);
|
||||
PushWord(_state.PC);
|
||||
PushByte(_state.PS);
|
||||
|
@ -504,27 +564,36 @@ Bitwise operations
|
|||
*******************/
|
||||
void Cpu::AND()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
SetRegister(_state.A, _state.A & GetByteValue(), true);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRegister(_state.A, _state.A & GetWordValue(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::EOR()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
SetRegister(_state.A, _state.A ^ GetByteValue(), true);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRegister(_state.A, _state.A ^ GetWordValue(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::ORA()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
SetRegister(_state.A, _state.A | GetByteValue(), true);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRegister(_state.A, _state.A | GetWordValue(), false);
|
||||
}
|
||||
}
|
||||
|
@ -532,48 +601,64 @@ void Cpu::ORA()
|
|||
/****************
|
||||
Shift operations
|
||||
*****************/
|
||||
template<typename T> T Cpu::ShiftLeft(T value)
|
||||
template <typename T>
|
||||
T Cpu::ShiftLeft(T value)
|
||||
{
|
||||
T result = value << 1;
|
||||
if(value & (1 << (sizeof(T) * 8 - 1))) {
|
||||
if (value & (1 << (sizeof(T) * 8 - 1)))
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Carry);
|
||||
}
|
||||
SetZeroNegativeFlags(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T> T Cpu::RollLeft(T value)
|
||||
template <typename T>
|
||||
T Cpu::RollLeft(T value)
|
||||
{
|
||||
T result = value << 1 | (_state.PS & ProcFlags::Carry);
|
||||
if(value & (1 << (sizeof(T) * 8 - 1))) {
|
||||
if (value & (1 << (sizeof(T) * 8 - 1)))
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Carry);
|
||||
}
|
||||
SetZeroNegativeFlags(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T> T Cpu::ShiftRight(T value)
|
||||
template <typename T>
|
||||
T Cpu::ShiftRight(T value)
|
||||
{
|
||||
T result = value >> 1;
|
||||
if(value & 0x01) {
|
||||
if (value & 0x01)
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Carry);
|
||||
}
|
||||
SetZeroNegativeFlags(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T> T Cpu::RollRight(T value)
|
||||
template <typename T>
|
||||
T Cpu::RollRight(T value)
|
||||
{
|
||||
T result = value >> 1 | ((_state.PS & 0x01) << (sizeof(T) * 8 - 1));
|
||||
if(value & 0x01) {
|
||||
if (value & 0x01)
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Carry);
|
||||
}
|
||||
SetZeroNegativeFlags(result);
|
||||
|
@ -582,20 +667,26 @@ template<typename T> T Cpu::RollRight(T value)
|
|||
|
||||
void Cpu::ASL_Acc()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
_state.A = (_state.A & 0xFF00) | (ShiftLeft<uint8_t>((uint8_t)_state.A));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_state.A = ShiftLeft<uint16_t>(_state.A);
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::ASL()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
uint8_t value = GetByteValue();
|
||||
Idle();
|
||||
Write(_operand, ShiftLeft<uint8_t>(value));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t value = GetWordValue();
|
||||
Idle();
|
||||
WriteWord(_operand, ShiftLeft<uint16_t>(value));
|
||||
|
@ -604,20 +695,26 @@ void Cpu::ASL()
|
|||
|
||||
void Cpu::LSR_Acc()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
_state.A = (_state.A & 0xFF00) | ShiftRight<uint8_t>((uint8_t)_state.A);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_state.A = ShiftRight<uint16_t>(_state.A);
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::LSR()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
uint8_t value = GetByteValue();
|
||||
Idle();
|
||||
Write(_operand, ShiftRight<uint8_t>(value));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t value = GetWordValue();
|
||||
Idle();
|
||||
WriteWord(_operand, ShiftRight<uint16_t>(value));
|
||||
|
@ -626,20 +723,26 @@ void Cpu::LSR()
|
|||
|
||||
void Cpu::ROL_Acc()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
_state.A = (_state.A & 0xFF00) | RollLeft<uint8_t>((uint8_t)_state.A);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_state.A = RollLeft<uint16_t>(_state.A);
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::ROL()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
uint8_t value = GetByteValue();
|
||||
Idle();
|
||||
Write(_operand, RollLeft<uint8_t>(value));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t value = GetWordValue();
|
||||
Idle();
|
||||
WriteWord(_operand, RollLeft<uint16_t>(value));
|
||||
|
@ -648,20 +751,26 @@ void Cpu::ROL()
|
|||
|
||||
void Cpu::ROR_Acc()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
_state.A = (_state.A & 0xFF00) | RollRight<uint8_t>((uint8_t)_state.A);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_state.A = RollRight<uint16_t>(_state.A);
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::ROR()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
uint8_t value = GetByteValue();
|
||||
Idle();
|
||||
Write(_operand, RollRight<uint8_t>(value));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t value = GetWordValue();
|
||||
Idle();
|
||||
WriteWord(_operand, RollRight<uint16_t>(value));
|
||||
|
@ -685,14 +794,16 @@ void Cpu::MVN()
|
|||
|
||||
_state.X++;
|
||||
_state.Y++;
|
||||
if(CheckFlag(ProcFlags::IndexMode8)) {
|
||||
if (CheckFlag(ProcFlags::IndexMode8))
|
||||
{
|
||||
_state.X &= 0xFF;
|
||||
_state.Y &= 0xFF;
|
||||
}
|
||||
|
||||
_state.A--;
|
||||
|
||||
if(_state.A != 0xFFFF) {
|
||||
if (_state.A != 0xFFFF)
|
||||
{
|
||||
//Operation isn't done, set the PC back to the start of the instruction
|
||||
_state.PC -= 3;
|
||||
}
|
||||
|
@ -712,14 +823,16 @@ void Cpu::MVP()
|
|||
|
||||
_state.X--;
|
||||
_state.Y--;
|
||||
if(CheckFlag(ProcFlags::IndexMode8)) {
|
||||
if (CheckFlag(ProcFlags::IndexMode8))
|
||||
{
|
||||
_state.X &= 0xFF;
|
||||
_state.Y &= 0xFF;
|
||||
}
|
||||
|
||||
_state.A--;
|
||||
|
||||
if(_state.A != 0xFFFF) {
|
||||
|
||||
if (_state.A != 0xFFFF)
|
||||
{
|
||||
//Operation isn't done, set the PC back to the start of the instruction
|
||||
_state.PC -= 3;
|
||||
}
|
||||
|
@ -793,9 +906,12 @@ void Cpu::PLP()
|
|||
//"For PLP, (all of) the flags are pulled from the stack. Note that when the e flag is 1, the m and x flag are forced to 1, so after the PLP, both flags will still be 1 no matter what value is pulled from the stack."
|
||||
Idle();
|
||||
Idle();
|
||||
if(_state.EmulationMode) {
|
||||
if (_state.EmulationMode)
|
||||
{
|
||||
SetPS(PopByte() | ProcFlags::MemoryMode8 | ProcFlags::IndexMode8);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPS(PopByte());
|
||||
}
|
||||
}
|
||||
|
@ -844,19 +960,25 @@ void Cpu::PLY()
|
|||
void Cpu::PushRegister(uint16_t reg, bool eightBitMode)
|
||||
{
|
||||
//"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value."
|
||||
if(eightBitMode) {
|
||||
if (eightBitMode)
|
||||
{
|
||||
PushByte((uint8_t)reg);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
PushWord(reg);
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::PullRegister(uint16_t ®, bool eightBitMode)
|
||||
void Cpu::PullRegister(uint16_t& reg, bool eightBitMode)
|
||||
{
|
||||
//"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value."
|
||||
if(eightBitMode) {
|
||||
if (eightBitMode)
|
||||
{
|
||||
SetRegister(reg, PopByte(), true);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRegister(reg, PopWord(), false);
|
||||
}
|
||||
}
|
||||
|
@ -864,20 +986,26 @@ void Cpu::PullRegister(uint16_t ®, bool eightBitMode)
|
|||
/*********************
|
||||
Store/load operations
|
||||
**********************/
|
||||
void Cpu::LoadRegister(uint16_t ®, bool eightBitMode)
|
||||
void Cpu::LoadRegister(uint16_t& reg, bool eightBitMode)
|
||||
{
|
||||
if(eightBitMode) {
|
||||
if (eightBitMode)
|
||||
{
|
||||
SetRegister(reg, GetByteValue(), true);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRegister(reg, GetWordValue(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::StoreRegister(uint16_t val, bool eightBitMode)
|
||||
{
|
||||
if(eightBitMode) {
|
||||
if (eightBitMode)
|
||||
{
|
||||
Write(_operand, (uint8_t)val);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteWord(_operand, val);
|
||||
}
|
||||
}
|
||||
|
@ -927,26 +1055,36 @@ void Cpu::STZ()
|
|||
/*******************
|
||||
Bit test operations
|
||||
********************/
|
||||
template<typename T> void Cpu::TestBits(T value, bool alterZeroFlagOnly)
|
||||
template <typename T>
|
||||
void Cpu::TestBits(T value, bool alterZeroFlagOnly)
|
||||
{
|
||||
if(alterZeroFlagOnly) {
|
||||
if (alterZeroFlagOnly)
|
||||
{
|
||||
//"Immediate addressing only affects the z flag (with the result of the bitwise And), but does not affect the n and v flags."
|
||||
if(((T)_state.A & value) == 0) {
|
||||
if (((T)_state.A & value) == 0)
|
||||
{
|
||||
SetFlags(ProcFlags::Zero);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Zero);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Zero | ProcFlags::Overflow | ProcFlags::Negative);
|
||||
|
||||
if(((T)_state.A & value) == 0) {
|
||||
if (((T)_state.A & value) == 0)
|
||||
{
|
||||
SetFlags(ProcFlags::Zero);
|
||||
}
|
||||
|
||||
if(value & (1 << (sizeof(T) * 8 - 2))) {
|
||||
if (value & (1 << (sizeof(T) * 8 - 2)))
|
||||
{
|
||||
SetFlags(ProcFlags::Overflow);
|
||||
}
|
||||
if(value & (1 << (sizeof(T) * 8 - 1))) {
|
||||
if (value & (1 << (sizeof(T) * 8 - 1)))
|
||||
{
|
||||
SetFlags(ProcFlags::Negative);
|
||||
}
|
||||
}
|
||||
|
@ -954,16 +1092,20 @@ template<typename T> void Cpu::TestBits(T value, bool alterZeroFlagOnly)
|
|||
|
||||
void Cpu::BIT()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
TestBits<uint8_t>(GetByteValue(), _immediateMode);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
TestBits<uint16_t>(GetWordValue(), _immediateMode);
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::TRB()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
uint8_t value = GetByteValue();
|
||||
TestBits<uint8_t>(value, true);
|
||||
|
||||
|
@ -971,7 +1113,9 @@ void Cpu::TRB()
|
|||
Idle();
|
||||
|
||||
Write(_operand, value);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t value = GetWordValue();
|
||||
TestBits<uint16_t>(value, true);
|
||||
|
||||
|
@ -984,7 +1128,8 @@ void Cpu::TRB()
|
|||
|
||||
void Cpu::TSB()
|
||||
{
|
||||
if(CheckFlag(ProcFlags::MemoryMode8)) {
|
||||
if (CheckFlag(ProcFlags::MemoryMode8))
|
||||
{
|
||||
uint8_t value = GetByteValue();
|
||||
TestBits<uint8_t>(value, true);
|
||||
|
||||
|
@ -992,7 +1137,9 @@ void Cpu::TSB()
|
|||
Idle();
|
||||
|
||||
Write(_operand, value);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t value = GetWordValue();
|
||||
TestBits<uint16_t>(value, true);
|
||||
|
||||
|
@ -1076,14 +1223,18 @@ void Cpu::XBA()
|
|||
void Cpu::XCE()
|
||||
{
|
||||
bool carry = CheckFlag(ProcFlags::Carry);
|
||||
if(_state.EmulationMode) {
|
||||
if (_state.EmulationMode)
|
||||
{
|
||||
SetFlags(ProcFlags::Carry);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlags(ProcFlags::Carry);
|
||||
}
|
||||
_state.EmulationMode = carry;
|
||||
|
||||
if(_state.EmulationMode) {
|
||||
if (_state.EmulationMode)
|
||||
{
|
||||
SetPS(_state.PS | ProcFlags::IndexMode8 | ProcFlags::MemoryMode8);
|
||||
_state.SP = 0x100 | (_state.SP & 0xFF);
|
||||
}
|
||||
|
@ -1129,7 +1280,8 @@ void Cpu::AddrMode_AbsIdxX(bool isWrite)
|
|||
{
|
||||
uint32_t baseAddr = GetDataAddress(ReadOperandWord());
|
||||
_operand = (baseAddr + _state.X) & 0xFFFFFF;
|
||||
if(isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00)) {
|
||||
if (isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00))
|
||||
{
|
||||
Idle();
|
||||
}
|
||||
}
|
||||
|
@ -1138,7 +1290,8 @@ void Cpu::AddrMode_AbsIdxY(bool isWrite)
|
|||
{
|
||||
uint32_t baseAddr = GetDataAddress(ReadOperandWord());
|
||||
_operand = (baseAddr + _state.Y) & 0xFFFFFF;
|
||||
if(isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00)) {
|
||||
if (isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00))
|
||||
{
|
||||
Idle();
|
||||
}
|
||||
}
|
||||
|
@ -1192,7 +1345,8 @@ void Cpu::AddrMode_BlkMov()
|
|||
uint8_t Cpu::ReadDirectOperandByte()
|
||||
{
|
||||
uint8_t value = ReadOperandByte();
|
||||
if(_state.D & 0xFF) {
|
||||
if (_state.D & 0xFF)
|
||||
{
|
||||
//Add 1 cycle for direct register low (DL) not equal 0
|
||||
Idle();
|
||||
}
|
||||
|
@ -1232,8 +1386,9 @@ void Cpu::AddrMode_DirIndIdxY(bool isWrite)
|
|||
{
|
||||
uint32_t baseAddr = GetDataAddress(GetDirectAddressIndirectWord(ReadDirectOperandByte()));
|
||||
_operand = (baseAddr + _state.Y) & 0xFFFFFF;
|
||||
|
||||
if(isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00)) {
|
||||
|
||||
if (isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00))
|
||||
{
|
||||
Idle();
|
||||
}
|
||||
}
|
||||
|
@ -1268,7 +1423,7 @@ void Cpu::AddrMode_ImmX()
|
|||
|
||||
void Cpu::AddrMode_ImmM()
|
||||
{
|
||||
_immediateMode = true;
|
||||
_immediateMode = true;
|
||||
_operand = CheckFlag(ProcFlags::MemoryMode8) ? ReadOperandByte() : ReadOperandWord();
|
||||
}
|
||||
|
||||
|
|
1090
Core/Cpu.Shared.h
1090
Core/Cpu.Shared.h
File diff suppressed because it is too large
Load diff
69
Core/Cpu.cpp
69
Core/Cpu.cpp
|
@ -26,24 +26,27 @@ void Cpu::Exec()
|
|||
{
|
||||
_immediateMode = false;
|
||||
|
||||
switch(_state.StopState) {
|
||||
case CpuStopState::Running: RunOp(); break;
|
||||
case CpuStopState::Stopped:
|
||||
//STP was executed, CPU no longer executes any code
|
||||
#ifndef DUMMYCPU
|
||||
switch (_state.StopState)
|
||||
{
|
||||
case CpuStopState::Running: RunOp();
|
||||
break;
|
||||
case CpuStopState::Stopped:
|
||||
//STP was executed, CPU no longer executes any code
|
||||
#ifndef DUMMYCPU
|
||||
_memoryManager->IncMasterClock4();
|
||||
#endif
|
||||
return;
|
||||
#endif
|
||||
return;
|
||||
|
||||
case CpuStopState::WaitingForIrq:
|
||||
//WAI
|
||||
case CpuStopState::WaitingForIrq:
|
||||
//WAI
|
||||
Idle();
|
||||
if (_state.IrqSource || _state.NeedNmi)
|
||||
{
|
||||
Idle();
|
||||
if(_state.IrqSource || _state.NeedNmi) {
|
||||
Idle();
|
||||
Idle();
|
||||
_state.StopState = CpuStopState::Running;
|
||||
}
|
||||
break;
|
||||
Idle();
|
||||
_state.StopState = CpuStopState::Running;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef DUMMYCPU
|
||||
|
@ -120,21 +123,33 @@ void Cpu::Write(uint32_t addr, uint8_t value, MemoryOperationType type)
|
|||
|
||||
void Cpu::SetReg(CpuRegister reg, uint16_t value)
|
||||
{
|
||||
switch (reg) {
|
||||
case CpuRegister::CpuRegA: { _state.A = value; } break;
|
||||
case CpuRegister::CpuRegX: { _state.X = value; } break;
|
||||
case CpuRegister::CpuRegY: { _state.Y = value; } break;
|
||||
case CpuRegister::CpuRegSP: { _state.SP = value; } break;
|
||||
case CpuRegister::CpuRegD: { _state.D = value; } break;
|
||||
case CpuRegister::CpuRegPC: { _state.PC = value; } break;
|
||||
case CpuRegister::CpuRegK: { _state.K = value & 0xFF; } break;
|
||||
case CpuRegister::CpuRegDBR: { _state.DBR = value & 0xFF; } break;
|
||||
case CpuRegister::CpuRegPS: { _state.PS = value & 0xFF; } break;
|
||||
case CpuRegister::CpuFlagNmi: { _state.NmiFlag = value != 0; } break;
|
||||
switch (reg)
|
||||
{
|
||||
case CpuRegister::CpuRegA: { _state.A = value; }
|
||||
break;
|
||||
case CpuRegister::CpuRegX: { _state.X = value; }
|
||||
break;
|
||||
case CpuRegister::CpuRegY: { _state.Y = value; }
|
||||
break;
|
||||
case CpuRegister::CpuRegSP: { _state.SP = value; }
|
||||
break;
|
||||
case CpuRegister::CpuRegD: { _state.D = value; }
|
||||
break;
|
||||
case CpuRegister::CpuRegPC: { _state.PC = value; }
|
||||
break;
|
||||
case CpuRegister::CpuRegK: { _state.K = value & 0xFF; }
|
||||
break;
|
||||
case CpuRegister::CpuRegDBR: { _state.DBR = value & 0xFF; }
|
||||
break;
|
||||
case CpuRegister::CpuRegPS: { _state.PS = value & 0xFF; }
|
||||
break;
|
||||
case CpuRegister::CpuFlagNmi: { _state.NmiFlag = value != 0; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cpu::GetCpuProcFlag(ProcFlags::ProcFlags flag) {
|
||||
bool Cpu::GetCpuProcFlag(ProcFlags::ProcFlags flag)
|
||||
{
|
||||
return _state.PS & static_cast<uint8_t>(flag);
|
||||
}
|
||||
|
||||
|
|
65
Core/Cpu.h
65
Core/Cpu.h
|
@ -29,11 +29,11 @@ private:
|
|||
static constexpr uint32_t LegacyIrqVector = 0xFFFE;
|
||||
static constexpr uint32_t LegacyCoprocessorVector = 0x00FFF4;
|
||||
|
||||
typedef void(Cpu::*Func)();
|
||||
|
||||
MemoryManager *_memoryManager = nullptr;
|
||||
DmaController *_dmaController = nullptr;
|
||||
Console *_console = nullptr;
|
||||
typedef void (Cpu::*Func)();
|
||||
|
||||
MemoryManager* _memoryManager = nullptr;
|
||||
DmaController* _dmaController = nullptr;
|
||||
Console* _console = nullptr;
|
||||
|
||||
bool _immediateMode = false;
|
||||
|
||||
|
@ -47,9 +47,9 @@ private:
|
|||
|
||||
uint16_t GetDirectAddressIndirectWord(uint16_t offset, bool allowEmulationMode = true);
|
||||
uint32_t GetDirectAddressIndirectLong(uint16_t offset, bool allowEmulationMode = true);
|
||||
|
||||
|
||||
uint8_t GetOpCode();
|
||||
|
||||
|
||||
uint16_t GetResetVector();
|
||||
|
||||
void UpdateIrqNmiFlags();
|
||||
|
@ -59,7 +59,7 @@ private:
|
|||
void IdleOrRead();
|
||||
void IdleEndJump();
|
||||
void IdleTakeBranch();
|
||||
|
||||
|
||||
uint8_t ReadOperandByte();
|
||||
uint16_t ReadOperandWord();
|
||||
uint32_t ReadOperandLong();
|
||||
|
@ -71,9 +71,9 @@ private:
|
|||
void SetSP(uint16_t sp);
|
||||
void SetPS(uint8_t ps);
|
||||
|
||||
void SetRegister(uint8_t ®, uint8_t value);
|
||||
void SetRegister(uint16_t ®, uint16_t value, bool eightBitMode);
|
||||
|
||||
void SetRegister(uint8_t& reg, uint8_t value);
|
||||
void SetRegister(uint16_t& reg, uint16_t value, bool eightBitMode);
|
||||
|
||||
void SetZeroNegativeFlags(uint16_t value);
|
||||
void SetZeroNegativeFlags(uint8_t value);
|
||||
|
||||
|
@ -109,7 +109,7 @@ private:
|
|||
void Sub8(uint8_t value);
|
||||
void Sub16(uint16_t value);
|
||||
void SBC();
|
||||
|
||||
|
||||
//Branch instructions
|
||||
void BCC();
|
||||
void BCS();
|
||||
|
@ -122,7 +122,7 @@ private:
|
|||
void BVC();
|
||||
void BVS();
|
||||
void BranchRelative(bool branch);
|
||||
|
||||
|
||||
//Set/clear flag instructions
|
||||
void CLC();
|
||||
void CLD();
|
||||
|
@ -146,7 +146,7 @@ private:
|
|||
void DEC_Acc();
|
||||
void INC_Acc();
|
||||
|
||||
void IncDecReg(uint16_t & reg, int8_t offset);
|
||||
void IncDecReg(uint16_t& reg, int8_t offset);
|
||||
void IncDec(int8_t offset);
|
||||
|
||||
//Compare instructions
|
||||
|
@ -174,10 +174,14 @@ private:
|
|||
void EOR();
|
||||
void ORA();
|
||||
|
||||
template<typename T> T ShiftLeft(T value);
|
||||
template<typename T> T RollLeft(T value);
|
||||
template<typename T> T ShiftRight(T value);
|
||||
template<typename T> T RollRight(T value);
|
||||
template <typename T>
|
||||
T ShiftLeft(T value);
|
||||
template <typename T>
|
||||
T RollLeft(T value);
|
||||
template <typename T>
|
||||
T ShiftRight(T value);
|
||||
template <typename T>
|
||||
T RollRight(T value);
|
||||
|
||||
//Shift operations
|
||||
void ASL_Acc();
|
||||
|
@ -213,10 +217,10 @@ private:
|
|||
void PLY();
|
||||
|
||||
void PushRegister(uint16_t reg, bool eightBitMode);
|
||||
void PullRegister(uint16_t ®, bool eightBitMode);
|
||||
void PullRegister(uint16_t& reg, bool eightBitMode);
|
||||
|
||||
//Store/load instructions
|
||||
void LoadRegister(uint16_t ®, bool eightBitMode);
|
||||
void LoadRegister(uint16_t& reg, bool eightBitMode);
|
||||
void StoreRegister(uint16_t val, bool eightBitMode);
|
||||
|
||||
void LDA();
|
||||
|
@ -227,9 +231,10 @@ private:
|
|||
void STX();
|
||||
void STY();
|
||||
void STZ();
|
||||
|
||||
|
||||
//Test bits
|
||||
template<typename T> void TestBits(T value, bool alterZeroFlagOnly);
|
||||
template <typename T>
|
||||
void TestBits(T value, bool alterZeroFlagOnly);
|
||||
void BIT();
|
||||
|
||||
void TRB();
|
||||
|
@ -282,7 +287,7 @@ private:
|
|||
void AddrMode_BlkMov();
|
||||
|
||||
uint8_t ReadDirectOperandByte();
|
||||
|
||||
|
||||
//Direct: d
|
||||
void AddrMode_Dir();
|
||||
//Direct Indexed: d,x
|
||||
|
@ -291,7 +296,7 @@ private:
|
|||
void AddrMode_DirIdxY();
|
||||
//Direct Indirect: (d)
|
||||
void AddrMode_DirInd();
|
||||
|
||||
|
||||
//Direct Indexed Indirect: (d,x)
|
||||
void AddrMode_DirIdxIndX();
|
||||
//Direct Indirect Indexed: (d),y
|
||||
|
@ -313,12 +318,12 @@ private:
|
|||
|
||||
void AddrMode_StkRel();
|
||||
void AddrMode_StkRelIndIdxY();
|
||||
|
||||
|
||||
void RunOp();
|
||||
|
||||
public:
|
||||
#ifndef DUMMYCPU
|
||||
Cpu(Console *console);
|
||||
Cpu(Console* console);
|
||||
#else
|
||||
DummyCpu(Console* console, CpuType type);
|
||||
#endif
|
||||
|
@ -334,7 +339,7 @@ public:
|
|||
bool GetCpuProcFlag(ProcFlags::ProcFlags flag);
|
||||
uint64_t GetCycleCount();
|
||||
|
||||
template<uint64_t value>
|
||||
template <uint64_t value>
|
||||
void IncreaseCycleCount();
|
||||
|
||||
void SetNmiFlag(bool nmiFlag);
|
||||
|
@ -345,7 +350,7 @@ public:
|
|||
void ClearIrqSource(IrqSource source);
|
||||
|
||||
// Inherited via ISerializable
|
||||
void Serialize(Serializer &s) override;
|
||||
void Serialize(Serializer& s) override;
|
||||
|
||||
void SetReg(CpuRegister reg, uint16_t value);
|
||||
void SetCpuProcFlag(ProcFlags::ProcFlags flag, bool set);
|
||||
|
@ -377,10 +382,10 @@ public:
|
|||
|
||||
void SetReg(CpuRegister reg, uint16_t value);
|
||||
|
||||
template<uint64_t count>
|
||||
template <uint64_t count>
|
||||
void Cpu::IncreaseCycleCount()
|
||||
{
|
||||
_state.CycleCount += count;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
class CpuBwRamHandler : public IMemoryHandler
|
||||
{
|
||||
private:
|
||||
IMemoryHandler * _handler;
|
||||
IMemoryHandler* _handler;
|
||||
Sa1State* _state;
|
||||
Sa1* _sa1;
|
||||
|
||||
|
@ -24,9 +24,12 @@ public:
|
|||
|
||||
uint8_t Read(uint32_t addr) override
|
||||
{
|
||||
if(_state->CharConvDmaActive) {
|
||||
if (_state->CharConvDmaActive)
|
||||
{
|
||||
return _sa1->ReadCharConvertType1(addr);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return _handler->Read(addr);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +39,7 @@ public:
|
|||
return Read(addr);
|
||||
}
|
||||
|
||||
void PeekBlock(uint32_t addr, uint8_t *output) override
|
||||
void PeekBlock(uint32_t addr, uint8_t* output) override
|
||||
{
|
||||
_handler->PeekBlock(addr, output);
|
||||
}
|
||||
|
@ -50,4 +53,4 @@ public:
|
|||
{
|
||||
return _handler->GetAbsoluteAddress(address);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -36,14 +36,16 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
|
|||
_codeDataLogger = debugger->GetCodeDataLogger(CpuType::Cpu).get();
|
||||
_settings = debugger->GetConsole()->GetSettings().get();
|
||||
_memoryManager = debugger->GetConsole()->GetMemoryManager().get();
|
||||
|
||||
_eventManager.reset(new EventManager(debugger, _cpu, _debugger->GetConsole()->GetPpu().get(), _memoryManager, _debugger->GetConsole()->GetDmaController().get()));
|
||||
|
||||
_eventManager.reset(new EventManager(debugger, _cpu, _debugger->GetConsole()->GetPpu().get(), _memoryManager,
|
||||
_debugger->GetConsole()->GetDmaController().get()));
|
||||
_callstackManager.reset(new CallstackManager(debugger));
|
||||
_breakpointManager.reset(new BreakpointManager(debugger, cpuType, _eventManager.get()));
|
||||
_step.reset(new StepRequest());
|
||||
_assembler.reset(new Assembler(_debugger->GetLabelManager()));
|
||||
|
||||
if(GetState().PC == 0) {
|
||||
if (GetState().PC == 0)
|
||||
{
|
||||
//Enable breaking on uninit reads when debugger is opened at power on
|
||||
_enableBreakOnUninitRead = true;
|
||||
}
|
||||
|
@ -59,26 +61,34 @@ void CpuDebugger::Reset()
|
|||
void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type)
|
||||
{
|
||||
AddressInfo addressInfo = GetMemoryMappings().GetAbsoluteAddress(addr);
|
||||
MemoryOperationInfo operation = { addr, value, type };
|
||||
MemoryOperationInfo operation = {addr, value, type};
|
||||
CpuState state = GetState();
|
||||
BreakSource breakSource = BreakSource::Unspecified;
|
||||
|
||||
if(type == MemoryOperationType::ExecOpCode) {
|
||||
bool needDisassemble = _traceLogger->IsCpuLogged(_cpuType) || _settings->CheckDebuggerFlag(_cpuType == CpuType::Cpu ? DebuggerFlags::CpuDebuggerEnabled : DebuggerFlags::Sa1DebuggerEnabled);
|
||||
if(addressInfo.Address >= 0) {
|
||||
if(addressInfo.Type == SnesMemoryType::PrgRom) {
|
||||
if (type == MemoryOperationType::ExecOpCode)
|
||||
{
|
||||
bool needDisassemble = _traceLogger->IsCpuLogged(_cpuType) || _settings->CheckDebuggerFlag(
|
||||
_cpuType == CpuType::Cpu ? DebuggerFlags::CpuDebuggerEnabled : DebuggerFlags::Sa1DebuggerEnabled);
|
||||
if (addressInfo.Address >= 0)
|
||||
{
|
||||
if (addressInfo.Type == SnesMemoryType::PrgRom)
|
||||
{
|
||||
uint8_t flags = CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8));
|
||||
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) {
|
||||
if (_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC)
|
||||
{
|
||||
flags |= CdlFlags::SubEntryPoint;
|
||||
}
|
||||
_codeDataLogger->SetFlags(addressInfo.Address, flags);
|
||||
}
|
||||
if(needDisassemble) {
|
||||
_disassembler->BuildCache(addressInfo, state.PS & (ProcFlags::IndexMode8 | ProcFlags::MemoryMode8), _cpuType);
|
||||
if (needDisassemble)
|
||||
{
|
||||
_disassembler->BuildCache(addressInfo, state.PS & (ProcFlags::IndexMode8 | ProcFlags::MemoryMode8),
|
||||
_cpuType);
|
||||
}
|
||||
}
|
||||
|
||||
if(_traceLogger->IsCpuLogged(_cpuType)) {
|
||||
if (_traceLogger->IsCpuLogged(_cpuType))
|
||||
{
|
||||
_debugger->GetState(_debugState, true);
|
||||
|
||||
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, _cpuType);
|
||||
|
@ -86,19 +96,25 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
|
|||
}
|
||||
|
||||
uint32_t pc = (state.K << 16) | state.PC;
|
||||
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) {
|
||||
if (_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC)
|
||||
{
|
||||
//JSR, JSL
|
||||
uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, state.PS, _cpuType);
|
||||
uint32_t returnPc = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + opSize) & 0xFFFF);
|
||||
AddressInfo srcAddress = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter);
|
||||
AddressInfo retAddress = GetMemoryMappings().GetAbsoluteAddress(returnPc);
|
||||
_callstackManager->Push(srcAddress, _prevProgramCounter, addressInfo, pc, retAddress, returnPc, StackFrameFlags::None);
|
||||
} else if(_prevOpCode == 0x60 || _prevOpCode == 0x6B || _prevOpCode == 0x40) {
|
||||
_callstackManager->Push(srcAddress, _prevProgramCounter, addressInfo, pc, retAddress, returnPc,
|
||||
StackFrameFlags::None);
|
||||
}
|
||||
else if (_prevOpCode == 0x60 || _prevOpCode == 0x6B || _prevOpCode == 0x40)
|
||||
{
|
||||
//RTS, RTL, RTI
|
||||
_callstackManager->Pop(addressInfo, pc);
|
||||
}
|
||||
|
||||
if(_step->BreakAddress == (int32_t)pc && (_prevOpCode == 0x60 || _prevOpCode == 0x40 || _prevOpCode == 0x6B || _prevOpCode == 0x44 || _prevOpCode == 0x54)) {
|
||||
if (_step->BreakAddress == (int32_t)pc && (_prevOpCode == 0x60 || _prevOpCode == 0x40 || _prevOpCode == 0x6B ||
|
||||
_prevOpCode == 0x44 || _prevOpCode == 0x54))
|
||||
{
|
||||
//RTS/RTL/RTI found, if we're on the expected return address, break immediately (for step over/step out)
|
||||
_step->StepCount = 0;
|
||||
}
|
||||
|
@ -106,47 +122,72 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
|
|||
_prevOpCode = value;
|
||||
_prevProgramCounter = pc;
|
||||
|
||||
if(_step->StepCount > 0) {
|
||||
if (_step->StepCount > 0)
|
||||
{
|
||||
_step->StepCount--;
|
||||
}
|
||||
|
||||
if(_settings->CheckDebuggerFlag(DebuggerFlags::CpuDebuggerEnabled)) {
|
||||
if(value == 0x00 || value == 0x02 || value == 0x42 || value == 0xDB) {
|
||||
if (_settings->CheckDebuggerFlag(DebuggerFlags::CpuDebuggerEnabled))
|
||||
{
|
||||
if (value == 0x00 || value == 0x02 || value == 0x42 || value == 0xDB)
|
||||
{
|
||||
//Break on BRK/STP/WDM/COP
|
||||
if(value == 0x00 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnBrk)) {
|
||||
if (value == 0x00 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnBrk))
|
||||
{
|
||||
breakSource = BreakSource::BreakOnBrk;
|
||||
_step->StepCount = 0;
|
||||
} else if(value == 0x02 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnCop)) {
|
||||
}
|
||||
else if (value == 0x02 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnCop))
|
||||
{
|
||||
breakSource = BreakSource::BreakOnCop;
|
||||
_step->StepCount = 0;
|
||||
} else if(value == 0x42 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnWdm)) {
|
||||
}
|
||||
else if (value == 0x42 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnWdm))
|
||||
{
|
||||
breakSource = BreakSource::BreakOnWdm;
|
||||
_step->StepCount = 0;
|
||||
} else if(value == 0xDB && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnStp)) {
|
||||
}
|
||||
else if (value == 0xDB && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnStp))
|
||||
{
|
||||
breakSource = BreakSource::BreakOnStp;
|
||||
_step->StepCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
|
||||
} else if(type == MemoryOperationType::ExecOperand) {
|
||||
if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) {
|
||||
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
|
||||
}
|
||||
else if (type == MemoryOperationType::ExecOperand)
|
||||
{
|
||||
if (addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0)
|
||||
{
|
||||
_codeDataLogger->SetFlags(addressInfo.Address,
|
||||
CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
|
||||
}
|
||||
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
|
||||
} else {
|
||||
if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) {
|
||||
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0)
|
||||
{
|
||||
_codeDataLogger->SetFlags(addressInfo.Address,
|
||||
CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
|
||||
}
|
||||
|
||||
if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock())) {
|
||||
if (_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock()))
|
||||
{
|
||||
//Memory access was a read on an uninitialized memory address
|
||||
if(_enableBreakOnUninitRead) {
|
||||
if(_memoryAccessCounter->GetReadCount(addressInfo) == 1) {
|
||||
if (_enableBreakOnUninitRead)
|
||||
{
|
||||
if (_memoryAccessCounter->GetReadCount(addressInfo) == 1)
|
||||
{
|
||||
//Only warn the first time
|
||||
_debugger->Log(string(_cpuType == CpuType::Sa1 ? "[SA1]" : "[CPU]") + " Uninitialized memory read: $" + HexUtilities::ToHex24(addr));
|
||||
_debugger->Log(
|
||||
string(_cpuType == CpuType::Sa1 ? "[SA1]" : "[CPU]") + " Uninitialized memory read: $" +
|
||||
HexUtilities::ToHex24(addr));
|
||||
}
|
||||
if(_settings->CheckDebuggerFlag(DebuggerFlags::CpuDebuggerEnabled) && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnUninitRead)) {
|
||||
if (_settings->CheckDebuggerFlag(DebuggerFlags::CpuDebuggerEnabled) && _settings->CheckDebuggerFlag(
|
||||
DebuggerFlags::BreakOnUninitRead))
|
||||
{
|
||||
breakSource = BreakSource::BreakOnUninitMemoryRead;
|
||||
_step->StepCount = 0;
|
||||
}
|
||||
|
@ -154,22 +195,27 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
|
|||
}
|
||||
}
|
||||
|
||||
if(IsRegister(addr)) {
|
||||
if (IsRegister(addr))
|
||||
{
|
||||
_eventManager->AddEvent(DebugEventType::Register, operation);
|
||||
}
|
||||
|
||||
_debugger->ProcessBreakConditions(_step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource);
|
||||
_debugger->ProcessBreakConditions(_step->StepCount == 0, _breakpointManager.get(), operation, addressInfo,
|
||||
breakSource);
|
||||
}
|
||||
|
||||
void CpuDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
|
||||
{
|
||||
AddressInfo addressInfo = GetMemoryMappings().GetAbsoluteAddress(addr);
|
||||
MemoryOperationInfo operation = { addr, value, type };
|
||||
if(addressInfo.Address >= 0 && (addressInfo.Type == SnesMemoryType::WorkRam || addressInfo.Type == SnesMemoryType::SaveRam)) {
|
||||
MemoryOperationInfo operation = {addr, value, type};
|
||||
if (addressInfo.Address >= 0 && (addressInfo.Type == SnesMemoryType::WorkRam || addressInfo.Type ==
|
||||
SnesMemoryType::SaveRam))
|
||||
{
|
||||
_disassembler->InvalidateCache(addressInfo, _cpuType);
|
||||
}
|
||||
|
||||
if(IsRegister(addr)) {
|
||||
if (IsRegister(addr))
|
||||
{
|
||||
_eventManager->AddEvent(DebugEventType::Register, operation);
|
||||
}
|
||||
|
||||
|
@ -186,25 +232,39 @@ void CpuDebugger::Run()
|
|||
void CpuDebugger::Step(int32_t stepCount, StepType type)
|
||||
{
|
||||
StepRequest step;
|
||||
if((type == StepType::StepOver || type == StepType::StepOut || type == StepType::Step) && GetState().StopState == CpuStopState::Stopped) {
|
||||
if ((type == StepType::StepOver || type == StepType::StepOut || type == StepType::Step) && GetState().StopState ==
|
||||
CpuStopState::Stopped)
|
||||
{
|
||||
//If STP was called, the CPU isn't running anymore - use the PPU to break execution instead (useful for test roms that end with STP)
|
||||
step.PpuStepCount = 1;
|
||||
} else {
|
||||
switch(type) {
|
||||
case StepType::Step: step.StepCount = stepCount; break;
|
||||
case StepType::StepOut: step.BreakAddress = _callstackManager->GetReturnAddress(); break;
|
||||
case StepType::StepOver:
|
||||
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC || _prevOpCode == 0x00 || _prevOpCode == 0x02 || _prevOpCode == 0x44 || _prevOpCode == 0x54) {
|
||||
//JSR, JSL, BRK, COP, MVP, MVN
|
||||
step.BreakAddress = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + DisassemblyInfo::GetOpSize(_prevOpCode, 0, _cpuType)) & 0xFFFF);
|
||||
} else {
|
||||
//For any other instruction, step over is the same as step into
|
||||
step.StepCount = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case StepType::Step: step.StepCount = stepCount;
|
||||
break;
|
||||
case StepType::StepOut: step.BreakAddress = _callstackManager->GetReturnAddress();
|
||||
break;
|
||||
case StepType::StepOver:
|
||||
if (_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC || _prevOpCode == 0x00 || _prevOpCode ==
|
||||
0x02 || _prevOpCode == 0x44 || _prevOpCode == 0x54)
|
||||
{
|
||||
//JSR, JSL, BRK, COP, MVP, MVN
|
||||
step.BreakAddress = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) +
|
||||
DisassemblyInfo::GetOpSize(_prevOpCode, 0, _cpuType)) & 0xFFFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
//For any other instruction, step over is the same as step into
|
||||
step.StepCount = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case StepType::PpuStep: step.PpuStepCount = stepCount; break;
|
||||
case StepType::SpecificScanline: step.BreakScanline = stepCount; break;
|
||||
case StepType::PpuStep: step.PpuStepCount = stepCount;
|
||||
break;
|
||||
case StepType::SpecificScanline: step.BreakScanline = stepCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_step.reset(new StepRequest(step));
|
||||
|
@ -215,20 +275,24 @@ void CpuDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool
|
|||
AddressInfo src = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter);
|
||||
AddressInfo ret = GetMemoryMappings().GetAbsoluteAddress(originalPc);
|
||||
AddressInfo dest = GetMemoryMappings().GetAbsoluteAddress(currentPc);
|
||||
_callstackManager->Push(src, _prevProgramCounter, dest, currentPc, ret, originalPc, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq);
|
||||
_callstackManager->Push(src, _prevProgramCounter, dest, currentPc, ret, originalPc,
|
||||
forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq);
|
||||
_eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq);
|
||||
}
|
||||
|
||||
void CpuDebugger::ProcessPpuCycle(uint16_t scanline, uint16_t cycle)
|
||||
{
|
||||
if(_step->PpuStepCount > 0) {
|
||||
if (_step->PpuStepCount > 0)
|
||||
{
|
||||
_step->PpuStepCount--;
|
||||
if(_step->PpuStepCount == 0) {
|
||||
if (_step->PpuStepCount == 0)
|
||||
{
|
||||
_debugger->SleepUntilResume(BreakSource::PpuStep);
|
||||
}
|
||||
}
|
||||
|
||||
if(cycle == 0 && scanline == _step->BreakScanline) {
|
||||
if (cycle == 0 && scanline == _step->BreakScanline)
|
||||
{
|
||||
_step->BreakScanline = -1;
|
||||
_debugger->SleepUntilResume(BreakSource::PpuStep);
|
||||
}
|
||||
|
@ -236,18 +300,24 @@ void CpuDebugger::ProcessPpuCycle(uint16_t scanline, uint16_t cycle)
|
|||
|
||||
MemoryMappings& CpuDebugger::GetMemoryMappings()
|
||||
{
|
||||
if(_cpuType == CpuType::Cpu) {
|
||||
if (_cpuType == CpuType::Cpu)
|
||||
{
|
||||
return *_memoryManager->GetMemoryMappings();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return *_sa1->GetMemoryMappings();
|
||||
}
|
||||
}
|
||||
|
||||
CpuState CpuDebugger::GetState()
|
||||
{
|
||||
if(_cpuType == CpuType::Cpu) {
|
||||
if (_cpuType == CpuType::Cpu)
|
||||
{
|
||||
return _cpu->GetState();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return _sa1->GetCpuState();
|
||||
}
|
||||
}
|
||||
|
@ -275,4 +345,4 @@ shared_ptr<CallstackManager> CpuDebugger::GetCallstackManager()
|
|||
BreakpointManager* CpuDebugger::GetBreakpointManager()
|
||||
{
|
||||
return _breakpointManager.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,4 +62,4 @@ public:
|
|||
shared_ptr<Assembler> GetAssembler();
|
||||
shared_ptr<CallstackManager> GetCallstackManager();
|
||||
BreakpointManager* GetBreakpointManager();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
#include "../Utilities/HexUtilities.h"
|
||||
#include "../Utilities/FastString.h"
|
||||
|
||||
void CpuDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings)
|
||||
void CpuDisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager,
|
||||
EmuSettings* settings)
|
||||
{
|
||||
FastString str(settings->CheckDebuggerFlag(DebuggerFlags::UseLowerCaseDisassembly));
|
||||
|
||||
|
@ -23,80 +24,127 @@ void CpuDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t me
|
|||
uint32_t opSize = info.GetOpSize();
|
||||
|
||||
FastString operand(settings->CheckDebuggerFlag(DebuggerFlags::UseLowerCaseDisassembly));
|
||||
if(opSize > 1) {
|
||||
if(addrMode == AddrMode::Rel || addrMode == AddrMode::RelLng || opSize == 4) {
|
||||
AddressInfo address { (int32_t)opAddr, SnesMemoryType::CpuMemory };
|
||||
if (opSize > 1)
|
||||
{
|
||||
if (addrMode == AddrMode::Rel || addrMode == AddrMode::RelLng || opSize == 4)
|
||||
{
|
||||
AddressInfo address{(int32_t)opAddr, SnesMemoryType::CpuMemory};
|
||||
string label = labelManager ? labelManager->GetLabel(address) : "";
|
||||
if(label.size()) {
|
||||
if (label.size())
|
||||
{
|
||||
operand.Write(label, true);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
operand.WriteAll('$', HexUtilities::ToHex24(opAddr));
|
||||
}
|
||||
} else if(opSize == 2) {
|
||||
}
|
||||
else if (opSize == 2)
|
||||
{
|
||||
operand.WriteAll('$', HexUtilities::ToHex((uint8_t)opAddr));
|
||||
} else if(opSize == 3) {
|
||||
}
|
||||
else if (opSize == 3)
|
||||
{
|
||||
operand.WriteAll('$', HexUtilities::ToHex((uint16_t)opAddr));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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 AddrMode::Imm8: case AddrMode::Imm16: case AddrMode::ImmX: case AddrMode::ImmM:
|
||||
str.WriteAll('#', operand);
|
||||
break;
|
||||
case AddrMode::Imm8:
|
||||
case AddrMode::Imm16:
|
||||
case AddrMode::ImmX:
|
||||
case AddrMode::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 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;
|
||||
|
||||
default: throw std::runtime_error("invalid address mode");
|
||||
default: throw std::runtime_error("invalid address mode");
|
||||
}
|
||||
|
||||
out += str.ToString();
|
||||
}
|
||||
|
||||
uint32_t CpuDisUtils::GetOperandAddress(DisassemblyInfo &info, uint32_t memoryAddr)
|
||||
uint32_t CpuDisUtils::GetOperandAddress(DisassemblyInfo& info, uint32_t memoryAddr)
|
||||
{
|
||||
uint32_t opSize = info.GetOpSize();
|
||||
uint32_t opAddr = 0;
|
||||
uint8_t* byteCode = info.GetByteCode();
|
||||
if(opSize == 2) {
|
||||
if (opSize == 2)
|
||||
{
|
||||
opAddr = byteCode[1];
|
||||
} else if(opSize == 3) {
|
||||
}
|
||||
else if (opSize == 3)
|
||||
{
|
||||
opAddr = byteCode[1] | (byteCode[2] << 8);
|
||||
} else if(opSize == 4) {
|
||||
}
|
||||
else if (opSize == 4)
|
||||
{
|
||||
opAddr = byteCode[1] | (byteCode[2] << 8) | (byteCode[3] << 16);
|
||||
}
|
||||
|
||||
AddrMode addrMode = CpuDisUtils::OpMode[byteCode[0]];
|
||||
if(addrMode == AddrMode::Rel || addrMode == AddrMode::RelLng) {
|
||||
if(opSize == 2) {
|
||||
if (addrMode == AddrMode::Rel || addrMode == AddrMode::RelLng)
|
||||
{
|
||||
if (opSize == 2)
|
||||
{
|
||||
opAddr = (memoryAddr & 0xFF0000) | (((int8_t)opAddr + memoryAddr + 2) & 0xFFFF);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
opAddr = (memoryAddr & 0xFF0000) | (((int16_t)opAddr + memoryAddr + 3) & 0xFFFF);
|
||||
}
|
||||
}
|
||||
|
@ -104,9 +152,10 @@ uint32_t CpuDisUtils::GetOperandAddress(DisassemblyInfo &info, uint32_t memoryAd
|
|||
return opAddr;
|
||||
}
|
||||
|
||||
int32_t CpuDisUtils::GetEffectiveAddress(DisassemblyInfo &info, Console *console, CpuState &state, CpuType type)
|
||||
int32_t CpuDisUtils::GetEffectiveAddress(DisassemblyInfo& info, Console* console, CpuState& state, CpuType type)
|
||||
{
|
||||
if(HasEffectiveAddress(CpuDisUtils::OpMode[info.GetOpCode()])) {
|
||||
if (HasEffectiveAddress(CpuDisUtils::OpMode[info.GetOpCode()]))
|
||||
{
|
||||
DummyCpu cpu(console, type);
|
||||
state.PS &= ~(ProcFlags::IndexMode8 | ProcFlags::MemoryMode8);
|
||||
state.PS |= info.GetFlags();
|
||||
|
@ -119,41 +168,42 @@ int32_t CpuDisUtils::GetEffectiveAddress(DisassemblyInfo &info, Console *console
|
|||
|
||||
bool CpuDisUtils::HasEffectiveAddress(AddrMode 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:
|
||||
return false;
|
||||
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:
|
||||
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:
|
||||
return true;
|
||||
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:
|
||||
return true;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Invalid mode");
|
||||
|
@ -161,9 +211,12 @@ bool CpuDisUtils::HasEffectiveAddress(AddrMode addrMode)
|
|||
|
||||
uint8_t CpuDisUtils::GetOpSize(AddrMode addrMode, uint8_t flags)
|
||||
{
|
||||
if(addrMode == AddrMode::ImmX) {
|
||||
if (addrMode == AddrMode::ImmX)
|
||||
{
|
||||
return (flags & ProcFlags::IndexMode8) ? 2 : 3;
|
||||
} else if(addrMode == AddrMode::ImmM) {
|
||||
}
|
||||
else if (addrMode == AddrMode::ImmM)
|
||||
{
|
||||
return (flags & ProcFlags::MemoryMode8) ? 2 : 3;
|
||||
}
|
||||
|
||||
|
@ -199,26 +252,42 @@ string CpuDisUtils::OpName[256] = {
|
|||
"CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", "INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP", // C
|
||||
"BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", "CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP", // D
|
||||
"CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", "INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC", // E
|
||||
"BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC" // F
|
||||
"BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC" // F
|
||||
};
|
||||
|
||||
typedef AddrMode M;
|
||||
AddrMode CpuDisUtils::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::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
|
||||
M::Stk, M::DirIdxIndX, M::Imm8, M::StkRel, M::BlkMov, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 4
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::BlkMov, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 5
|
||||
M::Stk, M::DirIdxIndX, M::RelLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::AbsInd, M::Abs, M::Abs, M::AbsLng, // 6
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 7
|
||||
M::Rel, M::DirIdxIndX, M::RelLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 8
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Imp, M::Imp, M::Abs, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 9
|
||||
M::ImmX, M::DirIdxIndX, M::ImmX, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // A
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Imp, M::Imp, M::AbsIdxX, M::AbsIdxX, M::AbsIdxY, M::AbsLngIdxX, // B
|
||||
M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::AbsLng, // C
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Dir, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIndLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // D
|
||||
M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::AbsLng, // E
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Imm16, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX // 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::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
|
||||
M::Stk, M::DirIdxIndX, M::Imm8, M::StkRel, M::BlkMov, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk,
|
||||
M::Abs, M::Abs, M::Abs, M::AbsLng, // 4
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::BlkMov, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp,
|
||||
M::AbsIdxY, M::Stk, M::Imp, M::AbsLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 5
|
||||
M::Stk, M::DirIdxIndX, M::RelLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk,
|
||||
M::AbsInd, M::Abs, M::Abs, M::AbsLng, // 6
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp,
|
||||
M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 7
|
||||
M::Rel, M::DirIdxIndX, M::RelLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk,
|
||||
M::Abs, M::Abs, M::Abs, M::AbsLng, // 8
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp,
|
||||
M::AbsIdxY, M::Imp, M::Imp, M::Abs, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 9
|
||||
M::ImmX, M::DirIdxIndX, M::ImmX, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk,
|
||||
M::Abs, M::Abs, M::Abs, M::AbsLng, // A
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp,
|
||||
M::AbsIdxY, M::Imp, M::Imp, M::AbsIdxX, M::AbsIdxX, M::AbsIdxY, M::AbsLngIdxX, // B
|
||||
M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp,
|
||||
M::Abs, M::Abs, M::Abs, M::AbsLng, // C
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Dir, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp,
|
||||
M::AbsIdxY, M::Stk, M::Imp, M::AbsIndLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // D
|
||||
M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp,
|
||||
M::Abs, M::Abs, M::Abs, M::AbsLng, // E
|
||||
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Imm16, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp,
|
||||
M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX // F
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ class CpuDisUtils
|
|||
{
|
||||
private:
|
||||
static uint8_t OpSize[0x1F];
|
||||
static uint32_t GetOperandAddress(DisassemblyInfo &info, uint32_t memoryAddr);
|
||||
static uint32_t GetOperandAddress(DisassemblyInfo& info, uint32_t memoryAddr);
|
||||
static uint8_t GetOpSize(AddrMode addrMode, uint8_t flags);
|
||||
|
||||
static bool HasEffectiveAddress(AddrMode addrMode);
|
||||
|
@ -22,9 +22,10 @@ public:
|
|||
static string OpName[256];
|
||||
static AddrMode OpMode[256];
|
||||
|
||||
static void GetDisassembly(DisassemblyInfo &info, string &out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings);
|
||||
static void GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager,
|
||||
EmuSettings* settings);
|
||||
static uint8_t GetOpSize(uint8_t opCode, uint8_t flags);
|
||||
static int32_t GetEffectiveAddress(DisassemblyInfo &info, Console* console, CpuState &state, CpuType type);
|
||||
static int32_t GetEffectiveAddress(DisassemblyInfo& info, Console* console, CpuState& state, CpuType type);
|
||||
};
|
||||
|
||||
enum class AddrMode : uint8_t
|
||||
|
@ -35,15 +36,20 @@ enum class AddrMode : uint8_t
|
|||
ImmX,
|
||||
ImmM,
|
||||
Abs,
|
||||
AbsIdxXInd, //JMP/JSR only
|
||||
AbsIdxXInd,
|
||||
//JMP/JSR only
|
||||
AbsIdxX,
|
||||
AbsIdxY,
|
||||
AbsInd, //JMP only
|
||||
AbsIndLng, //JML only
|
||||
AbsInd,
|
||||
//JMP only
|
||||
AbsIndLng,
|
||||
//JML only
|
||||
AbsLngIdxX,
|
||||
AbsLng,
|
||||
AbsJmp, //JSR/JMP only
|
||||
AbsLngJmp, //JSL/JMP only
|
||||
AbsJmp,
|
||||
//JSR/JMP only
|
||||
AbsLngJmp,
|
||||
//JSL/JMP only
|
||||
Acc,
|
||||
BlkMov,
|
||||
DirIdxIndX,
|
||||
|
@ -60,4 +66,4 @@ enum class AddrMode : uint8_t
|
|||
Stk,
|
||||
StkRel,
|
||||
StkRelIndIdxY
|
||||
};
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ struct CpuState
|
|||
uint16_t A;
|
||||
uint16_t X;
|
||||
uint16_t Y;
|
||||
|
||||
|
||||
/* 16-bit stack pointer */
|
||||
uint16_t SP;
|
||||
|
||||
|
@ -88,4 +88,3 @@ enum class IrqSource
|
|||
Ppu = 1,
|
||||
Coprocessor = 2
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "Cx4DisUtils.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
|
||||
static constexpr int shiftLut[4] = { 0 , 1, 8, 16 };
|
||||
static constexpr int shiftLut[4] = {0, 1, 8, 16};
|
||||
|
||||
extern const uint32_t _dataRom[1024];
|
||||
|
||||
|
@ -14,86 +14,151 @@ void Cx4::Exec(uint16_t opCode)
|
|||
uint8_t param1 = (opCode >> 8) & 0x03;
|
||||
uint8_t param2 = opCode & 0xFF;
|
||||
|
||||
switch(op) {
|
||||
case 0x00: NOP(); break;
|
||||
case 0x04: NOP(); break; //???
|
||||
case 0x08: Branch(true, param1, param2); break;
|
||||
case 0x0C: Branch(_state.Zero, param1, param2); break;
|
||||
switch (op)
|
||||
{
|
||||
case 0x00: NOP();
|
||||
break;
|
||||
case 0x04: NOP();
|
||||
break; //???
|
||||
case 0x08: Branch(true, param1, param2);
|
||||
break;
|
||||
case 0x0C: Branch(_state.Zero, param1, param2);
|
||||
break;
|
||||
|
||||
case 0x10: Branch(_state.Carry, param1, param2); break;
|
||||
case 0x14: Branch(_state.Negative, param1, param2); break;
|
||||
case 0x18: Branch(_state.Overflow, param1, param2); break;
|
||||
case 0x1C: WAIT(); break;
|
||||
case 0x10: Branch(_state.Carry, param1, param2);
|
||||
break;
|
||||
case 0x14: Branch(_state.Negative, param1, param2);
|
||||
break;
|
||||
case 0x18: Branch(_state.Overflow, param1, param2);
|
||||
break;
|
||||
case 0x1C: WAIT();
|
||||
break;
|
||||
|
||||
case 0x20: NOP(); break; //???
|
||||
case 0x24: Skip(param1, param2); break;
|
||||
case 0x28: JSR(true, param1, param2); break;
|
||||
case 0x2C: JSR(_state.Zero, param1, param2); break;
|
||||
case 0x20: NOP();
|
||||
break; //???
|
||||
case 0x24: Skip(param1, param2);
|
||||
break;
|
||||
case 0x28: JSR(true, param1, param2);
|
||||
break;
|
||||
case 0x2C: JSR(_state.Zero, param1, param2);
|
||||
break;
|
||||
|
||||
case 0x30: JSR(_state.Carry, param1, param2); break;
|
||||
case 0x34: JSR(_state.Negative, param1, param2); break;
|
||||
case 0x38: JSR(_state.Overflow, param1, param2); break;
|
||||
case 0x3C: RTS(); break;
|
||||
case 0x30: JSR(_state.Carry, param1, param2);
|
||||
break;
|
||||
case 0x34: JSR(_state.Negative, param1, param2);
|
||||
break;
|
||||
case 0x38: JSR(_state.Overflow, param1, param2);
|
||||
break;
|
||||
case 0x3C: RTS();
|
||||
break;
|
||||
|
||||
case 0x40: IncMar(); break;
|
||||
case 0x44: NOP(); break; //???
|
||||
case 0x48: CMPR(param1, param2); break;
|
||||
case 0x4C: CMPR_Imm(param1, param2); break;
|
||||
case 0x40: IncMar();
|
||||
break;
|
||||
case 0x44: NOP();
|
||||
break; //???
|
||||
case 0x48: CMPR(param1, param2);
|
||||
break;
|
||||
case 0x4C: CMPR_Imm(param1, param2);
|
||||
break;
|
||||
|
||||
case 0x50: CMP(param1, param2); break;
|
||||
case 0x54: CMP_Imm(param1, param2); break;
|
||||
case 0x58: SignExtend(param1); break;
|
||||
case 0x5C: NOP(); break; //???
|
||||
case 0x50: CMP(param1, param2);
|
||||
break;
|
||||
case 0x54: CMP_Imm(param1, param2);
|
||||
break;
|
||||
case 0x58: SignExtend(param1);
|
||||
break;
|
||||
case 0x5C: NOP();
|
||||
break; //???
|
||||
|
||||
case 0x60: Load(param1, param2); break;
|
||||
case 0x64: Load_Imm(param1, param2); break;
|
||||
case 0x68: ReadRam(param1); break;
|
||||
case 0x6C: ReadRam_Imm(param1, param2); break;
|
||||
case 0x60: Load(param1, param2);
|
||||
break;
|
||||
case 0x64: Load_Imm(param1, param2);
|
||||
break;
|
||||
case 0x68: ReadRam(param1);
|
||||
break;
|
||||
case 0x6C: ReadRam_Imm(param1, param2);
|
||||
break;
|
||||
|
||||
case 0x70: ReadRom(); break;
|
||||
case 0x74: ReadRom_Imm((param1 << 8) | param2); break;
|
||||
case 0x78: NOP(); break;
|
||||
case 0x7C: LoadP(param1, param2); break;
|
||||
case 0x70: ReadRom();
|
||||
break;
|
||||
case 0x74: ReadRom_Imm((param1 << 8) | param2);
|
||||
break;
|
||||
case 0x78: NOP();
|
||||
break;
|
||||
case 0x7C: LoadP(param1, param2);
|
||||
break;
|
||||
|
||||
case 0x80: ADD(param1, param2); break;
|
||||
case 0x84: ADD_Imm(param1, param2); break;
|
||||
case 0x88: SUBR(param1, param2); break;
|
||||
case 0x8C: SUBR_Imm(param1, param2); break;
|
||||
case 0x80: ADD(param1, param2);
|
||||
break;
|
||||
case 0x84: ADD_Imm(param1, param2);
|
||||
break;
|
||||
case 0x88: SUBR(param1, param2);
|
||||
break;
|
||||
case 0x8C: SUBR_Imm(param1, param2);
|
||||
break;
|
||||
|
||||
case 0x90: SUB(param1, param2); break;
|
||||
case 0x94: SUB_Imm(param1, param2); break;
|
||||
case 0x98: SMUL(param2); break;
|
||||
case 0x9C: SMUL_Imm(param2); break;
|
||||
case 0x90: SUB(param1, param2);
|
||||
break;
|
||||
case 0x94: SUB_Imm(param1, param2);
|
||||
break;
|
||||
case 0x98: SMUL(param2);
|
||||
break;
|
||||
case 0x9C: SMUL_Imm(param2);
|
||||
break;
|
||||
|
||||
case 0xA0: XNOR(param1, param2); break;
|
||||
case 0xA4: XNOR_Imm(param1, param2); break;
|
||||
case 0xA8: XOR(param1, param2); break;
|
||||
case 0xAC: XOR_Imm(param1, param2); break;
|
||||
case 0xA0: XNOR(param1, param2);
|
||||
break;
|
||||
case 0xA4: XNOR_Imm(param1, param2);
|
||||
break;
|
||||
case 0xA8: XOR(param1, param2);
|
||||
break;
|
||||
case 0xAC: XOR_Imm(param1, param2);
|
||||
break;
|
||||
|
||||
case 0xB0: AND(param1, param2); break;
|
||||
case 0xB4: AND_Imm(param1, param2); break;
|
||||
case 0xB8: OR(param1, param2); break;
|
||||
case 0xBC: OR_Imm(param1, param2); break;
|
||||
case 0xB0: AND(param1, param2);
|
||||
break;
|
||||
case 0xB4: AND_Imm(param1, param2);
|
||||
break;
|
||||
case 0xB8: OR(param1, param2);
|
||||
break;
|
||||
case 0xBC: OR_Imm(param1, param2);
|
||||
break;
|
||||
|
||||
case 0xC0: SHR(param2); break;
|
||||
case 0xC4: SHR_Imm(param2); break;
|
||||
case 0xC8: ASR(param2); break;
|
||||
case 0xCC: ASR_Imm(param2); break;
|
||||
case 0xC0: SHR(param2);
|
||||
break;
|
||||
case 0xC4: SHR_Imm(param2);
|
||||
break;
|
||||
case 0xC8: ASR(param2);
|
||||
break;
|
||||
case 0xCC: ASR_Imm(param2);
|
||||
break;
|
||||
|
||||
case 0xD0: ROR(param2); break;
|
||||
case 0xD4: ROR_Imm(param2); break;
|
||||
case 0xD8: SHL(param2); break;
|
||||
case 0xDC: SHL_Imm(param2); break;
|
||||
case 0xD0: ROR(param2);
|
||||
break;
|
||||
case 0xD4: ROR_Imm(param2);
|
||||
break;
|
||||
case 0xD8: SHL(param2);
|
||||
break;
|
||||
case 0xDC: SHL_Imm(param2);
|
||||
break;
|
||||
|
||||
case 0xE0: Store(param1, param2); break;
|
||||
case 0xE4: NOP(); break; //???
|
||||
case 0xE8: WriteRam(param1); break;
|
||||
case 0xEC: WriteRam_Imm(param1, param2); break;
|
||||
case 0xE0: Store(param1, param2);
|
||||
break;
|
||||
case 0xE4: NOP();
|
||||
break; //???
|
||||
case 0xE8: WriteRam(param1);
|
||||
break;
|
||||
case 0xEC: WriteRam_Imm(param1, param2);
|
||||
break;
|
||||
|
||||
case 0xF0: Swap(param2 & 0x0F); break;
|
||||
case 0xF4: NOP(); break; //???
|
||||
case 0xF8: NOP(); break; //???
|
||||
case 0xFC: Stop(); break;
|
||||
case 0xF0: Swap(param2 & 0x0F);
|
||||
break;
|
||||
case 0xF4: NOP();
|
||||
break; //???
|
||||
case 0xF8: NOP();
|
||||
break; //???
|
||||
case 0xFC: Stop();
|
||||
break;
|
||||
}
|
||||
|
||||
Step(1);
|
||||
|
@ -101,119 +166,177 @@ void Cx4::Exec(uint16_t opCode)
|
|||
|
||||
uint32_t Cx4::GetSourceValue(uint8_t src)
|
||||
{
|
||||
switch(src & 0x7F) {
|
||||
case 0x00: return _state.A;
|
||||
case 0x01: return (_state.Mult >> 24) & 0xFFFFFF;
|
||||
case 0x02: return _state.Mult & 0xFFFFFF;
|
||||
case 0x03: return _state.MemoryDataReg;
|
||||
case 0x08: return _state.RomBuffer;
|
||||
case 0x0C: return (_state.RamBuffer[2] << 16) | (_state.RamBuffer[1] << 8) | _state.RamBuffer[0];
|
||||
case 0x13: return _state.MemoryAddressReg;
|
||||
case 0x1C: return _state.DataPointerReg;
|
||||
case 0x20: return _state.PC;
|
||||
case 0x28: return _state.P;
|
||||
switch (src & 0x7F)
|
||||
{
|
||||
case 0x00: return _state.A;
|
||||
case 0x01: return (_state.Mult >> 24) & 0xFFFFFF;
|
||||
case 0x02: return _state.Mult & 0xFFFFFF;
|
||||
case 0x03: return _state.MemoryDataReg;
|
||||
case 0x08: return _state.RomBuffer;
|
||||
case 0x0C: return (_state.RamBuffer[2] << 16) | (_state.RamBuffer[1] << 8) | _state.RamBuffer[0];
|
||||
case 0x13: return _state.MemoryAddressReg;
|
||||
case 0x1C: return _state.DataPointerReg;
|
||||
case 0x20: return _state.PC;
|
||||
case 0x28: return _state.P;
|
||||
|
||||
case 0x2E:
|
||||
_state.Bus.Enabled = true;
|
||||
_state.Bus.Reading = true;
|
||||
_state.Bus.DelayCycles = 1 + _state.RomAccessDelay;
|
||||
_state.Bus.Address = _state.MemoryAddressReg;
|
||||
return 0;
|
||||
case 0x2E:
|
||||
_state.Bus.Enabled = true;
|
||||
_state.Bus.Reading = true;
|
||||
_state.Bus.DelayCycles = 1 + _state.RomAccessDelay;
|
||||
_state.Bus.Address = _state.MemoryAddressReg;
|
||||
return 0;
|
||||
|
||||
case 0x2F:
|
||||
_state.Bus.Enabled = true;
|
||||
_state.Bus.Reading = true;
|
||||
_state.Bus.DelayCycles = 1 + _state.RamAccessDelay;
|
||||
_state.Bus.Address = _state.MemoryAddressReg;
|
||||
return 0;
|
||||
case 0x2F:
|
||||
_state.Bus.Enabled = true;
|
||||
_state.Bus.Reading = true;
|
||||
_state.Bus.DelayCycles = 1 + _state.RamAccessDelay;
|
||||
_state.Bus.Address = _state.MemoryAddressReg;
|
||||
return 0;
|
||||
|
||||
case 0x50: return 0x000000;
|
||||
case 0x51: return 0xFFFFFF;
|
||||
case 0x52: return 0x00FF00;
|
||||
case 0x53: return 0xFF0000;
|
||||
case 0x54: return 0x00FFFF;
|
||||
case 0x55: return 0xFFFF00;
|
||||
case 0x56: return 0x800000;
|
||||
case 0x57: return 0x7FFFFF;
|
||||
case 0x58: return 0x008000;
|
||||
case 0x59: return 0x007FFF;
|
||||
case 0x5A: return 0xFF7FFF;
|
||||
case 0x5B: return 0xFFFF7F;
|
||||
case 0x5C: return 0x010000;
|
||||
case 0x5D: return 0xFEFFFF;
|
||||
case 0x5E: return 0x000100;
|
||||
case 0x5F: return 0x00FEFF;
|
||||
case 0x50: return 0x000000;
|
||||
case 0x51: return 0xFFFFFF;
|
||||
case 0x52: return 0x00FF00;
|
||||
case 0x53: return 0xFF0000;
|
||||
case 0x54: return 0x00FFFF;
|
||||
case 0x55: return 0xFFFF00;
|
||||
case 0x56: return 0x800000;
|
||||
case 0x57: return 0x7FFFFF;
|
||||
case 0x58: return 0x008000;
|
||||
case 0x59: return 0x007FFF;
|
||||
case 0x5A: return 0xFF7FFF;
|
||||
case 0x5B: return 0xFFFF7F;
|
||||
case 0x5C: return 0x010000;
|
||||
case 0x5D: return 0xFEFFFF;
|
||||
case 0x5E: return 0x000100;
|
||||
case 0x5F: return 0x00FEFF;
|
||||
|
||||
case 0x60: case 0x70: return _state.Regs[0];
|
||||
case 0x61: case 0x71: return _state.Regs[1];
|
||||
case 0x62: case 0x72: return _state.Regs[2];
|
||||
case 0x63: case 0x73: return _state.Regs[3];
|
||||
case 0x64: case 0x74: return _state.Regs[4];
|
||||
case 0x65: case 0x75: return _state.Regs[5];
|
||||
case 0x66: case 0x76: return _state.Regs[6];
|
||||
case 0x67: case 0x77: return _state.Regs[7];
|
||||
case 0x68: case 0x78: return _state.Regs[8];
|
||||
case 0x69: case 0x79: return _state.Regs[9];
|
||||
case 0x6A: case 0x7A: return _state.Regs[10];
|
||||
case 0x6B: case 0x7B: return _state.Regs[11];
|
||||
case 0x6C: case 0x7C: return _state.Regs[12];
|
||||
case 0x6D: case 0x7D: return _state.Regs[13];
|
||||
case 0x6E: case 0x7E: return _state.Regs[14];
|
||||
case 0x6F: case 0x7F: return _state.Regs[15];
|
||||
case 0x60:
|
||||
case 0x70: return _state.Regs[0];
|
||||
case 0x61:
|
||||
case 0x71: return _state.Regs[1];
|
||||
case 0x62:
|
||||
case 0x72: return _state.Regs[2];
|
||||
case 0x63:
|
||||
case 0x73: return _state.Regs[3];
|
||||
case 0x64:
|
||||
case 0x74: return _state.Regs[4];
|
||||
case 0x65:
|
||||
case 0x75: return _state.Regs[5];
|
||||
case 0x66:
|
||||
case 0x76: return _state.Regs[6];
|
||||
case 0x67:
|
||||
case 0x77: return _state.Regs[7];
|
||||
case 0x68:
|
||||
case 0x78: return _state.Regs[8];
|
||||
case 0x69:
|
||||
case 0x79: return _state.Regs[9];
|
||||
case 0x6A:
|
||||
case 0x7A: return _state.Regs[10];
|
||||
case 0x6B:
|
||||
case 0x7B: return _state.Regs[11];
|
||||
case 0x6C:
|
||||
case 0x7C: return _state.Regs[12];
|
||||
case 0x6D:
|
||||
case 0x7D: return _state.Regs[13];
|
||||
case 0x6E:
|
||||
case 0x7E: return _state.Regs[14];
|
||||
case 0x6F:
|
||||
case 0x7F: return _state.Regs[15];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Cx4::WriteRegister(uint8_t reg, uint32_t value)
|
||||
void Cx4::WriteRegister(uint8_t reg, uint32_t value)
|
||||
{
|
||||
value &= 0xFFFFFF;
|
||||
switch(reg & 0x7F) {
|
||||
case 0x01: _state.Mult = (_state.Mult & 0xFFFFFF) | (value << 24); break;
|
||||
case 0x02: _state.Mult = (_state.Mult & 0xFFFFFF000000) | value; break;
|
||||
case 0x03: _state.MemoryDataReg = value; break;
|
||||
case 0x08: _state.RomBuffer = value; break;
|
||||
case 0x0C:
|
||||
_state.RamBuffer[0] = value;
|
||||
_state.RamBuffer[1] = value >> 8;
|
||||
_state.RamBuffer[2] = value >> 16;
|
||||
break;
|
||||
switch (reg & 0x7F)
|
||||
{
|
||||
case 0x01: _state.Mult = (_state.Mult & 0xFFFFFF) | (value << 24);
|
||||
break;
|
||||
case 0x02: _state.Mult = (_state.Mult & 0xFFFFFF000000) | value;
|
||||
break;
|
||||
case 0x03: _state.MemoryDataReg = value;
|
||||
break;
|
||||
case 0x08: _state.RomBuffer = value;
|
||||
break;
|
||||
case 0x0C:
|
||||
_state.RamBuffer[0] = value;
|
||||
_state.RamBuffer[1] = value >> 8;
|
||||
_state.RamBuffer[2] = value >> 16;
|
||||
break;
|
||||
|
||||
case 0x13: _state.MemoryAddressReg = value; break;
|
||||
case 0x1C: _state.DataPointerReg = value; break;
|
||||
case 0x20: _state.PC = value; break;
|
||||
case 0x28: _state.P = (value & 0x7FFF); break;
|
||||
|
||||
case 0x2E:
|
||||
_state.Bus.Enabled = true;
|
||||
_state.Bus.Writing = true;
|
||||
_state.Bus.DelayCycles = 1 + _state.RomAccessDelay;
|
||||
_state.Bus.Address = _state.MemoryAddressReg;
|
||||
break;
|
||||
case 0x13: _state.MemoryAddressReg = value;
|
||||
break;
|
||||
case 0x1C: _state.DataPointerReg = value;
|
||||
break;
|
||||
case 0x20: _state.PC = value;
|
||||
break;
|
||||
case 0x28: _state.P = (value & 0x7FFF);
|
||||
break;
|
||||
|
||||
case 0x2F:
|
||||
_state.Bus.Enabled = true;
|
||||
_state.Bus.Writing = true;
|
||||
_state.Bus.DelayCycles = 1 + _state.RamAccessDelay;
|
||||
_state.Bus.Address = _state.MemoryAddressReg;
|
||||
break;
|
||||
case 0x2E:
|
||||
_state.Bus.Enabled = true;
|
||||
_state.Bus.Writing = true;
|
||||
_state.Bus.DelayCycles = 1 + _state.RomAccessDelay;
|
||||
_state.Bus.Address = _state.MemoryAddressReg;
|
||||
break;
|
||||
|
||||
case 0x60: case 0x70: _state.Regs[0] = value; break;
|
||||
case 0x61: case 0x71: _state.Regs[1] = value; break;
|
||||
case 0x62: case 0x72: _state.Regs[2] = value; break;
|
||||
case 0x63: case 0x73: _state.Regs[3] = value; break;
|
||||
case 0x64: case 0x74: _state.Regs[4] = value; break;
|
||||
case 0x65: case 0x75: _state.Regs[5] = value; break;
|
||||
case 0x66: case 0x76: _state.Regs[6] = value; break;
|
||||
case 0x67: case 0x77: _state.Regs[7] = value; break;
|
||||
case 0x68: case 0x78: _state.Regs[8] = value; break;
|
||||
case 0x69: case 0x79: _state.Regs[9] = value; break;
|
||||
case 0x6A: case 0x7a: _state.Regs[10] = value; break;
|
||||
case 0x6B: case 0x7b: _state.Regs[11] = value; break;
|
||||
case 0x6C: case 0x7c: _state.Regs[12] = value; break;
|
||||
case 0x6D: case 0x7d: _state.Regs[13] = value; break;
|
||||
case 0x6E: case 0x7e: _state.Regs[14] = value; break;
|
||||
case 0x6F: case 0x7f: _state.Regs[15] = value; break;
|
||||
case 0x2F:
|
||||
_state.Bus.Enabled = true;
|
||||
_state.Bus.Writing = true;
|
||||
_state.Bus.DelayCycles = 1 + _state.RamAccessDelay;
|
||||
_state.Bus.Address = _state.MemoryAddressReg;
|
||||
break;
|
||||
|
||||
case 0x60:
|
||||
case 0x70: _state.Regs[0] = value;
|
||||
break;
|
||||
case 0x61:
|
||||
case 0x71: _state.Regs[1] = value;
|
||||
break;
|
||||
case 0x62:
|
||||
case 0x72: _state.Regs[2] = value;
|
||||
break;
|
||||
case 0x63:
|
||||
case 0x73: _state.Regs[3] = value;
|
||||
break;
|
||||
case 0x64:
|
||||
case 0x74: _state.Regs[4] = value;
|
||||
break;
|
||||
case 0x65:
|
||||
case 0x75: _state.Regs[5] = value;
|
||||
break;
|
||||
case 0x66:
|
||||
case 0x76: _state.Regs[6] = value;
|
||||
break;
|
||||
case 0x67:
|
||||
case 0x77: _state.Regs[7] = value;
|
||||
break;
|
||||
case 0x68:
|
||||
case 0x78: _state.Regs[8] = value;
|
||||
break;
|
||||
case 0x69:
|
||||
case 0x79: _state.Regs[9] = value;
|
||||
break;
|
||||
case 0x6A:
|
||||
case 0x7a: _state.Regs[10] = value;
|
||||
break;
|
||||
case 0x6B:
|
||||
case 0x7b: _state.Regs[11] = value;
|
||||
break;
|
||||
case 0x6C:
|
||||
case 0x7c: _state.Regs[12] = value;
|
||||
break;
|
||||
case 0x6D:
|
||||
case 0x7d: _state.Regs[13] = value;
|
||||
break;
|
||||
case 0x6E:
|
||||
case 0x7e: _state.Regs[14] = value;
|
||||
break;
|
||||
case 0x6F:
|
||||
case 0x7f: _state.Regs[15] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,7 +351,8 @@ void Cx4::NOP()
|
|||
|
||||
void Cx4::WAIT()
|
||||
{
|
||||
if(_state.Bus.Enabled) {
|
||||
if (_state.Bus.Enabled)
|
||||
{
|
||||
Step(_state.Bus.DelayCycles);
|
||||
}
|
||||
}
|
||||
|
@ -236,17 +360,24 @@ void Cx4::WAIT()
|
|||
void Cx4::Skip(uint8_t flagToCheck, uint8_t skipIfSet)
|
||||
{
|
||||
bool skip;
|
||||
switch(flagToCheck) {
|
||||
default:
|
||||
case 0: skip = _state.Overflow == (skipIfSet & 0x01); break;
|
||||
case 1: skip = _state.Carry == (skipIfSet & 0x01); break;
|
||||
case 2: skip = _state.Zero == (skipIfSet & 0x01); break;
|
||||
case 3: skip = _state.Negative == (skipIfSet & 0x01); break;
|
||||
switch (flagToCheck)
|
||||
{
|
||||
default:
|
||||
case 0: skip = _state.Overflow == (skipIfSet & 0x01);
|
||||
break;
|
||||
case 1: skip = _state.Carry == (skipIfSet & 0x01);
|
||||
break;
|
||||
case 2: skip = _state.Zero == (skipIfSet & 0x01);
|
||||
break;
|
||||
case 3: skip = _state.Negative == (skipIfSet & 0x01);
|
||||
break;
|
||||
}
|
||||
|
||||
if(skip) {
|
||||
if (skip)
|
||||
{
|
||||
_state.PC++;
|
||||
if(_state.PC == 0) {
|
||||
if (_state.PC == 0)
|
||||
{
|
||||
SwitchCachePage();
|
||||
}
|
||||
Step(1);
|
||||
|
@ -255,8 +386,10 @@ void Cx4::Skip(uint8_t flagToCheck, uint8_t skipIfSet)
|
|||
|
||||
void Cx4::Branch(bool branch, uint8_t far, uint8_t dest)
|
||||
{
|
||||
if(branch) {
|
||||
if(far) {
|
||||
if (branch)
|
||||
{
|
||||
if (far)
|
||||
{
|
||||
_state.PB = _state.P;
|
||||
}
|
||||
_state.PC = dest;
|
||||
|
@ -266,9 +399,11 @@ void Cx4::Branch(bool branch, uint8_t far, uint8_t dest)
|
|||
|
||||
void Cx4::JSR(bool branch, uint8_t far, uint8_t dest)
|
||||
{
|
||||
if(branch) {
|
||||
if (branch)
|
||||
{
|
||||
PushPC();
|
||||
if(far) {
|
||||
if (far)
|
||||
{
|
||||
_state.PB = _state.P;
|
||||
}
|
||||
_state.PC = dest;
|
||||
|
@ -348,11 +483,14 @@ void Cx4::CMP_Imm(uint8_t shift, uint8_t imm)
|
|||
|
||||
void Cx4::SignExtend(uint8_t mode)
|
||||
{
|
||||
if(mode == 1) {
|
||||
if (mode == 1)
|
||||
{
|
||||
_state.A = ((uint32_t)(int8_t)_state.A) & 0xFFFFFF;
|
||||
_state.Negative = _state.A & 0x800000;
|
||||
_state.Zero = _state.A == 0;
|
||||
} else if(mode == 2) {
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
_state.A = ((uint32_t)(int16_t)_state.A) & 0xFFFFFF;
|
||||
_state.Negative = _state.A & 0x800000;
|
||||
_state.Zero = _state.A == 0;
|
||||
|
@ -361,21 +499,31 @@ void Cx4::SignExtend(uint8_t mode)
|
|||
|
||||
void Cx4::Load(uint8_t dest, uint8_t src)
|
||||
{
|
||||
switch(dest) {
|
||||
case 0: _state.A = GetSourceValue(src); break;
|
||||
case 1: _state.MemoryDataReg = GetSourceValue(src); break;
|
||||
case 2: _state.MemoryAddressReg = GetSourceValue(src); break;
|
||||
case 3: _state.P = GetSourceValue(src) & 0x7FFF; break;
|
||||
switch (dest)
|
||||
{
|
||||
case 0: _state.A = GetSourceValue(src);
|
||||
break;
|
||||
case 1: _state.MemoryDataReg = GetSourceValue(src);
|
||||
break;
|
||||
case 2: _state.MemoryAddressReg = GetSourceValue(src);
|
||||
break;
|
||||
case 3: _state.P = GetSourceValue(src) & 0x7FFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Cx4::Load_Imm(uint8_t dest, uint8_t imm)
|
||||
{
|
||||
switch(dest) {
|
||||
case 0: _state.A = imm; break;
|
||||
case 1: _state.MemoryDataReg = imm; break;
|
||||
case 2: _state.MemoryAddressReg = imm; break;
|
||||
case 3: _state.P = imm; break;
|
||||
switch (dest)
|
||||
{
|
||||
case 0: _state.A = imm;
|
||||
break;
|
||||
case 1: _state.MemoryDataReg = imm;
|
||||
break;
|
||||
case 2: _state.MemoryAddressReg = imm;
|
||||
break;
|
||||
case 3: _state.P = imm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,7 +619,8 @@ void Cx4::XNOR_Imm(uint8_t shift, uint8_t imm)
|
|||
void Cx4::SHR(uint8_t src)
|
||||
{
|
||||
uint8_t shift = GetSourceValue(src) & 0x1F;
|
||||
if(shift < 24) {
|
||||
if (shift < 24)
|
||||
{
|
||||
SetA(_state.A >> shift);
|
||||
}
|
||||
SetZeroNegativeFlags();
|
||||
|
@ -480,7 +629,8 @@ void Cx4::SHR(uint8_t src)
|
|||
void Cx4::SHR_Imm(uint8_t imm)
|
||||
{
|
||||
uint8_t shift = imm & 0x1F;
|
||||
if(shift < 24) {
|
||||
if (shift < 24)
|
||||
{
|
||||
SetA(_state.A >> shift);
|
||||
}
|
||||
SetZeroNegativeFlags();
|
||||
|
@ -489,7 +639,8 @@ void Cx4::SHR_Imm(uint8_t imm)
|
|||
void Cx4::ASR(uint8_t src)
|
||||
{
|
||||
uint8_t shift = GetSourceValue(src) & 0x1F;
|
||||
if(shift < 24) {
|
||||
if (shift < 24)
|
||||
{
|
||||
SetA((((int32_t)_state.A << 8) >> 8) >> shift);
|
||||
}
|
||||
SetZeroNegativeFlags();
|
||||
|
@ -498,7 +649,8 @@ void Cx4::ASR(uint8_t src)
|
|||
void Cx4::ASR_Imm(uint8_t imm)
|
||||
{
|
||||
uint8_t shift = imm & 0x1F;
|
||||
if(shift < 24) {
|
||||
if (shift < 24)
|
||||
{
|
||||
SetA((((int32_t)_state.A << 8) >> 8) >> shift);
|
||||
}
|
||||
SetZeroNegativeFlags();
|
||||
|
@ -507,7 +659,8 @@ void Cx4::ASR_Imm(uint8_t imm)
|
|||
void Cx4::SHL(uint8_t src)
|
||||
{
|
||||
uint8_t shift = GetSourceValue(src) & 0x1F;
|
||||
if(shift < 24) {
|
||||
if (shift < 24)
|
||||
{
|
||||
SetA(_state.A << shift);
|
||||
}
|
||||
SetZeroNegativeFlags();
|
||||
|
@ -516,7 +669,8 @@ void Cx4::SHL(uint8_t src)
|
|||
void Cx4::SHL_Imm(uint8_t imm)
|
||||
{
|
||||
uint8_t shift = imm & 0x1F;
|
||||
if(shift < 24) {
|
||||
if (shift < 24)
|
||||
{
|
||||
SetA(_state.A << shift);
|
||||
}
|
||||
SetZeroNegativeFlags();
|
||||
|
@ -525,7 +679,8 @@ void Cx4::SHL_Imm(uint8_t imm)
|
|||
void Cx4::ROR(uint8_t src)
|
||||
{
|
||||
uint8_t shift = GetSourceValue(src) & 0x1F;
|
||||
if(shift < 24) {
|
||||
if (shift < 24)
|
||||
{
|
||||
SetA((_state.A >> shift) | (_state.A << (24 - shift)));
|
||||
}
|
||||
SetZeroNegativeFlags();
|
||||
|
@ -534,7 +689,8 @@ void Cx4::ROR(uint8_t src)
|
|||
void Cx4::ROR_Imm(uint8_t imm)
|
||||
{
|
||||
uint8_t shift = imm & 0x1F;
|
||||
if(shift < 24) {
|
||||
if (shift < 24)
|
||||
{
|
||||
SetA((_state.A >> shift) | (_state.A << (24 - shift)));
|
||||
}
|
||||
SetZeroNegativeFlags();
|
||||
|
@ -552,12 +708,14 @@ void Cx4::ReadRom_Imm(uint16_t imm)
|
|||
|
||||
void Cx4::ReadRam(uint8_t byteIndex)
|
||||
{
|
||||
if(byteIndex >= 3) {
|
||||
if (byteIndex >= 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t addr = _state.A & 0xFFF;
|
||||
if(addr >= 0xC00) {
|
||||
if (addr >= 0xC00)
|
||||
{
|
||||
addr -= 0x400;
|
||||
}
|
||||
|
||||
|
@ -566,12 +724,14 @@ void Cx4::ReadRam(uint8_t byteIndex)
|
|||
|
||||
void Cx4::ReadRam_Imm(uint8_t byteIndex, uint8_t imm)
|
||||
{
|
||||
if(byteIndex >= 3) {
|
||||
if (byteIndex >= 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t addr = (_state.DataPointerReg + imm) & 0xFFF;
|
||||
if(addr >= 0xC00) {
|
||||
if (addr >= 0xC00)
|
||||
{
|
||||
addr -= 0x400;
|
||||
}
|
||||
_state.RamBuffer[byteIndex] = _dataRam[addr];
|
||||
|
@ -579,12 +739,14 @@ void Cx4::ReadRam_Imm(uint8_t byteIndex, uint8_t imm)
|
|||
|
||||
void Cx4::WriteRam(uint8_t byteIndex)
|
||||
{
|
||||
if(byteIndex >= 3) {
|
||||
if (byteIndex >= 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t addr = _state.A & 0xFFF;
|
||||
if(addr >= 0xC00) {
|
||||
if (addr >= 0xC00)
|
||||
{
|
||||
addr -= 0x400;
|
||||
}
|
||||
|
||||
|
@ -593,12 +755,14 @@ void Cx4::WriteRam(uint8_t byteIndex)
|
|||
|
||||
void Cx4::WriteRam_Imm(uint8_t byteIndex, uint8_t imm)
|
||||
{
|
||||
if(byteIndex >= 3) {
|
||||
if (byteIndex >= 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t addr = (_state.DataPointerReg + imm) & 0xFFF;
|
||||
if(addr >= 0xC00) {
|
||||
if (addr >= 0xC00)
|
||||
{
|
||||
addr -= 0x400;
|
||||
}
|
||||
_dataRam[addr] = _state.RamBuffer[byteIndex];
|
||||
|
@ -606,10 +770,13 @@ void Cx4::WriteRam_Imm(uint8_t byteIndex, uint8_t imm)
|
|||
|
||||
void Cx4::LoadP(uint8_t byteIndex, uint8_t imm)
|
||||
{
|
||||
switch(byteIndex) {
|
||||
case 0: _state.P = (_state.P & 0x7F00) | imm; break;
|
||||
case 1: _state.P = (_state.P & 0xFF) | ((imm & 0x7F) << 8); break;
|
||||
default: break; //nop
|
||||
switch (byteIndex)
|
||||
{
|
||||
case 0: _state.P = (_state.P & 0x7F00) | imm;
|
||||
break;
|
||||
case 1: _state.P = (_state.P & 0xFF) | ((imm & 0x7F) << 8);
|
||||
break;
|
||||
default: break; //nop
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -622,17 +789,21 @@ void Cx4::Swap(uint8_t reg)
|
|||
|
||||
void Cx4::Store(uint8_t src, uint8_t dst)
|
||||
{
|
||||
switch(src) {
|
||||
case 0: WriteRegister(dst, _state.A); break;
|
||||
case 1: WriteRegister(dst, _state.MemoryDataReg); break;
|
||||
default: break; //nop
|
||||
switch (src)
|
||||
{
|
||||
case 0: WriteRegister(dst, _state.A);
|
||||
break;
|
||||
case 1: WriteRegister(dst, _state.MemoryDataReg);
|
||||
break;
|
||||
default: break; //nop
|
||||
}
|
||||
}
|
||||
|
||||
void Cx4::Stop()
|
||||
{
|
||||
_state.Stopped = true;
|
||||
if(!_state.IrqDisabled) {
|
||||
if (!_state.IrqDisabled)
|
||||
{
|
||||
_state.IrqFlag = true;
|
||||
_cpu->SetIrqSource(IrqSource::Coprocessor);
|
||||
}
|
||||
|
@ -645,68 +816,132 @@ void Cx4::IncMar()
|
|||
|
||||
//Data ROM that contains precalculated math lookup tables for sines, cosines, division, etc.
|
||||
const uint32_t _dataRom[1024] = {
|
||||
0xFFFFFF, 0x800000, 0x400000, 0x2AAAAA, 0x200000, 0x199999, 0x155555, 0x124924, 0x100000, 0x0E38E3, 0x0CCCCC, 0x0BA2E8, 0x0AAAAA, 0x09D89D, 0x092492, 0x088888,
|
||||
0x080000, 0x078787, 0x071C71, 0x06BCA1, 0x066666, 0x061861, 0x05D174, 0x0590B2, 0x055555, 0x051EB8, 0x04EC4E, 0x04BDA1, 0x049249, 0x0469EE, 0x044444, 0x042108,
|
||||
0x040000, 0x03E0F8, 0x03C3C3, 0x03A83A, 0x038E38, 0x03759F, 0x035E50, 0x034834, 0x033333, 0x031F38, 0x030C30, 0x02FA0B, 0x02E8BA, 0x02D82D, 0x02C859, 0x02B931,
|
||||
0x02AAAA, 0x029CBC, 0x028F5C, 0x028282, 0x027627, 0x026A43, 0x025ED0, 0x0253C8, 0x024924, 0x023EE0, 0x0234F7, 0x022B63, 0x022222, 0x02192E, 0x021084, 0x020820,
|
||||
0x020000, 0x01F81F, 0x01F07C, 0x01E913, 0x01E1E1, 0x01DAE6, 0x01D41D, 0x01CD85, 0x01C71C, 0x01C0E0, 0x01BACF, 0x01B4E8, 0x01AF28, 0x01A98E, 0x01A41A, 0x019EC8,
|
||||
0x019999, 0x01948B, 0x018F9C, 0x018ACB, 0x018618, 0x018181, 0x017D05, 0x0178A4, 0x01745D, 0x01702E, 0x016C16, 0x016816, 0x01642C, 0x016058, 0x015C98, 0x0158ED,
|
||||
0x015555, 0x0151D0, 0x014E5E, 0x014AFD, 0x0147AE, 0x01446F, 0x014141, 0x013E22, 0x013B13, 0x013813, 0x013521, 0x01323E, 0x012F68, 0x012C9F, 0x0129E4, 0x012735,
|
||||
0x012492, 0x0121FB, 0x011F70, 0x011CF0, 0x011A7B, 0x011811, 0x0115B1, 0x01135C, 0x011111, 0x010ECF, 0x010C97, 0x010A68, 0x010842, 0x010624, 0x010410, 0x010204,
|
||||
0x010000, 0x00FE03, 0x00FC0F, 0x00FA23, 0x00F83E, 0x00F660, 0x00F489, 0x00F2B9, 0x00F0F0, 0x00EF2E, 0x00ED73, 0x00EBBD, 0x00EA0E, 0x00E865, 0x00E6C2, 0x00E525,
|
||||
0x00E38E, 0x00E1FC, 0x00E070, 0x00DEE9, 0x00DD67, 0x00DBEB, 0x00DA74, 0x00D901, 0x00D794, 0x00D62B, 0x00D4C7, 0x00D368, 0x00D20D, 0x00D0B6, 0x00CF64, 0x00CE16,
|
||||
0x00CCCC, 0x00CB87, 0x00CA45, 0x00C907, 0x00C7CE, 0x00C698, 0x00C565, 0x00C437, 0x00C30C, 0x00C1E4, 0x00C0C0, 0x00BFA0, 0x00BE82, 0x00BD69, 0x00BC52, 0x00BB3E,
|
||||
0x00BA2E, 0x00B921, 0x00B817, 0x00B70F, 0x00B60B, 0x00B509, 0x00B40B, 0x00B30F, 0x00B216, 0x00B11F, 0x00B02C, 0x00AF3A, 0x00AE4C, 0x00AD60, 0x00AC76, 0x00AB8F,
|
||||
0x00AAAA, 0x00A9C8, 0x00A8E8, 0x00A80A, 0x00A72F, 0x00A655, 0x00A57E, 0x00A4A9, 0x00A3D7, 0x00A306, 0x00A237, 0x00A16B, 0x00A0A0, 0x009FD8, 0x009F11, 0x009E4C,
|
||||
0x009D89, 0x009CC8, 0x009C09, 0x009B4C, 0x009A90, 0x0099D7, 0x00991F, 0x009868, 0x0097B4, 0x009701, 0x00964F, 0x0095A0, 0x0094F2, 0x009445, 0x00939A, 0x0092F1,
|
||||
0x009249, 0x0091A2, 0x0090FD, 0x00905A, 0x008FB8, 0x008F17, 0x008E78, 0x008DDA, 0x008D3D, 0x008CA2, 0x008C08, 0x008B70, 0x008AD8, 0x008A42, 0x0089AE, 0x00891A,
|
||||
0x008888, 0x0087F7, 0x008767, 0x0086D9, 0x00864B, 0x0085BF, 0x008534, 0x0084A9, 0x008421, 0x008399, 0x008312, 0x00828C, 0x008208, 0x008184, 0x008102, 0x008080,
|
||||
0x000000, 0x100000, 0x16A09E, 0x1BB67A, 0x200000, 0x23C6EF, 0x27311C, 0x2A54FF, 0x2D413C, 0x300000, 0x3298B0, 0x3510E5, 0x376CF5, 0x39B056, 0x3BDDD4, 0x3DF7BD,
|
||||
0x400000, 0x41F83D, 0x43E1DB, 0x45BE0C, 0x478DDE, 0x49523A, 0x4B0BF1, 0x4CBBB9, 0x4E6238, 0x500000, 0x519595, 0x532370, 0x54A9FE, 0x5629A2, 0x57A2B7, 0x591590,
|
||||
0x5A8279, 0x5BE9BA, 0x5D4B94, 0x5EA843, 0x600000, 0x6152FE, 0x62A170, 0x63EB83, 0x653160, 0x667332, 0x67B11D, 0x68EB44, 0x6A21CA, 0x6B54CD, 0x6C846C, 0x6DB0C2,
|
||||
0x6ED9EB, 0x700000, 0x712318, 0x72434A, 0x7360AD, 0x747B54, 0x759354, 0x76A8BF, 0x77BBA8, 0x78CC1F, 0x79DA34, 0x7AE5F9, 0x7BEF7A, 0x7CF6C8, 0x7DFBEF, 0x7EFEFD,
|
||||
0x800000, 0x80FF01, 0x81FC0F, 0x82F734, 0x83F07B, 0x84E7EE, 0x85DD98, 0x86D182, 0x87C3B6, 0x88B43D, 0x89A31F, 0x8A9066, 0x8B7C19, 0x8C6641, 0x8D4EE4, 0x8E360B,
|
||||
0x8F1BBC, 0x900000, 0x90E2DB, 0x91C456, 0x92A475, 0x938341, 0x9460BD, 0x953CF1, 0x9617E2, 0x96F196, 0x97CA11, 0x98A159, 0x997773, 0x9A4C64, 0x9B2031, 0x9BF2DE,
|
||||
0x9CC470, 0x9D94EB, 0x9E6454, 0x9F32AF, 0xA00000, 0xA0CC4A, 0xA19792, 0xA261DC, 0xA32B2A, 0xA3F382, 0xA4BAE6, 0xA5815A, 0xA646E1, 0xA70B7E, 0xA7CF35, 0xA89209,
|
||||
0xA953FD, 0xAA1513, 0xAAD550, 0xAB94B4, 0xAC5345, 0xAD1103, 0xADCDF2, 0xAE8A15, 0xAF456E, 0xB00000, 0xB0B9CC, 0xB172D6, 0xB22B20, 0xB2E2AC, 0xB3997C, 0xB44F93,
|
||||
0xB504F3, 0xB5B99D, 0xB66D95, 0xB720DC, 0xB7D375, 0xB88560, 0xB936A0, 0xB9E738, 0xBA9728, 0xBB4673, 0xBBF51A, 0xBCA320, 0xBD5086, 0xBDFD4E, 0xBEA979, 0xBF5509,
|
||||
0xC00000, 0xC0AA5F, 0xC15428, 0xC1FD5C, 0xC2A5FD, 0xC34E0D, 0xC3F58C, 0xC49C7D, 0xC542E1, 0xC5E8B8, 0xC68E05, 0xC732C9, 0xC7D706, 0xC87ABB, 0xC91DEB, 0xC9C098,
|
||||
0xCA62C1, 0xCB0469, 0xCBA591, 0xCC463A, 0xCCE664, 0xCD8612, 0xCE2544, 0xCEC3FC, 0xCF623A, 0xD00000, 0xD09D4E, 0xD13A26, 0xD1D689, 0xD27277, 0xD30DF3, 0xD3A8FC,
|
||||
0xD44394, 0xD4DDBC, 0xD57774, 0xD610BE, 0xD6A99B, 0xD7420B, 0xD7DA0F, 0xD871A9, 0xD908D8, 0xD99F9F, 0xDA35FE, 0xDACBF5, 0xDB6185, 0xDBF6B0, 0xDC8B76, 0xDD1FD8,
|
||||
0xDDB3D7, 0xDE4773, 0xDEDAAD, 0xDF6D86, 0xE00000, 0xE09219, 0xE123D4, 0xE1B530, 0xE24630, 0xE2D6D2, 0xE36719, 0xE3F704, 0xE48694, 0xE515CB, 0xE5A4A8, 0xE6332D,
|
||||
0xE6C15A, 0xE74F2F, 0xE7DCAD, 0xE869D6, 0xE8F6A9, 0xE98326, 0xEA0F50, 0xEA9B26, 0xEB26A8, 0xEBB1D9, 0xEC3CB7, 0xECC743, 0xED517F, 0xEDDB6A, 0xEE6506, 0xEEEE52,
|
||||
0xEF7750, 0xF00000, 0xF08861, 0xF11076, 0xF1983E, 0xF21FBA, 0xF2A6EA, 0xF32DCF, 0xF3B469, 0xF43AB9, 0xF4C0C0, 0xF5467D, 0xF5CBF2, 0xF6511E, 0xF6D602, 0xF75A9F,
|
||||
0xF7DEF5, 0xF86305, 0xF8E6CE, 0xF96A52, 0xF9ED90, 0xFA708A, 0xFAF33F, 0xFB75B1, 0xFBF7DF, 0xFC79CA, 0xFCFB72, 0xFD7CD8, 0xFDFDFB, 0xFE7EDE, 0xFEFF7F, 0xFF7FDF,
|
||||
0x000000, 0x03243A, 0x064855, 0x096C32, 0x0C8FB2, 0x0FB2B7, 0x12D520, 0x15F6D0, 0x1917A6, 0x1C3785, 0x1F564E, 0x2273E1, 0x259020, 0x28AAED, 0x2BC428, 0x2EDBB3,
|
||||
0x31F170, 0x350540, 0x381704, 0x3B269F, 0x3E33F2, 0x413EE0, 0x444749, 0x474D10, 0x4A5018, 0x4D5043, 0x504D72, 0x534789, 0x563E69, 0x5931F7, 0x5C2214, 0x5F0EA4,
|
||||
0x61F78A, 0x64DCA9, 0x67BDE5, 0x6A9B20, 0x6D7440, 0x704927, 0x7319BA, 0x75E5DD, 0x78AD74, 0x7B7065, 0x7E2E93, 0x80E7E4, 0x839C3C, 0x864B82, 0x88F59A, 0x8B9A6B,
|
||||
0x8E39D9, 0x90D3CC, 0x93682A, 0x95F6D9, 0x987FBF, 0x9B02C5, 0x9D7FD1, 0x9FF6CA, 0xA26799, 0xA4D224, 0xA73655, 0xA99414, 0xABEB49, 0xAE3BDD, 0xB085BA, 0xB2C8C9,
|
||||
0xB504F3, 0xB73A22, 0xB96841, 0xBB8F3A, 0xBDAEF9, 0xBFC767, 0xC1D870, 0xC3E200, 0xC5E403, 0xC7DE65, 0xC9D112, 0xCBBBF7, 0xCD9F02, 0xCF7A1F, 0xD14D3D, 0xD31848,
|
||||
0xD4DB31, 0xD695E4, 0xD84852, 0xD9F269, 0xDB941A, 0xDD2D53, 0xDEBE05, 0xE04621, 0xE1C597, 0xE33C59, 0xE4AA59, 0xE60F87, 0xE76BD7, 0xE8BF3B, 0xEA09A6, 0xEB4B0B,
|
||||
0xEC835E, 0xEDB293, 0xEED89D, 0xEFF573, 0xF10908, 0xF21352, 0xF31447, 0xF40BDD, 0xF4FA0A, 0xF5DEC6, 0xF6BA07, 0xF78BC5, 0xF853F7, 0xF91297, 0xF9C79D, 0xFA7301,
|
||||
0xFB14BE, 0xFBACCD, 0xFC3B27, 0xFCBFC9, 0xFD3AAB, 0xFDABCB, 0xFE1323, 0xFE70AF, 0xFEC46D, 0xFF0E57, 0xFF4E6D, 0xFF84AB, 0xFFB10F, 0xFFD397, 0xFFEC43, 0xFFFB10,
|
||||
0x000000, 0x00A2F9, 0x0145F6, 0x01E8F8, 0x028C01, 0x032F14, 0x03D234, 0x047564, 0x0518A5, 0x05BBFB, 0x065F68, 0x0702EF, 0x07A692, 0x084A54, 0x08EE38, 0x099240,
|
||||
0x0A366E, 0x0ADAC7, 0x0B7F4C, 0x0C2401, 0x0CC8E7, 0x0D6E02, 0x0E1355, 0x0EB8E3, 0x0F5EAE, 0x1004B9, 0x10AB08, 0x11519E, 0x11F87D, 0x129FA9, 0x134725, 0x13EEF4,
|
||||
0x149719, 0x153F99, 0x15E875, 0x1691B2, 0x173B53, 0x17E55C, 0x188FD1, 0x193AB4, 0x19E60A, 0x1A91D8, 0x1B3E20, 0x1BEAE7, 0x1C9831, 0x1D4602, 0x1DF45F, 0x1EA34C,
|
||||
0x1F52CE, 0x2002EA, 0x20B3A3, 0x216500, 0x221705, 0x22C9B8, 0x237D1E, 0x24313C, 0x24E618, 0x259BB9, 0x265224, 0x27095F, 0x27C171, 0x287A61, 0x293436, 0x29EEF6,
|
||||
0x2AAAAA, 0x2B6759, 0x2C250A, 0x2CE3C7, 0x2DA398, 0x2E6485, 0x2F2699, 0x2FE9DC, 0x30AE59, 0x31741B, 0x323B2C, 0x330398, 0x33CD6B, 0x3498B1, 0x356578, 0x3633CE,
|
||||
0x3703C1, 0x37D560, 0x38A8BB, 0x397DE4, 0x3A54EC, 0x3B2DE6, 0x3C08E6, 0x3CE601, 0x3DC54D, 0x3EA6E3, 0x3F8ADC, 0x407152, 0x415A62, 0x42462C, 0x4334D0, 0x442671,
|
||||
0x451B37, 0x46134A, 0x470ED6, 0x480E0C, 0x491120, 0x4A184C, 0x4B23CD, 0x4C33EA, 0x4D48EC, 0x4E6327, 0x4F82F9, 0x50A8C9, 0x51D50A, 0x53083F, 0x5442FC, 0x5585EA,
|
||||
0x56D1CC, 0x582782, 0x598815, 0x5AF4BC, 0x5C6EED, 0x5DF86C, 0x5F9369, 0x6142A3, 0x6309A5, 0x64ED1E, 0x66F381, 0x692617, 0x6B9322, 0x6E52A5, 0x71937C, 0x75CEB4,
|
||||
0x000000, 0x000324, 0x000648, 0x00096D, 0x000C93, 0x000FBA, 0x0012E2, 0x00160B, 0x001936, 0x001C63, 0x001F93, 0x0022C4, 0x0025F9, 0x002930, 0x002C6B, 0x002FA9,
|
||||
0x0032EB, 0x003632, 0x00397C, 0x003CCB, 0x00401F, 0x004379, 0x0046D8, 0x004A3D, 0x004DA8, 0x005119, 0x005492, 0x005811, 0x005B99, 0x005F28, 0x0062C0, 0x006660,
|
||||
0x006A09, 0x006DBC, 0x00717A, 0x007541, 0x007914, 0x007CF2, 0x0080DC, 0x0084D2, 0x0088D5, 0x008CE6, 0x009105, 0x009533, 0x009970, 0x009DBE, 0x00A21C, 0x00A68B,
|
||||
0x00AB0D, 0x00AFA2, 0x00B44B, 0x00B909, 0x00BDDC, 0x00C2C6, 0x00C7C8, 0x00CCE3, 0x00D218, 0x00D767, 0x00DCD3, 0x00E25D, 0x00E806, 0x00EDCF, 0x00F3BB, 0x00F9CA,
|
||||
0x010000, 0x01065C, 0x010CE2, 0x011394, 0x011A73, 0x012183, 0x0128C6, 0x01303E, 0x0137EF, 0x013FDC, 0x014808, 0x015077, 0x01592D, 0x01622D, 0x016B7D, 0x017522,
|
||||
0x017F21, 0x018980, 0x019444, 0x019F76, 0x01AB1C, 0x01B73E, 0x01C3E7, 0x01D11F, 0x01DEF1, 0x01ED69, 0x01FC95, 0x020C83, 0x021D44, 0x022EE9, 0x024186, 0x025533,
|
||||
0x026A09, 0x028025, 0x0297A7, 0x02B0B5, 0x02CB78, 0x02E823, 0x0306EC, 0x032815, 0x034BEB, 0x0372C6, 0x039D10, 0x03CB47, 0x03FE02, 0x0435F7, 0x047405, 0x04B93F,
|
||||
0x0506FF, 0x055EF9, 0x05C35D, 0x063709, 0x06BDCF, 0x075CE6, 0x081B97, 0x09046D, 0x0A2736, 0x0B9CC6, 0x0D8E81, 0x1046E9, 0x145AFF, 0x1B2671, 0x28BC48, 0x517BB5,
|
||||
0xFFFFFF, 0xFFFB10, 0xFFEC43, 0xFFD397, 0xFFB10F, 0xFF84AB, 0xFF4E6D, 0xFF0E57, 0xFEC46D, 0xFE70AF, 0xFE1323, 0xFDABCB, 0xFD3AAB, 0xFCBFC9, 0xFC3B27, 0xFBACCD,
|
||||
0xFB14BE, 0xFA7301, 0xF9C79D, 0xF91297, 0xF853F7, 0xF78BC5, 0xF6BA07, 0xF5DEC6, 0xF4FA0A, 0xF40BDD, 0xF31447, 0xF21352, 0xF10908, 0xEFF573, 0xEED89D, 0xEDB293,
|
||||
0xEC835E, 0xEB4B0B, 0xEA09A6, 0xE8BF3B, 0xE76BD7, 0xE60F87, 0xE4AA59, 0xE33C59, 0xE1C597, 0xE04621, 0xDEBE05, 0xDD2D53, 0xDB941A, 0xD9F269, 0xD84852, 0xD695E4,
|
||||
0xD4DB31, 0xD31848, 0xD14D3D, 0xCF7A1F, 0xCD9F02, 0xCBBBF7, 0xC9D112, 0xC7DE65, 0xC5E403, 0xC3E200, 0xC1D870, 0xBFC767, 0xBDAEF9, 0xBB8F3A, 0xB96841, 0xB73A22,
|
||||
0xB504F3, 0xB2C8C9, 0xB085BA, 0xAE3BDD, 0xABEB49, 0xA99414, 0xA73655, 0xA4D224, 0xA26799, 0x9FF6CA, 0x9D7FD1, 0x9B02C5, 0x987FBF, 0x95F6D9, 0x93682A, 0x90D3CC,
|
||||
0x8E39D9, 0x8B9A6B, 0x88F59A, 0x864B82, 0x839C3C, 0x80E7E4, 0x7E2E93, 0x7B7065, 0x78AD74, 0x75E5DD, 0x7319BA, 0x704927, 0x6D7440, 0x6A9B20, 0x67BDE5, 0x64DCA9,
|
||||
0x61F78A, 0x5F0EA4, 0x5C2214, 0x5931F7, 0x563E69, 0x534789, 0x504D72, 0x4D5043, 0x4A5018, 0x474D10, 0x444749, 0x413EE0, 0x3E33F2, 0x3B269F, 0x381704, 0x350540,
|
||||
0x31F170, 0x2EDBB3, 0x2BC428, 0x28AAED, 0x259020, 0x2273E1, 0x1F564E, 0x1C3785, 0x1917A6, 0x15F6D0, 0x12D520, 0x0FB2B7, 0x0C8FB2, 0x096C32, 0x064855, 0x03243A
|
||||
0xFFFFFF, 0x800000, 0x400000, 0x2AAAAA, 0x200000, 0x199999, 0x155555, 0x124924, 0x100000, 0x0E38E3, 0x0CCCCC,
|
||||
0x0BA2E8, 0x0AAAAA, 0x09D89D, 0x092492, 0x088888,
|
||||
0x080000, 0x078787, 0x071C71, 0x06BCA1, 0x066666, 0x061861, 0x05D174, 0x0590B2, 0x055555, 0x051EB8, 0x04EC4E,
|
||||
0x04BDA1, 0x049249, 0x0469EE, 0x044444, 0x042108,
|
||||
0x040000, 0x03E0F8, 0x03C3C3, 0x03A83A, 0x038E38, 0x03759F, 0x035E50, 0x034834, 0x033333, 0x031F38, 0x030C30,
|
||||
0x02FA0B, 0x02E8BA, 0x02D82D, 0x02C859, 0x02B931,
|
||||
0x02AAAA, 0x029CBC, 0x028F5C, 0x028282, 0x027627, 0x026A43, 0x025ED0, 0x0253C8, 0x024924, 0x023EE0, 0x0234F7,
|
||||
0x022B63, 0x022222, 0x02192E, 0x021084, 0x020820,
|
||||
0x020000, 0x01F81F, 0x01F07C, 0x01E913, 0x01E1E1, 0x01DAE6, 0x01D41D, 0x01CD85, 0x01C71C, 0x01C0E0, 0x01BACF,
|
||||
0x01B4E8, 0x01AF28, 0x01A98E, 0x01A41A, 0x019EC8,
|
||||
0x019999, 0x01948B, 0x018F9C, 0x018ACB, 0x018618, 0x018181, 0x017D05, 0x0178A4, 0x01745D, 0x01702E, 0x016C16,
|
||||
0x016816, 0x01642C, 0x016058, 0x015C98, 0x0158ED,
|
||||
0x015555, 0x0151D0, 0x014E5E, 0x014AFD, 0x0147AE, 0x01446F, 0x014141, 0x013E22, 0x013B13, 0x013813, 0x013521,
|
||||
0x01323E, 0x012F68, 0x012C9F, 0x0129E4, 0x012735,
|
||||
0x012492, 0x0121FB, 0x011F70, 0x011CF0, 0x011A7B, 0x011811, 0x0115B1, 0x01135C, 0x011111, 0x010ECF, 0x010C97,
|
||||
0x010A68, 0x010842, 0x010624, 0x010410, 0x010204,
|
||||
0x010000, 0x00FE03, 0x00FC0F, 0x00FA23, 0x00F83E, 0x00F660, 0x00F489, 0x00F2B9, 0x00F0F0, 0x00EF2E, 0x00ED73,
|
||||
0x00EBBD, 0x00EA0E, 0x00E865, 0x00E6C2, 0x00E525,
|
||||
0x00E38E, 0x00E1FC, 0x00E070, 0x00DEE9, 0x00DD67, 0x00DBEB, 0x00DA74, 0x00D901, 0x00D794, 0x00D62B, 0x00D4C7,
|
||||
0x00D368, 0x00D20D, 0x00D0B6, 0x00CF64, 0x00CE16,
|
||||
0x00CCCC, 0x00CB87, 0x00CA45, 0x00C907, 0x00C7CE, 0x00C698, 0x00C565, 0x00C437, 0x00C30C, 0x00C1E4, 0x00C0C0,
|
||||
0x00BFA0, 0x00BE82, 0x00BD69, 0x00BC52, 0x00BB3E,
|
||||
0x00BA2E, 0x00B921, 0x00B817, 0x00B70F, 0x00B60B, 0x00B509, 0x00B40B, 0x00B30F, 0x00B216, 0x00B11F, 0x00B02C,
|
||||
0x00AF3A, 0x00AE4C, 0x00AD60, 0x00AC76, 0x00AB8F,
|
||||
0x00AAAA, 0x00A9C8, 0x00A8E8, 0x00A80A, 0x00A72F, 0x00A655, 0x00A57E, 0x00A4A9, 0x00A3D7, 0x00A306, 0x00A237,
|
||||
0x00A16B, 0x00A0A0, 0x009FD8, 0x009F11, 0x009E4C,
|
||||
0x009D89, 0x009CC8, 0x009C09, 0x009B4C, 0x009A90, 0x0099D7, 0x00991F, 0x009868, 0x0097B4, 0x009701, 0x00964F,
|
||||
0x0095A0, 0x0094F2, 0x009445, 0x00939A, 0x0092F1,
|
||||
0x009249, 0x0091A2, 0x0090FD, 0x00905A, 0x008FB8, 0x008F17, 0x008E78, 0x008DDA, 0x008D3D, 0x008CA2, 0x008C08,
|
||||
0x008B70, 0x008AD8, 0x008A42, 0x0089AE, 0x00891A,
|
||||
0x008888, 0x0087F7, 0x008767, 0x0086D9, 0x00864B, 0x0085BF, 0x008534, 0x0084A9, 0x008421, 0x008399, 0x008312,
|
||||
0x00828C, 0x008208, 0x008184, 0x008102, 0x008080,
|
||||
0x000000, 0x100000, 0x16A09E, 0x1BB67A, 0x200000, 0x23C6EF, 0x27311C, 0x2A54FF, 0x2D413C, 0x300000, 0x3298B0,
|
||||
0x3510E5, 0x376CF5, 0x39B056, 0x3BDDD4, 0x3DF7BD,
|
||||
0x400000, 0x41F83D, 0x43E1DB, 0x45BE0C, 0x478DDE, 0x49523A, 0x4B0BF1, 0x4CBBB9, 0x4E6238, 0x500000, 0x519595,
|
||||
0x532370, 0x54A9FE, 0x5629A2, 0x57A2B7, 0x591590,
|
||||
0x5A8279, 0x5BE9BA, 0x5D4B94, 0x5EA843, 0x600000, 0x6152FE, 0x62A170, 0x63EB83, 0x653160, 0x667332, 0x67B11D,
|
||||
0x68EB44, 0x6A21CA, 0x6B54CD, 0x6C846C, 0x6DB0C2,
|
||||
0x6ED9EB, 0x700000, 0x712318, 0x72434A, 0x7360AD, 0x747B54, 0x759354, 0x76A8BF, 0x77BBA8, 0x78CC1F, 0x79DA34,
|
||||
0x7AE5F9, 0x7BEF7A, 0x7CF6C8, 0x7DFBEF, 0x7EFEFD,
|
||||
0x800000, 0x80FF01, 0x81FC0F, 0x82F734, 0x83F07B, 0x84E7EE, 0x85DD98, 0x86D182, 0x87C3B6, 0x88B43D, 0x89A31F,
|
||||
0x8A9066, 0x8B7C19, 0x8C6641, 0x8D4EE4, 0x8E360B,
|
||||
0x8F1BBC, 0x900000, 0x90E2DB, 0x91C456, 0x92A475, 0x938341, 0x9460BD, 0x953CF1, 0x9617E2, 0x96F196, 0x97CA11,
|
||||
0x98A159, 0x997773, 0x9A4C64, 0x9B2031, 0x9BF2DE,
|
||||
0x9CC470, 0x9D94EB, 0x9E6454, 0x9F32AF, 0xA00000, 0xA0CC4A, 0xA19792, 0xA261DC, 0xA32B2A, 0xA3F382, 0xA4BAE6,
|
||||
0xA5815A, 0xA646E1, 0xA70B7E, 0xA7CF35, 0xA89209,
|
||||
0xA953FD, 0xAA1513, 0xAAD550, 0xAB94B4, 0xAC5345, 0xAD1103, 0xADCDF2, 0xAE8A15, 0xAF456E, 0xB00000, 0xB0B9CC,
|
||||
0xB172D6, 0xB22B20, 0xB2E2AC, 0xB3997C, 0xB44F93,
|
||||
0xB504F3, 0xB5B99D, 0xB66D95, 0xB720DC, 0xB7D375, 0xB88560, 0xB936A0, 0xB9E738, 0xBA9728, 0xBB4673, 0xBBF51A,
|
||||
0xBCA320, 0xBD5086, 0xBDFD4E, 0xBEA979, 0xBF5509,
|
||||
0xC00000, 0xC0AA5F, 0xC15428, 0xC1FD5C, 0xC2A5FD, 0xC34E0D, 0xC3F58C, 0xC49C7D, 0xC542E1, 0xC5E8B8, 0xC68E05,
|
||||
0xC732C9, 0xC7D706, 0xC87ABB, 0xC91DEB, 0xC9C098,
|
||||
0xCA62C1, 0xCB0469, 0xCBA591, 0xCC463A, 0xCCE664, 0xCD8612, 0xCE2544, 0xCEC3FC, 0xCF623A, 0xD00000, 0xD09D4E,
|
||||
0xD13A26, 0xD1D689, 0xD27277, 0xD30DF3, 0xD3A8FC,
|
||||
0xD44394, 0xD4DDBC, 0xD57774, 0xD610BE, 0xD6A99B, 0xD7420B, 0xD7DA0F, 0xD871A9, 0xD908D8, 0xD99F9F, 0xDA35FE,
|
||||
0xDACBF5, 0xDB6185, 0xDBF6B0, 0xDC8B76, 0xDD1FD8,
|
||||
0xDDB3D7, 0xDE4773, 0xDEDAAD, 0xDF6D86, 0xE00000, 0xE09219, 0xE123D4, 0xE1B530, 0xE24630, 0xE2D6D2, 0xE36719,
|
||||
0xE3F704, 0xE48694, 0xE515CB, 0xE5A4A8, 0xE6332D,
|
||||
0xE6C15A, 0xE74F2F, 0xE7DCAD, 0xE869D6, 0xE8F6A9, 0xE98326, 0xEA0F50, 0xEA9B26, 0xEB26A8, 0xEBB1D9, 0xEC3CB7,
|
||||
0xECC743, 0xED517F, 0xEDDB6A, 0xEE6506, 0xEEEE52,
|
||||
0xEF7750, 0xF00000, 0xF08861, 0xF11076, 0xF1983E, 0xF21FBA, 0xF2A6EA, 0xF32DCF, 0xF3B469, 0xF43AB9, 0xF4C0C0,
|
||||
0xF5467D, 0xF5CBF2, 0xF6511E, 0xF6D602, 0xF75A9F,
|
||||
0xF7DEF5, 0xF86305, 0xF8E6CE, 0xF96A52, 0xF9ED90, 0xFA708A, 0xFAF33F, 0xFB75B1, 0xFBF7DF, 0xFC79CA, 0xFCFB72,
|
||||
0xFD7CD8, 0xFDFDFB, 0xFE7EDE, 0xFEFF7F, 0xFF7FDF,
|
||||
0x000000, 0x03243A, 0x064855, 0x096C32, 0x0C8FB2, 0x0FB2B7, 0x12D520, 0x15F6D0, 0x1917A6, 0x1C3785, 0x1F564E,
|
||||
0x2273E1, 0x259020, 0x28AAED, 0x2BC428, 0x2EDBB3,
|
||||
0x31F170, 0x350540, 0x381704, 0x3B269F, 0x3E33F2, 0x413EE0, 0x444749, 0x474D10, 0x4A5018, 0x4D5043, 0x504D72,
|
||||
0x534789, 0x563E69, 0x5931F7, 0x5C2214, 0x5F0EA4,
|
||||
0x61F78A, 0x64DCA9, 0x67BDE5, 0x6A9B20, 0x6D7440, 0x704927, 0x7319BA, 0x75E5DD, 0x78AD74, 0x7B7065, 0x7E2E93,
|
||||
0x80E7E4, 0x839C3C, 0x864B82, 0x88F59A, 0x8B9A6B,
|
||||
0x8E39D9, 0x90D3CC, 0x93682A, 0x95F6D9, 0x987FBF, 0x9B02C5, 0x9D7FD1, 0x9FF6CA, 0xA26799, 0xA4D224, 0xA73655,
|
||||
0xA99414, 0xABEB49, 0xAE3BDD, 0xB085BA, 0xB2C8C9,
|
||||
0xB504F3, 0xB73A22, 0xB96841, 0xBB8F3A, 0xBDAEF9, 0xBFC767, 0xC1D870, 0xC3E200, 0xC5E403, 0xC7DE65, 0xC9D112,
|
||||
0xCBBBF7, 0xCD9F02, 0xCF7A1F, 0xD14D3D, 0xD31848,
|
||||
0xD4DB31, 0xD695E4, 0xD84852, 0xD9F269, 0xDB941A, 0xDD2D53, 0xDEBE05, 0xE04621, 0xE1C597, 0xE33C59, 0xE4AA59,
|
||||
0xE60F87, 0xE76BD7, 0xE8BF3B, 0xEA09A6, 0xEB4B0B,
|
||||
0xEC835E, 0xEDB293, 0xEED89D, 0xEFF573, 0xF10908, 0xF21352, 0xF31447, 0xF40BDD, 0xF4FA0A, 0xF5DEC6, 0xF6BA07,
|
||||
0xF78BC5, 0xF853F7, 0xF91297, 0xF9C79D, 0xFA7301,
|
||||
0xFB14BE, 0xFBACCD, 0xFC3B27, 0xFCBFC9, 0xFD3AAB, 0xFDABCB, 0xFE1323, 0xFE70AF, 0xFEC46D, 0xFF0E57, 0xFF4E6D,
|
||||
0xFF84AB, 0xFFB10F, 0xFFD397, 0xFFEC43, 0xFFFB10,
|
||||
0x000000, 0x00A2F9, 0x0145F6, 0x01E8F8, 0x028C01, 0x032F14, 0x03D234, 0x047564, 0x0518A5, 0x05BBFB, 0x065F68,
|
||||
0x0702EF, 0x07A692, 0x084A54, 0x08EE38, 0x099240,
|
||||
0x0A366E, 0x0ADAC7, 0x0B7F4C, 0x0C2401, 0x0CC8E7, 0x0D6E02, 0x0E1355, 0x0EB8E3, 0x0F5EAE, 0x1004B9, 0x10AB08,
|
||||
0x11519E, 0x11F87D, 0x129FA9, 0x134725, 0x13EEF4,
|
||||
0x149719, 0x153F99, 0x15E875, 0x1691B2, 0x173B53, 0x17E55C, 0x188FD1, 0x193AB4, 0x19E60A, 0x1A91D8, 0x1B3E20,
|
||||
0x1BEAE7, 0x1C9831, 0x1D4602, 0x1DF45F, 0x1EA34C,
|
||||
0x1F52CE, 0x2002EA, 0x20B3A3, 0x216500, 0x221705, 0x22C9B8, 0x237D1E, 0x24313C, 0x24E618, 0x259BB9, 0x265224,
|
||||
0x27095F, 0x27C171, 0x287A61, 0x293436, 0x29EEF6,
|
||||
0x2AAAAA, 0x2B6759, 0x2C250A, 0x2CE3C7, 0x2DA398, 0x2E6485, 0x2F2699, 0x2FE9DC, 0x30AE59, 0x31741B, 0x323B2C,
|
||||
0x330398, 0x33CD6B, 0x3498B1, 0x356578, 0x3633CE,
|
||||
0x3703C1, 0x37D560, 0x38A8BB, 0x397DE4, 0x3A54EC, 0x3B2DE6, 0x3C08E6, 0x3CE601, 0x3DC54D, 0x3EA6E3, 0x3F8ADC,
|
||||
0x407152, 0x415A62, 0x42462C, 0x4334D0, 0x442671,
|
||||
0x451B37, 0x46134A, 0x470ED6, 0x480E0C, 0x491120, 0x4A184C, 0x4B23CD, 0x4C33EA, 0x4D48EC, 0x4E6327, 0x4F82F9,
|
||||
0x50A8C9, 0x51D50A, 0x53083F, 0x5442FC, 0x5585EA,
|
||||
0x56D1CC, 0x582782, 0x598815, 0x5AF4BC, 0x5C6EED, 0x5DF86C, 0x5F9369, 0x6142A3, 0x6309A5, 0x64ED1E, 0x66F381,
|
||||
0x692617, 0x6B9322, 0x6E52A5, 0x71937C, 0x75CEB4,
|
||||
0x000000, 0x000324, 0x000648, 0x00096D, 0x000C93, 0x000FBA, 0x0012E2, 0x00160B, 0x001936, 0x001C63, 0x001F93,
|
||||
0x0022C4, 0x0025F9, 0x002930, 0x002C6B, 0x002FA9,
|
||||
0x0032EB, 0x003632, 0x00397C, 0x003CCB, 0x00401F, 0x004379, 0x0046D8, 0x004A3D, 0x004DA8, 0x005119, 0x005492,
|
||||
0x005811, 0x005B99, 0x005F28, 0x0062C0, 0x006660,
|
||||
0x006A09, 0x006DBC, 0x00717A, 0x007541, 0x007914, 0x007CF2, 0x0080DC, 0x0084D2, 0x0088D5, 0x008CE6, 0x009105,
|
||||
0x009533, 0x009970, 0x009DBE, 0x00A21C, 0x00A68B,
|
||||
0x00AB0D, 0x00AFA2, 0x00B44B, 0x00B909, 0x00BDDC, 0x00C2C6, 0x00C7C8, 0x00CCE3, 0x00D218, 0x00D767, 0x00DCD3,
|
||||
0x00E25D, 0x00E806, 0x00EDCF, 0x00F3BB, 0x00F9CA,
|
||||
0x010000, 0x01065C, 0x010CE2, 0x011394, 0x011A73, 0x012183, 0x0128C6, 0x01303E, 0x0137EF, 0x013FDC, 0x014808,
|
||||
0x015077, 0x01592D, 0x01622D, 0x016B7D, 0x017522,
|
||||
0x017F21, 0x018980, 0x019444, 0x019F76, 0x01AB1C, 0x01B73E, 0x01C3E7, 0x01D11F, 0x01DEF1, 0x01ED69, 0x01FC95,
|
||||
0x020C83, 0x021D44, 0x022EE9, 0x024186, 0x025533,
|
||||
0x026A09, 0x028025, 0x0297A7, 0x02B0B5, 0x02CB78, 0x02E823, 0x0306EC, 0x032815, 0x034BEB, 0x0372C6, 0x039D10,
|
||||
0x03CB47, 0x03FE02, 0x0435F7, 0x047405, 0x04B93F,
|
||||
0x0506FF, 0x055EF9, 0x05C35D, 0x063709, 0x06BDCF, 0x075CE6, 0x081B97, 0x09046D, 0x0A2736, 0x0B9CC6, 0x0D8E81,
|
||||
0x1046E9, 0x145AFF, 0x1B2671, 0x28BC48, 0x517BB5,
|
||||
0xFFFFFF, 0xFFFB10, 0xFFEC43, 0xFFD397, 0xFFB10F, 0xFF84AB, 0xFF4E6D, 0xFF0E57, 0xFEC46D, 0xFE70AF, 0xFE1323,
|
||||
0xFDABCB, 0xFD3AAB, 0xFCBFC9, 0xFC3B27, 0xFBACCD,
|
||||
0xFB14BE, 0xFA7301, 0xF9C79D, 0xF91297, 0xF853F7, 0xF78BC5, 0xF6BA07, 0xF5DEC6, 0xF4FA0A, 0xF40BDD, 0xF31447,
|
||||
0xF21352, 0xF10908, 0xEFF573, 0xEED89D, 0xEDB293,
|
||||
0xEC835E, 0xEB4B0B, 0xEA09A6, 0xE8BF3B, 0xE76BD7, 0xE60F87, 0xE4AA59, 0xE33C59, 0xE1C597, 0xE04621, 0xDEBE05,
|
||||
0xDD2D53, 0xDB941A, 0xD9F269, 0xD84852, 0xD695E4,
|
||||
0xD4DB31, 0xD31848, 0xD14D3D, 0xCF7A1F, 0xCD9F02, 0xCBBBF7, 0xC9D112, 0xC7DE65, 0xC5E403, 0xC3E200, 0xC1D870,
|
||||
0xBFC767, 0xBDAEF9, 0xBB8F3A, 0xB96841, 0xB73A22,
|
||||
0xB504F3, 0xB2C8C9, 0xB085BA, 0xAE3BDD, 0xABEB49, 0xA99414, 0xA73655, 0xA4D224, 0xA26799, 0x9FF6CA, 0x9D7FD1,
|
||||
0x9B02C5, 0x987FBF, 0x95F6D9, 0x93682A, 0x90D3CC,
|
||||
0x8E39D9, 0x8B9A6B, 0x88F59A, 0x864B82, 0x839C3C, 0x80E7E4, 0x7E2E93, 0x7B7065, 0x78AD74, 0x75E5DD, 0x7319BA,
|
||||
0x704927, 0x6D7440, 0x6A9B20, 0x67BDE5, 0x64DCA9,
|
||||
0x61F78A, 0x5F0EA4, 0x5C2214, 0x5931F7, 0x563E69, 0x534789, 0x504D72, 0x4D5043, 0x4A5018, 0x474D10, 0x444749,
|
||||
0x413EE0, 0x3E33F2, 0x3B269F, 0x381704, 0x350540,
|
||||
0x31F170, 0x2EDBB3, 0x2BC428, 0x28AAED, 0x259020, 0x2273E1, 0x1F564E, 0x1C3785, 0x1917A6, 0x15F6D0, 0x12D520,
|
||||
0x0FB2B7, 0x0C8FB2, 0x096C32, 0x064855, 0x03243A
|
||||
};
|
||||
|
|
455
Core/Cx4.cpp
455
Core/Cx4.cpp
|
@ -19,11 +19,11 @@ Cx4::Cx4(Console* console) : BaseCoprocessor(SnesMemoryType::Register)
|
|||
_memoryType = SnesMemoryType::Register;
|
||||
_memoryManager = console->GetMemoryManager().get();
|
||||
_cpu = console->GetCpu().get();
|
||||
|
||||
|
||||
console->GetSettings()->InitializeRam(_dataRam, Cx4::DataRamSize);
|
||||
|
||||
auto &prgRomHandlers = console->GetCartridge()->GetPrgRomHandlers();
|
||||
auto &saveRamHandlers = console->GetCartridge()->GetSaveRamHandlers();
|
||||
|
||||
auto& prgRomHandlers = console->GetCartridge()->GetPrgRomHandlers();
|
||||
auto& saveRamHandlers = console->GetCartridge()->GetSaveRamHandlers();
|
||||
MemoryMappings* cpuMappings = _memoryManager->GetMemoryMappings();
|
||||
|
||||
//PRG ROM
|
||||
|
@ -62,36 +62,56 @@ void Cx4::Run()
|
|||
{
|
||||
uint64_t targetCycle = (uint64_t)(_memoryManager->GetMasterClock() * _clockRatio);
|
||||
|
||||
while(_state.CycleCount < targetCycle) {
|
||||
if(_state.Locked) {
|
||||
while (_state.CycleCount < targetCycle)
|
||||
{
|
||||
if (_state.Locked)
|
||||
{
|
||||
Step(1);
|
||||
} else if(_state.Suspend.Enabled) {
|
||||
if(_state.Suspend.Duration == 0) {
|
||||
}
|
||||
else if (_state.Suspend.Enabled)
|
||||
{
|
||||
if (_state.Suspend.Duration == 0)
|
||||
{
|
||||
Step(1);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Step(1);
|
||||
_state.Suspend.Duration--;
|
||||
if(_state.Suspend.Duration == 0) {
|
||||
if (_state.Suspend.Duration == 0)
|
||||
{
|
||||
_state.Suspend.Enabled = false;
|
||||
}
|
||||
}
|
||||
} else if(_state.Cache.Enabled) {
|
||||
}
|
||||
else if (_state.Cache.Enabled)
|
||||
{
|
||||
ProcessCache(targetCycle);
|
||||
} else if(_state.Dma.Enabled) {
|
||||
}
|
||||
else if (_state.Dma.Enabled)
|
||||
{
|
||||
ProcessDma(targetCycle);
|
||||
} else if(_state.Stopped) {
|
||||
}
|
||||
else if (_state.Stopped)
|
||||
{
|
||||
Step(targetCycle - _state.CycleCount);
|
||||
} else if(!ProcessCache(targetCycle)) {
|
||||
if(!_state.Cache.Enabled) {
|
||||
}
|
||||
else if (!ProcessCache(targetCycle))
|
||||
{
|
||||
if (!_state.Cache.Enabled)
|
||||
{
|
||||
//Cache operation required, but both caches are locked, stop
|
||||
Stop();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t opCode = _prgRam[_state.Cache.Page][_state.PC];
|
||||
_console->ProcessMemoryRead<CpuType::Cx4>(0, 0, MemoryOperationType::ExecOpCode);
|
||||
_state.PC++;
|
||||
|
||||
if(_state.PC == 0) {
|
||||
|
||||
if (_state.PC == 0)
|
||||
{
|
||||
//If execution reached the end of the page, start loading the next page
|
||||
//This must be done BEFORE running the instruction (otherwise a jump/branch to address 0 will trigger this)
|
||||
SwitchCachePage();
|
||||
|
@ -104,19 +124,25 @@ void Cx4::Run()
|
|||
|
||||
void Cx4::Step(uint64_t cycles)
|
||||
{
|
||||
if(_state.Bus.Enabled) {
|
||||
if(_state.Bus.DelayCycles > cycles) {
|
||||
if (_state.Bus.Enabled)
|
||||
{
|
||||
if (_state.Bus.DelayCycles > cycles)
|
||||
{
|
||||
_state.Bus.DelayCycles -= (uint8_t)cycles;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_state.Bus.Enabled = false;
|
||||
_state.Bus.DelayCycles = 0;
|
||||
|
||||
if(_state.Bus.Reading) {
|
||||
if (_state.Bus.Reading)
|
||||
{
|
||||
_state.MemoryDataReg = ReadCx4(_state.Bus.Address);
|
||||
_state.Bus.Reading = false;
|
||||
}
|
||||
|
||||
if(_state.Bus.Writing) {
|
||||
if (_state.Bus.Writing)
|
||||
{
|
||||
WriteCx4(_state.Bus.Address, _state.MemoryDataReg);
|
||||
_state.Bus.Writing = false;
|
||||
}
|
||||
|
@ -128,21 +154,24 @@ void Cx4::Step(uint64_t cycles)
|
|||
|
||||
void Cx4::SwitchCachePage()
|
||||
{
|
||||
if(_state.Cache.Page == 1) {
|
||||
if (_state.Cache.Page == 1)
|
||||
{
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
_state.Cache.Page = 1;
|
||||
if(_state.Cache.Lock[1]) {
|
||||
if (_state.Cache.Lock[1])
|
||||
{
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_state.PB = _state.P;
|
||||
|
||||
uint64_t targetCycle = (uint64_t)(_memoryManager->GetMasterClock() * _clockRatio);
|
||||
if(!ProcessCache(targetCycle) && !_state.Cache.Enabled) {
|
||||
if (!ProcessCache(targetCycle) && !_state.Cache.Enabled)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
|
@ -151,8 +180,10 @@ bool Cx4::ProcessCache(uint64_t targetCycle)
|
|||
{
|
||||
uint32_t address = (_state.Cache.Base + (_state.PB << 9)) & 0xFFFFFF;
|
||||
|
||||
if(_state.Cache.Pos == 0) {
|
||||
if(_state.Cache.Address[_state.Cache.Page] == address) {
|
||||
if (_state.Cache.Pos == 0)
|
||||
{
|
||||
if (_state.Cache.Address[_state.Cache.Page] == address)
|
||||
{
|
||||
//Current cache page matches the needed address, keep using it
|
||||
_state.Cache.Enabled = false;
|
||||
return true;
|
||||
|
@ -161,28 +192,32 @@ bool Cx4::ProcessCache(uint64_t targetCycle)
|
|||
//Check if the other page matches
|
||||
_state.Cache.Page ^= 1;
|
||||
|
||||
if(_state.Cache.Address[_state.Cache.Page] == address) {
|
||||
if (_state.Cache.Address[_state.Cache.Page] == address)
|
||||
{
|
||||
//The other cache page matches, use it
|
||||
_state.Cache.Enabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(_state.Cache.Lock[_state.Cache.Page]) {
|
||||
if (_state.Cache.Lock[_state.Cache.Page])
|
||||
{
|
||||
//If it's locked, use the other page
|
||||
_state.Cache.Page ^= 1;
|
||||
}
|
||||
|
||||
if(_state.Cache.Lock[_state.Cache.Page]) {
|
||||
if (_state.Cache.Lock[_state.Cache.Page])
|
||||
{
|
||||
//The both pages are locked, and the cache is invalid, give up.
|
||||
_state.Cache.Enabled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
_state.Cache.Enabled = true;
|
||||
}
|
||||
|
||||
//Populate the cache
|
||||
while(_state.Cache.Pos < 256) {
|
||||
while (_state.Cache.Pos < 256)
|
||||
{
|
||||
uint8_t lsb = ReadCx4(address + (_state.Cache.Pos * 2));
|
||||
Step(GetAccessDelay(address + (_state.Cache.Pos * 2)));
|
||||
|
||||
|
@ -192,12 +227,14 @@ bool Cx4::ProcessCache(uint64_t targetCycle)
|
|||
_prgRam[_state.Cache.Page][_state.Cache.Pos] = (msb << 8) | lsb;
|
||||
_state.Cache.Pos++;
|
||||
|
||||
if(_state.CycleCount > targetCycle) {
|
||||
if (_state.CycleCount > targetCycle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_state.Cache.Pos >= 256) {
|
||||
if (_state.Cache.Pos >= 256)
|
||||
{
|
||||
_state.Cache.Address[_state.Cache.Page] = address;
|
||||
_state.Cache.Pos = 0;
|
||||
_state.Cache.Enabled = false;
|
||||
|
@ -210,13 +247,16 @@ bool Cx4::ProcessCache(uint64_t targetCycle)
|
|||
|
||||
void Cx4::ProcessDma(uint64_t targetCycle)
|
||||
{
|
||||
while(_state.Dma.Pos < _state.Dma.Length) {
|
||||
while (_state.Dma.Pos < _state.Dma.Length)
|
||||
{
|
||||
uint32_t src = (_state.Dma.Source + _state.Dma.Pos) & 0xFFFFFF;
|
||||
uint32_t dest = (_state.Dma.Dest + _state.Dma.Pos) & 0xFFFFFF;
|
||||
|
||||
IMemoryHandler *srcHandler = _mappings.GetHandler(src);
|
||||
IMemoryHandler *destHandler = _mappings.GetHandler(dest);
|
||||
if(!srcHandler || !destHandler || srcHandler->GetMemoryType() == destHandler->GetMemoryType() || destHandler->GetMemoryType() == SnesMemoryType::PrgRom) {
|
||||
IMemoryHandler* srcHandler = _mappings.GetHandler(src);
|
||||
IMemoryHandler* destHandler = _mappings.GetHandler(dest);
|
||||
if (!srcHandler || !destHandler || srcHandler->GetMemoryType() == destHandler->GetMemoryType() || destHandler->
|
||||
GetMemoryType() == SnesMemoryType::PrgRom)
|
||||
{
|
||||
//Invalid DMA, the chip is locked until it gets restarted by a write to $7F53
|
||||
_state.Locked = true;
|
||||
_state.Dma.Pos = 0;
|
||||
|
@ -231,12 +271,14 @@ void Cx4::ProcessDma(uint64_t targetCycle)
|
|||
WriteCx4(dest, value);
|
||||
_state.Dma.Pos++;
|
||||
|
||||
if(_state.CycleCount > targetCycle) {
|
||||
if (_state.CycleCount > targetCycle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(_state.Dma.Pos >= _state.Dma.Length) {
|
||||
if (_state.Dma.Pos >= _state.Dma.Length)
|
||||
{
|
||||
_state.Dma.Pos = 0;
|
||||
_state.Dma.Enabled = false;
|
||||
}
|
||||
|
@ -245,9 +287,12 @@ void Cx4::ProcessDma(uint64_t targetCycle)
|
|||
uint8_t Cx4::GetAccessDelay(uint32_t addr)
|
||||
{
|
||||
IMemoryHandler* handler = _mappings.GetHandler(addr);
|
||||
if(handler->GetMemoryType() == SnesMemoryType::PrgRom) {
|
||||
if (handler->GetMemoryType() == SnesMemoryType::PrgRom)
|
||||
{
|
||||
return 1 + _state.RomAccessDelay;
|
||||
} else if(handler->GetMemoryType() == SnesMemoryType::SaveRam) {
|
||||
}
|
||||
else if (handler->GetMemoryType() == SnesMemoryType::SaveRam)
|
||||
{
|
||||
return 1 + _state.RamAccessDelay;
|
||||
}
|
||||
|
||||
|
@ -257,7 +302,8 @@ uint8_t Cx4::GetAccessDelay(uint32_t addr)
|
|||
uint8_t Cx4::ReadCx4(uint32_t addr)
|
||||
{
|
||||
IMemoryHandler* handler = _mappings.GetHandler(addr);
|
||||
if(handler) {
|
||||
if (handler)
|
||||
{
|
||||
uint8_t value = handler->Read(addr);
|
||||
_console->ProcessMemoryRead<CpuType::Cx4>(addr, value, MemoryOperationType::Read);
|
||||
return value;
|
||||
|
@ -268,7 +314,8 @@ uint8_t Cx4::ReadCx4(uint32_t addr)
|
|||
void Cx4::WriteCx4(uint32_t addr, uint8_t value)
|
||||
{
|
||||
IMemoryHandler* handler = _mappings.GetHandler(addr);
|
||||
if(handler) {
|
||||
if (handler)
|
||||
{
|
||||
_console->ProcessMemoryWrite<CpuType::Cx4>(addr, value, MemoryOperationType::Write);
|
||||
handler->Write(addr, value);
|
||||
}
|
||||
|
@ -277,42 +324,52 @@ void Cx4::WriteCx4(uint32_t addr, uint8_t value)
|
|||
uint8_t Cx4::Read(uint32_t addr)
|
||||
{
|
||||
addr = 0x7000 | (addr & 0xFFF);
|
||||
if(addr <= 0x7BFF) {
|
||||
if (addr <= 0x7BFF)
|
||||
{
|
||||
return _dataRam[addr & 0xFFF];
|
||||
} else if(addr >= 0x7F60 && addr <= 0x7F7F) {
|
||||
}
|
||||
else if (addr >= 0x7F60 && addr <= 0x7F7F)
|
||||
{
|
||||
return _state.Vectors[addr & 0x1F];
|
||||
} else if((addr >= 0x7F80 && addr <= 0x7FAF) || (addr >= 0x7FC0 && addr <= 0x7FEF)) {
|
||||
}
|
||||
else if ((addr >= 0x7F80 && addr <= 0x7FAF) || (addr >= 0x7FC0 && addr <= 0x7FEF))
|
||||
{
|
||||
addr &= 0x3F;
|
||||
uint32_t ® = _state.Regs[addr / 3];
|
||||
switch(addr % 3) {
|
||||
case 0: return reg;
|
||||
case 1: return reg >> 8;
|
||||
case 2: return reg >> 16;
|
||||
uint32_t& reg = _state.Regs[addr / 3];
|
||||
switch (addr % 3)
|
||||
{
|
||||
case 0: return reg;
|
||||
case 1: return reg >> 8;
|
||||
case 2: return reg >> 16;
|
||||
}
|
||||
} else if(addr >= 0x7F53 && addr <= 0x7F5F) {
|
||||
return (uint8_t)_state.Suspend.Enabled | ((uint8_t)_state.IrqFlag << 1) | ((uint8_t)IsRunning() << 6) | ((uint8_t)IsBusy() << 7);
|
||||
}
|
||||
else if (addr >= 0x7F53 && addr <= 0x7F5F)
|
||||
{
|
||||
return (uint8_t)_state.Suspend.Enabled | ((uint8_t)_state.IrqFlag << 1) | ((uint8_t)IsRunning() << 6) | ((uint8_t)
|
||||
IsBusy() << 7);
|
||||
}
|
||||
|
||||
switch(addr) {
|
||||
case 0x7F40: return _state.Dma.Source;
|
||||
case 0x7F41: return _state.Dma.Source >> 8;
|
||||
case 0x7F42: return _state.Dma.Source >> 16;
|
||||
case 0x7F43: return (uint8_t)_state.Dma.Length;
|
||||
case 0x7F44: return _state.Dma.Length >> 8;
|
||||
case 0x7F45: return _state.Dma.Dest;
|
||||
case 0x7F46: return _state.Dma.Dest >> 8;
|
||||
case 0x7F47: return _state.Dma.Dest >> 16;
|
||||
case 0x7F48: return _state.Cache.Page;
|
||||
case 0x7F49: return _state.Cache.Base;
|
||||
case 0x7F4A: return _state.Cache.Base >> 8;
|
||||
case 0x7F4B: return _state.Cache.Base >> 16;
|
||||
case 0x7F4C: return (uint8_t)_state.Cache.Lock[0] | ((uint8_t)_state.Cache.Lock[1] << 1);
|
||||
case 0x7F4D: return (uint8_t)_state.Cache.ProgramBank;
|
||||
case 0x7F4E: return _state.Cache.ProgramBank >> 8;
|
||||
case 0x7F4F: return _state.Cache.ProgramCounter;
|
||||
case 0x7F50: return _state.RamAccessDelay | (_state.RomAccessDelay << 4);
|
||||
case 0x7F51: return _state.IrqDisabled;
|
||||
case 0x7F52: return _state.SingleRom;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x7F40: return _state.Dma.Source;
|
||||
case 0x7F41: return _state.Dma.Source >> 8;
|
||||
case 0x7F42: return _state.Dma.Source >> 16;
|
||||
case 0x7F43: return (uint8_t)_state.Dma.Length;
|
||||
case 0x7F44: return _state.Dma.Length >> 8;
|
||||
case 0x7F45: return _state.Dma.Dest;
|
||||
case 0x7F46: return _state.Dma.Dest >> 8;
|
||||
case 0x7F47: return _state.Dma.Dest >> 16;
|
||||
case 0x7F48: return _state.Cache.Page;
|
||||
case 0x7F49: return _state.Cache.Base;
|
||||
case 0x7F4A: return _state.Cache.Base >> 8;
|
||||
case 0x7F4B: return _state.Cache.Base >> 16;
|
||||
case 0x7F4C: return (uint8_t)_state.Cache.Lock[0] | ((uint8_t)_state.Cache.Lock[1] << 1);
|
||||
case 0x7F4D: return (uint8_t)_state.Cache.ProgramBank;
|
||||
case 0x7F4E: return _state.Cache.ProgramBank >> 8;
|
||||
case 0x7F4F: return _state.Cache.ProgramCounter;
|
||||
case 0x7F50: return _state.RamAccessDelay | (_state.RomAccessDelay << 4);
|
||||
case 0x7F51: return _state.IrqDisabled;
|
||||
case 0x7F52: return _state.SingleRom;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -322,94 +379,125 @@ void Cx4::Write(uint32_t addr, uint8_t value)
|
|||
{
|
||||
addr = 0x7000 | (addr & 0xFFF);
|
||||
|
||||
if(addr <= 0x7BFF) {
|
||||
if (addr <= 0x7BFF)
|
||||
{
|
||||
_dataRam[addr & 0xFFF] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr >= 0x7F60 && addr <= 0x7F7F) {
|
||||
}
|
||||
|
||||
if (addr >= 0x7F60 && addr <= 0x7F7F)
|
||||
{
|
||||
_state.Vectors[addr & 0x1F] = value;
|
||||
} else if((addr >= 0x7F80 && addr <= 0x7FAF) || (addr >= 0x7FC0 && addr <= 0x7FEF)) {
|
||||
}
|
||||
else if ((addr >= 0x7F80 && addr <= 0x7FAF) || (addr >= 0x7FC0 && addr <= 0x7FEF))
|
||||
{
|
||||
addr &= 0x3F;
|
||||
uint32_t ® = _state.Regs[addr / 3];
|
||||
switch(addr % 3) {
|
||||
case 0: reg = (reg & 0xFFFF00) | value; break;
|
||||
case 1: reg = (reg & 0xFF00FF) | (value << 8); break;
|
||||
case 2: reg = (reg & 0x00FFFF) | (value << 16); break;
|
||||
uint32_t& reg = _state.Regs[addr / 3];
|
||||
switch (addr % 3)
|
||||
{
|
||||
case 0: reg = (reg & 0xFFFF00) | value;
|
||||
break;
|
||||
case 1: reg = (reg & 0xFF00FF) | (value << 8);
|
||||
break;
|
||||
case 2: reg = (reg & 0x00FFFF) | (value << 16);
|
||||
break;
|
||||
}
|
||||
} else if(addr >= 0x7F55 && addr <= 0x7F5C) {
|
||||
}
|
||||
else if (addr >= 0x7F55 && addr <= 0x7F5C)
|
||||
{
|
||||
_state.Suspend.Enabled = true;
|
||||
_state.Suspend.Duration = (addr - 0x7F55) * 32;
|
||||
} else {
|
||||
switch(addr) {
|
||||
case 0x7F40: _state.Dma.Source = (_state.Dma.Source & 0xFFFF00) | value; break;
|
||||
case 0x7F41: _state.Dma.Source = (_state.Dma.Source & 0xFF00FF) | (value << 8); break;
|
||||
case 0x7F42: _state.Dma.Source = (_state.Dma.Source & 0x00FFFF) | (value << 16); break;
|
||||
case 0x7F43: _state.Dma.Length = (_state.Dma.Length & 0xFF00) | value; break;
|
||||
case 0x7F44: _state.Dma.Length = (_state.Dma.Length & 0x00FF) | (value << 8); break;
|
||||
case 0x7F45: _state.Dma.Dest = (_state.Dma.Dest & 0xFFFF00) | value; break;
|
||||
case 0x7F46: _state.Dma.Dest = (_state.Dma.Dest & 0xFF00FF) | (value << 8); break;
|
||||
case 0x7F47:
|
||||
_state.Dma.Dest = (_state.Dma.Dest & 0x00FFFF) | (value << 16);
|
||||
if(_state.Stopped) {
|
||||
_state.Dma.Enabled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x7F40: _state.Dma.Source = (_state.Dma.Source & 0xFFFF00) | value;
|
||||
break;
|
||||
case 0x7F41: _state.Dma.Source = (_state.Dma.Source & 0xFF00FF) | (value << 8);
|
||||
break;
|
||||
case 0x7F42: _state.Dma.Source = (_state.Dma.Source & 0x00FFFF) | (value << 16);
|
||||
break;
|
||||
case 0x7F43: _state.Dma.Length = (_state.Dma.Length & 0xFF00) | value;
|
||||
break;
|
||||
case 0x7F44: _state.Dma.Length = (_state.Dma.Length & 0x00FF) | (value << 8);
|
||||
break;
|
||||
case 0x7F45: _state.Dma.Dest = (_state.Dma.Dest & 0xFFFF00) | value;
|
||||
break;
|
||||
case 0x7F46: _state.Dma.Dest = (_state.Dma.Dest & 0xFF00FF) | (value << 8);
|
||||
break;
|
||||
case 0x7F47:
|
||||
_state.Dma.Dest = (_state.Dma.Dest & 0x00FFFF) | (value << 16);
|
||||
if (_state.Stopped)
|
||||
{
|
||||
_state.Dma.Enabled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7F48:
|
||||
_state.Cache.Page = value & 0x01;
|
||||
if(_state.Stopped) {
|
||||
_state.Cache.Enabled = true;
|
||||
}
|
||||
break;
|
||||
case 0x7F48:
|
||||
_state.Cache.Page = value & 0x01;
|
||||
if (_state.Stopped)
|
||||
{
|
||||
_state.Cache.Enabled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7F49: _state.Cache.Base = (_state.Cache.Base & 0xFFFF00) | value; break;
|
||||
case 0x7F4A: _state.Cache.Base = (_state.Cache.Base & 0xFF00FF) | (value << 8); break;
|
||||
case 0x7F4B: _state.Cache.Base = (_state.Cache.Base & 0x00FFFF) | (value << 16); break;
|
||||
case 0x7F49: _state.Cache.Base = (_state.Cache.Base & 0xFFFF00) | value;
|
||||
break;
|
||||
case 0x7F4A: _state.Cache.Base = (_state.Cache.Base & 0xFF00FF) | (value << 8);
|
||||
break;
|
||||
case 0x7F4B: _state.Cache.Base = (_state.Cache.Base & 0x00FFFF) | (value << 16);
|
||||
break;
|
||||
|
||||
case 0x7F4C:
|
||||
_state.Cache.Lock[0] = (value & 0x01) != 0;
|
||||
_state.Cache.Lock[1] = (value & 0x02) != 0;
|
||||
break;
|
||||
case 0x7F4C:
|
||||
_state.Cache.Lock[0] = (value & 0x01) != 0;
|
||||
_state.Cache.Lock[1] = (value & 0x02) != 0;
|
||||
break;
|
||||
|
||||
case 0x7F4D: _state.Cache.ProgramBank = (_state.Cache.ProgramBank & 0xFF00) | value; break;
|
||||
case 0x7F4E: _state.Cache.ProgramBank = (_state.Cache.ProgramBank & 0x00FF) | ((value & 0x7F) << 8); break;
|
||||
case 0x7F4D: _state.Cache.ProgramBank = (_state.Cache.ProgramBank & 0xFF00) | value;
|
||||
break;
|
||||
case 0x7F4E: _state.Cache.ProgramBank = (_state.Cache.ProgramBank & 0x00FF) | ((value & 0x7F) << 8);
|
||||
break;
|
||||
|
||||
case 0x7F4F:
|
||||
_state.Cache.ProgramCounter = value;
|
||||
if(_state.Stopped) {
|
||||
_state.Stopped = false;
|
||||
_state.PB = _state.Cache.ProgramBank;
|
||||
_state.PC = _state.Cache.ProgramCounter;
|
||||
}
|
||||
break;
|
||||
case 0x7F4F:
|
||||
_state.Cache.ProgramCounter = value;
|
||||
if (_state.Stopped)
|
||||
{
|
||||
_state.Stopped = false;
|
||||
_state.PB = _state.Cache.ProgramBank;
|
||||
_state.PC = _state.Cache.ProgramCounter;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7F50:
|
||||
_state.RamAccessDelay = value & 0x07;
|
||||
_state.RomAccessDelay = (value >> 4) & 0x07;
|
||||
break;
|
||||
case 0x7F50:
|
||||
_state.RamAccessDelay = value & 0x07;
|
||||
_state.RomAccessDelay = (value >> 4) & 0x07;
|
||||
break;
|
||||
|
||||
case 0x7F51:
|
||||
_state.IrqDisabled = value & 0x01;
|
||||
if(_state.IrqDisabled) {
|
||||
_state.IrqFlag = true;
|
||||
_cpu->ClearIrqSource(IrqSource::Coprocessor);
|
||||
}
|
||||
break;
|
||||
case 0x7F51:
|
||||
_state.IrqDisabled = value & 0x01;
|
||||
if (_state.IrqDisabled)
|
||||
{
|
||||
_state.IrqFlag = true;
|
||||
_cpu->ClearIrqSource(IrqSource::Coprocessor);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7F52: _state.SingleRom = (value & 0x01) != 0; break;
|
||||
case 0x7F52: _state.SingleRom = (value & 0x01) != 0;
|
||||
break;
|
||||
|
||||
case 0x7F53:
|
||||
_state.Locked = false;
|
||||
_state.Stopped = true;
|
||||
break;
|
||||
case 0x7F53:
|
||||
_state.Locked = false;
|
||||
_state.Stopped = true;
|
||||
break;
|
||||
|
||||
case 0x7F5D: _state.Suspend.Enabled = false; break;
|
||||
case 0x7F5D: _state.Suspend.Enabled = false;
|
||||
break;
|
||||
|
||||
case 0x7F5E:
|
||||
//Clear IRQ flag in CX4, but keeps IRQ signal high
|
||||
_state.IrqFlag = false;
|
||||
break;
|
||||
case 0x7F5E:
|
||||
//Clear IRQ flag in CX4, but keeps IRQ signal high
|
||||
_state.IrqFlag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -424,19 +512,23 @@ bool Cx4::IsBusy()
|
|||
return _state.Cache.Enabled || _state.Dma.Enabled || _state.Bus.DelayCycles > 0;
|
||||
}
|
||||
|
||||
void Cx4::Serialize(Serializer &s)
|
||||
void Cx4::Serialize(Serializer& s)
|
||||
{
|
||||
s.Stream(
|
||||
_state.CycleCount, _state.PB, _state.PC, _state.A, _state.P, _state.SP, _state.Mult, _state.RomBuffer,
|
||||
_state.RamBuffer[0], _state.RamBuffer[1], _state.RamBuffer[2], _state.MemoryDataReg, _state.MemoryAddressReg,
|
||||
_state.DataPointerReg, _state.Negative, _state.Zero, _state.Carry, _state.Overflow, _state.IrqFlag, _state.Stopped,
|
||||
_state.Locked, _state.IrqDisabled, _state.SingleRom, _state.RamAccessDelay, _state.RomAccessDelay, _state.Bus.Address,
|
||||
_state.Bus.DelayCycles, _state.Bus.Enabled, _state.Bus.Reading, _state.Bus.Writing, _state.Dma.Dest, _state.Dma.Enabled,
|
||||
_state.Dma.Length, _state.Dma.Source, _state.Dma.Pos, _state.Suspend.Duration, _state.Suspend.Enabled, _state.Cache.Enabled,
|
||||
_state.DataPointerReg, _state.Negative, _state.Zero, _state.Carry, _state.Overflow, _state.IrqFlag,
|
||||
_state.Stopped,
|
||||
_state.Locked, _state.IrqDisabled, _state.SingleRom, _state.RamAccessDelay, _state.RomAccessDelay,
|
||||
_state.Bus.Address,
|
||||
_state.Bus.DelayCycles, _state.Bus.Enabled, _state.Bus.Reading, _state.Bus.Writing, _state.Dma.Dest,
|
||||
_state.Dma.Enabled,
|
||||
_state.Dma.Length, _state.Dma.Source, _state.Dma.Pos, _state.Suspend.Duration, _state.Suspend.Enabled,
|
||||
_state.Cache.Enabled,
|
||||
_state.Cache.Lock[0], _state.Cache.Lock[1], _state.Cache.Address[0], _state.Cache.Address[1], _state.Cache.Base,
|
||||
_state.Cache.Page, _state.Cache.ProgramBank, _state.Cache.ProgramCounter, _state.Cache.Pos
|
||||
);
|
||||
|
||||
|
||||
s.StreamArray(_state.Stack, 8);
|
||||
s.StreamArray(_state.Regs, 16);
|
||||
s.StreamArray(_state.Vectors, 0x20);
|
||||
|
@ -457,7 +549,7 @@ void Cx4::PeekBlock(uint32_t addr, uint8_t* output)
|
|||
|
||||
AddressInfo Cx4::GetAbsoluteAddress(uint32_t address)
|
||||
{
|
||||
return { -1, SnesMemoryType::Register };
|
||||
return {-1, SnesMemoryType::Register};
|
||||
}
|
||||
|
||||
MemoryMappings* Cx4::GetMemoryMappings()
|
||||
|
@ -484,31 +576,48 @@ void Cx4::SetReg(Cx4Register reg, uint32_t value)
|
|||
{
|
||||
switch (reg)
|
||||
{
|
||||
case Cx4Register::Cx4Reg0: case Cx4Register::Cx4Reg1: case Cx4Register::Cx4Reg2: case Cx4Register::Cx4Reg3:
|
||||
case Cx4Register::Cx4Reg4: case Cx4Register::Cx4Reg5: case Cx4Register::Cx4Reg6: case Cx4Register::Cx4Reg7:
|
||||
case Cx4Register::Cx4Reg8: case Cx4Register::Cx4Reg9: case Cx4Register::Cx4Reg10: case Cx4Register::Cx4Reg11:
|
||||
case Cx4Register::Cx4Reg12: case Cx4Register::Cx4Reg13: case Cx4Register::Cx4Reg14: case Cx4Register::Cx4Reg15:
|
||||
case Cx4Register::Cx4Reg0:
|
||||
case Cx4Register::Cx4Reg1:
|
||||
case Cx4Register::Cx4Reg2:
|
||||
case Cx4Register::Cx4Reg3:
|
||||
case Cx4Register::Cx4Reg4:
|
||||
case Cx4Register::Cx4Reg5:
|
||||
case Cx4Register::Cx4Reg6:
|
||||
case Cx4Register::Cx4Reg7:
|
||||
case Cx4Register::Cx4Reg8:
|
||||
case Cx4Register::Cx4Reg9:
|
||||
case Cx4Register::Cx4Reg10:
|
||||
case Cx4Register::Cx4Reg11:
|
||||
case Cx4Register::Cx4Reg12:
|
||||
case Cx4Register::Cx4Reg13:
|
||||
case Cx4Register::Cx4Reg14:
|
||||
case Cx4Register::Cx4Reg15:
|
||||
_state.Regs[(static_cast<int>(reg) - static_cast<int>(Cx4Register::Cx4Reg0)) & 0x0F] = value & 0xFFFFFF; // 24-bit
|
||||
break;
|
||||
case Cx4Register::Cx4RegPB:
|
||||
{
|
||||
_state.PB = value & 0xFFFF;
|
||||
} break;
|
||||
{
|
||||
_state.PB = value & 0xFFFF;
|
||||
}
|
||||
break;
|
||||
case Cx4Register::Cx4RegPC:
|
||||
{
|
||||
_state.PC = value & 0xFF;
|
||||
} break;
|
||||
{
|
||||
_state.PC = value & 0xFF;
|
||||
}
|
||||
break;
|
||||
case Cx4Register::Cx4RegA:
|
||||
{
|
||||
_state.A = value & 0xFFFFFF; // 24-bit
|
||||
} break;
|
||||
{
|
||||
_state.A = value & 0xFFFFFF; // 24-bit
|
||||
}
|
||||
break;
|
||||
case Cx4Register::Cx4RegP:
|
||||
{
|
||||
_state.P = value & 0xFFFF;
|
||||
} break;
|
||||
{
|
||||
_state.P = value & 0xFFFF;
|
||||
}
|
||||
break;
|
||||
case Cx4Register::Cx4RegSP:
|
||||
{
|
||||
_state.SP = value & 0xFF;
|
||||
} break;
|
||||
{
|
||||
_state.SP = value & 0xFF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
10
Core/Cx4.h
10
Core/Cx4.h
|
@ -13,9 +13,9 @@ class Cx4 : public BaseCoprocessor
|
|||
private:
|
||||
static constexpr int DataRamSize = 0xC00;
|
||||
|
||||
Console *_console;
|
||||
MemoryManager *_memoryManager;
|
||||
Cpu *_cpu;
|
||||
Console* _console;
|
||||
MemoryManager* _memoryManager;
|
||||
Cpu* _cpu;
|
||||
MemoryMappings _mappings;
|
||||
double _clockRatio;
|
||||
|
||||
|
@ -119,7 +119,7 @@ public:
|
|||
uint8_t Read(uint32_t addr) override;
|
||||
void Write(uint32_t addr, uint8_t value) override;
|
||||
|
||||
void Serialize(Serializer &s) override;
|
||||
void Serialize(Serializer& s) override;
|
||||
|
||||
uint8_t Peek(uint32_t addr) override;
|
||||
void PeekBlock(uint32_t addr, uint8_t* output) override;
|
||||
|
@ -131,4 +131,4 @@ public:
|
|||
Cx4State GetState();
|
||||
|
||||
void SetReg(Cx4Register reg, uint32_t value);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -42,19 +42,23 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
|
|||
addr = (state.Cache.Address[state.Cache.Page] + (state.PC * 2)) & 0xFFFFFF;
|
||||
|
||||
AddressInfo addressInfo = _cx4->GetMemoryMappings()->GetAbsoluteAddress(addr);
|
||||
MemoryOperationInfo operation { (uint32_t)addr, value, type };
|
||||
MemoryOperationInfo operation{(uint32_t)addr, value, type};
|
||||
|
||||
if(type == MemoryOperationType::ExecOpCode) {
|
||||
if (type == MemoryOperationType::ExecOpCode)
|
||||
{
|
||||
AddressInfo opCodeHighAddr = _cx4->GetMemoryMappings()->GetAbsoluteAddress(addr + 1);
|
||||
if(addressInfo.Type == SnesMemoryType::PrgRom) {
|
||||
if (addressInfo.Type == SnesMemoryType::PrgRom)
|
||||
{
|
||||
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | CdlFlags::Cx4);
|
||||
_codeDataLogger->SetFlags(addressInfo.Address + 1, CdlFlags::Code | CdlFlags::Cx4);
|
||||
}
|
||||
|
||||
if(_traceLogger->IsCpuLogged(CpuType::Cx4) || _settings->CheckDebuggerFlag(DebuggerFlags::Cx4DebuggerEnabled)) {
|
||||
if (_traceLogger->IsCpuLogged(CpuType::Cx4) || _settings->CheckDebuggerFlag(DebuggerFlags::Cx4DebuggerEnabled))
|
||||
{
|
||||
_disassembler->BuildCache(addressInfo, 0, CpuType::Cx4);
|
||||
|
||||
if(_traceLogger->IsCpuLogged(CpuType::Cx4)) {
|
||||
if (_traceLogger->IsCpuLogged(CpuType::Cx4))
|
||||
{
|
||||
DebugState debugState;
|
||||
_debugger->GetState(debugState, true);
|
||||
|
||||
|
@ -65,14 +69,18 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
|
|||
|
||||
_prevProgramCounter = addr;
|
||||
|
||||
if(_step->StepCount > 0) {
|
||||
if (_step->StepCount > 0)
|
||||
{
|
||||
_step->StepCount--;
|
||||
}
|
||||
|
||||
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
|
||||
_memoryAccessCounter->ProcessMemoryExec(opCodeHighAddr, _memoryManager->GetMasterClock());
|
||||
} else {
|
||||
if(addressInfo.Type == SnesMemoryType::PrgRom) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (addressInfo.Type == SnesMemoryType::PrgRom)
|
||||
{
|
||||
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | CdlFlags::Cx4);
|
||||
}
|
||||
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock());
|
||||
|
@ -84,7 +92,7 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
|
|||
void Cx4Debugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
|
||||
{
|
||||
AddressInfo addressInfo = _cx4->GetMemoryMappings()->GetAbsoluteAddress(addr);
|
||||
MemoryOperationInfo operation { (uint32_t)addr, value, type };
|
||||
MemoryOperationInfo operation{(uint32_t)addr, value, type};
|
||||
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
|
||||
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
|
||||
}
|
||||
|
@ -98,17 +106,19 @@ void Cx4Debugger::Step(int32_t stepCount, StepType type)
|
|||
{
|
||||
StepRequest step;
|
||||
|
||||
switch(type) {
|
||||
case StepType::Step: step.StepCount = stepCount; break;
|
||||
switch (type)
|
||||
{
|
||||
case StepType::Step: step.StepCount = stepCount;
|
||||
break;
|
||||
|
||||
case StepType::StepOut:
|
||||
case StepType::StepOver:
|
||||
step.StepCount = 1;
|
||||
break;
|
||||
case StepType::StepOut:
|
||||
case StepType::StepOver:
|
||||
step.StepCount = 1;
|
||||
break;
|
||||
|
||||
case StepType::SpecificScanline:
|
||||
case StepType::PpuStep:
|
||||
break;
|
||||
case StepType::SpecificScanline:
|
||||
case StepType::PpuStep:
|
||||
break;
|
||||
}
|
||||
|
||||
_step.reset(new StepRequest(step));
|
||||
|
@ -117,4 +127,4 @@ void Cx4Debugger::Step(int32_t stepCount, StepType type)
|
|||
BreakpointManager* Cx4Debugger::GetBreakpointManager()
|
||||
{
|
||||
return _breakpointManager.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,4 +40,4 @@ public:
|
|||
void Run();
|
||||
void Step(int32_t stepCount, StepType type);
|
||||
BreakpointManager* GetBreakpointManager();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
#include "../Utilities/HexUtilities.h"
|
||||
#include "../Utilities/FastString.h"
|
||||
|
||||
void Cx4DisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t memoryAddr, LabelManager *labelManager, EmuSettings *settings)
|
||||
void Cx4DisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager,
|
||||
EmuSettings* settings)
|
||||
{
|
||||
FastString str(settings->CheckDebuggerFlag(DebuggerFlags::UseLowerCaseDisassembly));
|
||||
|
||||
|
@ -13,197 +14,417 @@ void Cx4DisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t me
|
|||
uint8_t param1 = info.GetByteCode()[1] & 0x03;
|
||||
uint8_t param2 = info.GetByteCode()[0] & 0xFF;
|
||||
|
||||
auto writeSrc = [&str, param2]() -> void {
|
||||
switch(param2 & 0x7F) {
|
||||
case 0x00: str.Write("A"); break;
|
||||
case 0x01: str.Write("MULTH"); break;
|
||||
case 0x02: str.Write("MULTL"); break;
|
||||
case 0x03: str.Write("MDR"); break;
|
||||
case 0x08: str.Write("ROM"); break;
|
||||
case 0x0C: str.Write("RAM"); break;
|
||||
case 0x13: str.Write("MAR"); break;
|
||||
case 0x1C: str.Write("DPR"); break;
|
||||
case 0x20: str.Write("PC"); break;
|
||||
case 0x28: str.Write("P"); break;
|
||||
auto writeSrc = [&str, param2]() -> void
|
||||
{
|
||||
switch (param2 & 0x7F)
|
||||
{
|
||||
case 0x00: str.Write("A");
|
||||
break;
|
||||
case 0x01: str.Write("MULTH");
|
||||
break;
|
||||
case 0x02: str.Write("MULTL");
|
||||
break;
|
||||
case 0x03: str.Write("MDR");
|
||||
break;
|
||||
case 0x08: str.Write("ROM");
|
||||
break;
|
||||
case 0x0C: str.Write("RAM");
|
||||
break;
|
||||
case 0x13: str.Write("MAR");
|
||||
break;
|
||||
case 0x1C: str.Write("DPR");
|
||||
break;
|
||||
case 0x20: str.Write("PC");
|
||||
break;
|
||||
case 0x28: str.Write("P");
|
||||
break;
|
||||
|
||||
case 0x2E: str.Write("RDROM"); break;
|
||||
case 0x2E: str.Write("RDROM");
|
||||
break;
|
||||
|
||||
case 0x2F: str.Write("RDRAM"); break;
|
||||
case 0x2F: str.Write("RDRAM");
|
||||
break;
|
||||
|
||||
case 0x50: str.Write("#$000000"); break;
|
||||
case 0x51: str.Write("#$FFFFFF"); break;
|
||||
case 0x52: str.Write("#$00FF00"); break;
|
||||
case 0x53: str.Write("#$FF0000"); break;
|
||||
case 0x54: str.Write("#$00FFFF"); break;
|
||||
case 0x55: str.Write("#$FFFF00"); break;
|
||||
case 0x56: str.Write("#$800000"); break;
|
||||
case 0x57: str.Write("#$7FFFFF"); break;
|
||||
case 0x58: str.Write("#$008000"); break;
|
||||
case 0x59: str.Write("#$007FFF"); break;
|
||||
case 0x5A: str.Write("#$FF7FFF"); break;
|
||||
case 0x5B: str.Write("#$FFFF7F"); break;
|
||||
case 0x5C: str.Write("#$010000"); break;
|
||||
case 0x5D: str.Write("#$FEFFFF"); break;
|
||||
case 0x5E: str.Write("#$000100"); break;
|
||||
case 0x5F: str.Write("#$00FEFF"); break;
|
||||
case 0x50: str.Write("#$000000");
|
||||
break;
|
||||
case 0x51: str.Write("#$FFFFFF");
|
||||
break;
|
||||
case 0x52: str.Write("#$00FF00");
|
||||
break;
|
||||
case 0x53: str.Write("#$FF0000");
|
||||
break;
|
||||
case 0x54: str.Write("#$00FFFF");
|
||||
break;
|
||||
case 0x55: str.Write("#$FFFF00");
|
||||
break;
|
||||
case 0x56: str.Write("#$800000");
|
||||
break;
|
||||
case 0x57: str.Write("#$7FFFFF");
|
||||
break;
|
||||
case 0x58: str.Write("#$008000");
|
||||
break;
|
||||
case 0x59: str.Write("#$007FFF");
|
||||
break;
|
||||
case 0x5A: str.Write("#$FF7FFF");
|
||||
break;
|
||||
case 0x5B: str.Write("#$FFFF7F");
|
||||
break;
|
||||
case 0x5C: str.Write("#$010000");
|
||||
break;
|
||||
case 0x5D: str.Write("#$FEFFFF");
|
||||
break;
|
||||
case 0x5E: str.Write("#$000100");
|
||||
break;
|
||||
case 0x5F: str.Write("#$00FEFF");
|
||||
break;
|
||||
|
||||
case 0x60: case 0x70: str.Write("R0"); break;
|
||||
case 0x61: case 0x71: str.Write("R1"); break;
|
||||
case 0x62: case 0x72: str.Write("R2"); break;
|
||||
case 0x63: case 0x73: str.Write("R3"); break;
|
||||
case 0x64: case 0x74: str.Write("R4"); break;
|
||||
case 0x65: case 0x75: str.Write("R5"); break;
|
||||
case 0x66: case 0x76: str.Write("R6"); break;
|
||||
case 0x67: case 0x77: str.Write("R7"); break;
|
||||
case 0x68: case 0x78: str.Write("R8"); break;
|
||||
case 0x69: case 0x79: str.Write("R9"); break;
|
||||
case 0x6A: case 0x7A: str.Write("R10"); break;
|
||||
case 0x6B: case 0x7B: str.Write("R11"); break;
|
||||
case 0x6C: case 0x7C: str.Write("R12"); break;
|
||||
case 0x6D: case 0x7D: str.Write("R13"); break;
|
||||
case 0x6E: case 0x7E: str.Write("R14"); break;
|
||||
case 0x6F: case 0x7F: str.Write("R15"); break;
|
||||
case 0x60:
|
||||
case 0x70: str.Write("R0");
|
||||
break;
|
||||
case 0x61:
|
||||
case 0x71: str.Write("R1");
|
||||
break;
|
||||
case 0x62:
|
||||
case 0x72: str.Write("R2");
|
||||
break;
|
||||
case 0x63:
|
||||
case 0x73: str.Write("R3");
|
||||
break;
|
||||
case 0x64:
|
||||
case 0x74: str.Write("R4");
|
||||
break;
|
||||
case 0x65:
|
||||
case 0x75: str.Write("R5");
|
||||
break;
|
||||
case 0x66:
|
||||
case 0x76: str.Write("R6");
|
||||
break;
|
||||
case 0x67:
|
||||
case 0x77: str.Write("R7");
|
||||
break;
|
||||
case 0x68:
|
||||
case 0x78: str.Write("R8");
|
||||
break;
|
||||
case 0x69:
|
||||
case 0x79: str.Write("R9");
|
||||
break;
|
||||
case 0x6A:
|
||||
case 0x7A: str.Write("R10");
|
||||
break;
|
||||
case 0x6B:
|
||||
case 0x7B: str.Write("R11");
|
||||
break;
|
||||
case 0x6C:
|
||||
case 0x7C: str.Write("R12");
|
||||
break;
|
||||
case 0x6D:
|
||||
case 0x7D: str.Write("R13");
|
||||
break;
|
||||
case 0x6E:
|
||||
case 0x7E: str.Write("R14");
|
||||
break;
|
||||
case 0x6F:
|
||||
case 0x7F: str.Write("R15");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
auto writeDest = [&str, param1]() -> void {
|
||||
switch(param1) {
|
||||
case 0: str.Write("A"); break;
|
||||
case 1: str.Write("MDR"); break;
|
||||
case 2: str.Write("MAR"); break;
|
||||
case 3: str.Write("P"); break;
|
||||
auto writeDest = [&str, param1]() -> void
|
||||
{
|
||||
switch (param1)
|
||||
{
|
||||
case 0: str.Write("A");
|
||||
break;
|
||||
case 1: str.Write("MDR");
|
||||
break;
|
||||
case 2: str.Write("MAR");
|
||||
break;
|
||||
case 3: str.Write("P");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
auto writeShiftedA = [&str, param1]() -> void {
|
||||
switch(param1) {
|
||||
case 0: str.Write("A"); break;
|
||||
case 1: str.Write("(A << 1)"); break;
|
||||
case 2: str.Write("(A << 8)"); break;
|
||||
case 3: str.Write("(A << 16)"); break;
|
||||
auto writeShiftedA = [&str, param1]() -> void
|
||||
{
|
||||
switch (param1)
|
||||
{
|
||||
case 0: str.Write("A");
|
||||
break;
|
||||
case 1: str.Write("(A << 1)");
|
||||
break;
|
||||
case 2: str.Write("(A << 8)");
|
||||
break;
|
||||
case 3: str.Write("(A << 16)");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
auto writeBranchTarget = [&str, param1, param2]() -> void {
|
||||
if(param1) {
|
||||
auto writeBranchTarget = [&str, param1, param2]() -> void
|
||||
{
|
||||
if (param1)
|
||||
{
|
||||
//Far jump
|
||||
str.Write("P:");
|
||||
}
|
||||
str.WriteAll('$', HexUtilities::ToHex(param2));
|
||||
};
|
||||
|
||||
switch(op) {
|
||||
case 0x00: str.Write("NOP"); break;
|
||||
case 0x04: str.Write("???"); break;
|
||||
case 0x08: str.Write("BRA "); writeBranchTarget(); break;
|
||||
case 0x0C: str.Write("BEQ "); writeBranchTarget(); break;
|
||||
|
||||
case 0x10: str.Write("BCS "); writeBranchTarget(); break;
|
||||
case 0x14: str.Write("BMI "); writeBranchTarget(); break;
|
||||
case 0x18: str.Write("BVS "); writeBranchTarget(); break;
|
||||
case 0x1C: str.Write("WAIT"); break;
|
||||
switch (op)
|
||||
{
|
||||
case 0x00: str.Write("NOP");
|
||||
break;
|
||||
case 0x04: str.Write("???");
|
||||
break;
|
||||
case 0x08: str.Write("BRA ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
case 0x0C: str.Write("BEQ ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
|
||||
case 0x20: str.Write("???"); break;
|
||||
case 0x24:
|
||||
str.Write("SKIP");
|
||||
switch(param1) {
|
||||
case 0: str.Write('V'); break;
|
||||
case 1: str.Write('C'); break;
|
||||
case 2: str.Write('Z'); break;
|
||||
case 3: str.Write('N'); break;
|
||||
}
|
||||
str.Write((param2 & 0x01) ? 'S' : 'C');
|
||||
case 0x10: str.Write("BCS ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
case 0x14: str.Write("BMI ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
case 0x18: str.Write("BVS ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
case 0x1C: str.Write("WAIT");
|
||||
break;
|
||||
|
||||
case 0x20: str.Write("???");
|
||||
break;
|
||||
case 0x24:
|
||||
str.Write("SKIP");
|
||||
switch (param1)
|
||||
{
|
||||
case 0: str.Write('V');
|
||||
break;
|
||||
|
||||
case 0x28: str.Write("JSR "); writeBranchTarget(); break;
|
||||
case 0x2C: str.Write("JEQ "); writeBranchTarget(); break;
|
||||
|
||||
case 0x30: str.Write("JCS "); writeBranchTarget(); break;
|
||||
case 0x34: str.Write("JMI "); writeBranchTarget(); break;
|
||||
case 0x38: str.Write("JVS "); writeBranchTarget(); break;
|
||||
case 0x3C: str.Write("RTS"); break;
|
||||
|
||||
case 0x40: str.Write("INC MAR"); break;
|
||||
case 0x44: str.Write("???"); break;
|
||||
case 0x48: str.Write("CMPR "); writeSrc(); str.Write(","); writeShiftedA(); break;
|
||||
case 0x4C: str.WriteAll("CMPR #$", HexUtilities::ToHex(param2)); str.Write(","); writeShiftedA(); break;
|
||||
|
||||
case 0x50: str.Write("CMP "); writeShiftedA(); str.Write(","); writeSrc(); break;
|
||||
case 0x54: str.WriteAll("CMP "); writeShiftedA(); str.WriteAll(",#$", HexUtilities::ToHex(param2)); break;
|
||||
case 0x58:
|
||||
if(param1 == 1) {
|
||||
str.Write("SXB");
|
||||
} else if(param1 == 2) {
|
||||
str.Write("SXW");
|
||||
} else {
|
||||
str.Write("???");
|
||||
}
|
||||
case 1: str.Write('C');
|
||||
break;
|
||||
case 0x5C: str.Write("???"); break;
|
||||
|
||||
case 0x60: str.Write("LD "); writeDest(); str.Write(","); writeSrc(); break;
|
||||
case 0x64: str.Write("LD "); writeDest(); str.WriteAll(", #$", HexUtilities::ToHex(param2)); break;
|
||||
|
||||
case 0x68: str.WriteAll("RDRAM RAM:", '0' + param1, ",A"); break;
|
||||
case 0x6C: str.WriteAll("RDRAM RAM:", '0' + param1, ",DPR+#$", HexUtilities::ToHex(param2)); break;
|
||||
|
||||
case 0x70: str.Write("RDROM (a)"); break;
|
||||
case 0x74: str.WriteAll("RDROM (#$", HexUtilities::ToHex((param1 << 8) | param2), ")"); break;
|
||||
case 0x78: str.Write("???"); break;
|
||||
|
||||
case 0x7C:
|
||||
if(param1 <= 1) {
|
||||
str.WriteAll("LD P", param1 ? "H" : "L", ",#$", HexUtilities::ToHex(param2));
|
||||
} else {
|
||||
str.Write("???");
|
||||
}
|
||||
case 2: str.Write('Z');
|
||||
break;
|
||||
|
||||
case 0x80: str.Write("ADD "); writeShiftedA(); str.Write(","); writeSrc(); break;
|
||||
case 0x84: str.WriteAll("ADD "); writeShiftedA(); str.WriteAll(",#$", HexUtilities::ToHex(param2)); break;
|
||||
case 0x88: str.Write("SUBR "); writeSrc(); str.Write(","); writeShiftedA(); break;
|
||||
case 0x8C: str.WriteAll("SUBR #$", HexUtilities::ToHex(param2)); str.Write(","); writeShiftedA(); break;
|
||||
|
||||
case 0x90: str.Write("SUB "); writeShiftedA(); str.Write(","); writeSrc(); break;
|
||||
case 0x94: str.WriteAll("SUB "); writeShiftedA(); str.WriteAll(",#$", HexUtilities::ToHex(param2)); break;
|
||||
case 0x98: str.Write("SMUL A, "); writeSrc(); break;
|
||||
case 0x9C: str.WriteAll("SMUL A,#$", HexUtilities::ToHex(param2)); break;
|
||||
|
||||
case 0xA0: str.Write("XNOR "); writeShiftedA(); str.Write(","); writeSrc(); break;
|
||||
case 0xA4: str.WriteAll("XNOR "); writeShiftedA(); str.WriteAll(",#$", HexUtilities::ToHex(param2)); break;
|
||||
case 0xA8: str.Write("XOR "); writeShiftedA(); str.Write(","); writeSrc(); break;
|
||||
case 0xAC: str.WriteAll("XOR "); writeShiftedA(); str.WriteAll(",#$", HexUtilities::ToHex(param2)); break;
|
||||
|
||||
case 0xB0: str.Write("AND "); writeShiftedA(); str.Write(","); writeSrc(); break;
|
||||
case 0xB4: str.WriteAll("AND "); writeShiftedA(); str.WriteAll(",#$", HexUtilities::ToHex(param2)); break;
|
||||
case 0xB8: str.Write("OR "); writeShiftedA(); str.Write(","); writeSrc(); break;
|
||||
case 0xBC: str.WriteAll("OR "); writeShiftedA(); str.WriteAll(",#$", HexUtilities::ToHex(param2)); break;
|
||||
|
||||
case 0xC0: str.Write("SHR A,"); writeSrc(); break;
|
||||
case 0xC4: str.WriteAll("SHR A,#$", HexUtilities::ToHex(param2 & 0x1F)); break;
|
||||
case 0xC8: str.Write("ASR A,"); writeSrc(); break;
|
||||
case 0xCC: str.WriteAll("ASR A,#$", HexUtilities::ToHex(param2 & 0x1F)); break;
|
||||
|
||||
case 0xD0: str.Write("ROR A,"); writeSrc(); break;
|
||||
case 0xD4: str.WriteAll("ROR A,#$", HexUtilities::ToHex(param2 & 0x1F)); break;
|
||||
case 0xD8: str.Write("SHL A,"); writeSrc(); break;
|
||||
case 0xDC: str.WriteAll("SHL A,#$", HexUtilities::ToHex(param2 & 0x1F)); break;
|
||||
|
||||
case 0xE0:
|
||||
if(param1 <= 1) {
|
||||
str.Write("ST "); writeSrc(); str.WriteAll(",", param1 ? "MDR" : "A");
|
||||
} else {
|
||||
str.Write("???");
|
||||
}
|
||||
case 3: str.Write('N');
|
||||
break;
|
||||
case 0xE4: str.Write("???"); break;
|
||||
case 0xE8: str.WriteAll("WRRAM A,RAM:", '0' + param1); break;
|
||||
case 0xEC: str.WriteAll("WRRAM DPR+#$", HexUtilities::ToHex(param2), ",RAM:", '0' + param1); break;
|
||||
}
|
||||
str.Write((param2 & 0x01) ? 'S' : 'C');
|
||||
break;
|
||||
|
||||
case 0xF0: str.WriteAll("SWAP A,R", std::to_string(param2 & 0x0F)); break;
|
||||
case 0xF4: str.Write("???"); break;
|
||||
case 0xF8: str.Write("???"); break;
|
||||
case 0xFC: str.Write("STOP"); break;
|
||||
case 0x28: str.Write("JSR ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
case 0x2C: str.Write("JEQ ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
|
||||
case 0x30: str.Write("JCS ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
case 0x34: str.Write("JMI ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
case 0x38: str.Write("JVS ");
|
||||
writeBranchTarget();
|
||||
break;
|
||||
case 0x3C: str.Write("RTS");
|
||||
break;
|
||||
|
||||
case 0x40: str.Write("INC MAR");
|
||||
break;
|
||||
case 0x44: str.Write("???");
|
||||
break;
|
||||
case 0x48: str.Write("CMPR ");
|
||||
writeSrc();
|
||||
str.Write(",");
|
||||
writeShiftedA();
|
||||
break;
|
||||
case 0x4C: str.WriteAll("CMPR #$", HexUtilities::ToHex(param2));
|
||||
str.Write(",");
|
||||
writeShiftedA();
|
||||
break;
|
||||
|
||||
case 0x50: str.Write("CMP ");
|
||||
writeShiftedA();
|
||||
str.Write(",");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0x54: str.WriteAll("CMP ");
|
||||
writeShiftedA();
|
||||
str.WriteAll(",#$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
case 0x58:
|
||||
if (param1 == 1)
|
||||
{
|
||||
str.Write("SXB");
|
||||
}
|
||||
else if (param1 == 2)
|
||||
{
|
||||
str.Write("SXW");
|
||||
}
|
||||
else
|
||||
{
|
||||
str.Write("???");
|
||||
}
|
||||
break;
|
||||
case 0x5C: str.Write("???");
|
||||
break;
|
||||
|
||||
case 0x60: str.Write("LD ");
|
||||
writeDest();
|
||||
str.Write(",");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0x64: str.Write("LD ");
|
||||
writeDest();
|
||||
str.WriteAll(", #$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
|
||||
case 0x68: str.WriteAll("RDRAM RAM:", '0' + param1, ",A");
|
||||
break;
|
||||
case 0x6C: str.WriteAll("RDRAM RAM:", '0' + param1, ",DPR+#$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
|
||||
case 0x70: str.Write("RDROM (a)");
|
||||
break;
|
||||
case 0x74: str.WriteAll("RDROM (#$", HexUtilities::ToHex((param1 << 8) | param2), ")");
|
||||
break;
|
||||
case 0x78: str.Write("???");
|
||||
break;
|
||||
|
||||
case 0x7C:
|
||||
if (param1 <= 1)
|
||||
{
|
||||
str.WriteAll("LD P", param1 ? "H" : "L", ",#$", HexUtilities::ToHex(param2));
|
||||
}
|
||||
else
|
||||
{
|
||||
str.Write("???");
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80: str.Write("ADD ");
|
||||
writeShiftedA();
|
||||
str.Write(",");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0x84: str.WriteAll("ADD ");
|
||||
writeShiftedA();
|
||||
str.WriteAll(",#$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
case 0x88: str.Write("SUBR ");
|
||||
writeSrc();
|
||||
str.Write(",");
|
||||
writeShiftedA();
|
||||
break;
|
||||
case 0x8C: str.WriteAll("SUBR #$", HexUtilities::ToHex(param2));
|
||||
str.Write(",");
|
||||
writeShiftedA();
|
||||
break;
|
||||
|
||||
case 0x90: str.Write("SUB ");
|
||||
writeShiftedA();
|
||||
str.Write(",");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0x94: str.WriteAll("SUB ");
|
||||
writeShiftedA();
|
||||
str.WriteAll(",#$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
case 0x98: str.Write("SMUL A, ");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0x9C: str.WriteAll("SMUL A,#$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
|
||||
case 0xA0: str.Write("XNOR ");
|
||||
writeShiftedA();
|
||||
str.Write(",");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0xA4: str.WriteAll("XNOR ");
|
||||
writeShiftedA();
|
||||
str.WriteAll(",#$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
case 0xA8: str.Write("XOR ");
|
||||
writeShiftedA();
|
||||
str.Write(",");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0xAC: str.WriteAll("XOR ");
|
||||
writeShiftedA();
|
||||
str.WriteAll(",#$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
|
||||
case 0xB0: str.Write("AND ");
|
||||
writeShiftedA();
|
||||
str.Write(",");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0xB4: str.WriteAll("AND ");
|
||||
writeShiftedA();
|
||||
str.WriteAll(",#$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
case 0xB8: str.Write("OR ");
|
||||
writeShiftedA();
|
||||
str.Write(",");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0xBC: str.WriteAll("OR ");
|
||||
writeShiftedA();
|
||||
str.WriteAll(",#$", HexUtilities::ToHex(param2));
|
||||
break;
|
||||
|
||||
case 0xC0: str.Write("SHR A,");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0xC4: str.WriteAll("SHR A,#$", HexUtilities::ToHex(param2 & 0x1F));
|
||||
break;
|
||||
case 0xC8: str.Write("ASR A,");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0xCC: str.WriteAll("ASR A,#$", HexUtilities::ToHex(param2 & 0x1F));
|
||||
break;
|
||||
|
||||
case 0xD0: str.Write("ROR A,");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0xD4: str.WriteAll("ROR A,#$", HexUtilities::ToHex(param2 & 0x1F));
|
||||
break;
|
||||
case 0xD8: str.Write("SHL A,");
|
||||
writeSrc();
|
||||
break;
|
||||
case 0xDC: str.WriteAll("SHL A,#$", HexUtilities::ToHex(param2 & 0x1F));
|
||||
break;
|
||||
|
||||
case 0xE0:
|
||||
if (param1 <= 1)
|
||||
{
|
||||
str.Write("ST ");
|
||||
writeSrc();
|
||||
str.WriteAll(",", param1 ? "MDR" : "A");
|
||||
}
|
||||
else
|
||||
{
|
||||
str.Write("???");
|
||||
}
|
||||
break;
|
||||
case 0xE4: str.Write("???");
|
||||
break;
|
||||
case 0xE8: str.WriteAll("WRRAM A,RAM:", '0' + param1);
|
||||
break;
|
||||
case 0xEC: str.WriteAll("WRRAM DPR+#$", HexUtilities::ToHex(param2), ",RAM:", '0' + param1);
|
||||
break;
|
||||
|
||||
case 0xF0: str.WriteAll("SWAP A,R", std::to_string(param2 & 0x0F));
|
||||
break;
|
||||
case 0xF4: str.Write("???");
|
||||
break;
|
||||
case 0xF8: str.Write("???");
|
||||
break;
|
||||
case 0xFC: str.Write("STOP");
|
||||
break;
|
||||
}
|
||||
|
||||
out += str.ToString();
|
||||
|
|
|
@ -8,5 +8,6 @@ class EmuSettings;
|
|||
class Cx4DisUtils
|
||||
{
|
||||
public:
|
||||
static void GetDisassembly(DisassemblyInfo &info, string &out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings);
|
||||
static void GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager,
|
||||
EmuSettings* settings);
|
||||
};
|
||||
|
|
|
@ -48,7 +48,7 @@ struct Cx4State
|
|||
uint8_t PC;
|
||||
|
||||
//Accumulator
|
||||
uint32_t A;
|
||||
uint32_t A;
|
||||
|
||||
//Page register
|
||||
uint16_t P;
|
||||
|
@ -76,7 +76,7 @@ struct Cx4State
|
|||
bool Stopped;
|
||||
bool Locked;
|
||||
bool IrqDisabled;
|
||||
|
||||
|
||||
bool SingleRom;
|
||||
|
||||
uint8_t RomAccessDelay;
|
||||
|
@ -87,4 +87,4 @@ struct Cx4State
|
|||
Cx4Cache Cache;
|
||||
Cx4Suspend Suspend;
|
||||
uint8_t Vectors[0x20];
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
class DebugBreakHelper
|
||||
{
|
||||
private:
|
||||
Debugger * _debugger;
|
||||
Debugger* _debugger;
|
||||
bool _needResume = false;
|
||||
bool _isEmulationThread = false;
|
||||
|
||||
|
@ -17,11 +17,15 @@ public:
|
|||
|
||||
_isEmulationThread = debugger->GetConsole()->GetEmulationThreadId() == std::this_thread::get_id();
|
||||
|
||||
if(!_isEmulationThread) {
|
||||
if (!_isEmulationThread)
|
||||
{
|
||||
//Only attempt to break if this is done in a thread other than the main emulation thread
|
||||
debugger->BreakRequest(false);
|
||||
if(!debugger->IsExecutionStopped()) {
|
||||
while(!debugger->IsExecutionStopped()) {}
|
||||
if (!debugger->IsExecutionStopped())
|
||||
{
|
||||
while (!debugger->IsExecutionStopped())
|
||||
{
|
||||
}
|
||||
_needResume = true;
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +33,9 @@ public:
|
|||
|
||||
~DebugBreakHelper()
|
||||
{
|
||||
if(!_isEmulationThread) {
|
||||
if (!_isEmulationThread)
|
||||
{
|
||||
_debugger->BreakRequest(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -27,16 +27,19 @@ void DebugHud::ClearScreen()
|
|||
void DebugHud::Draw(uint32_t* argbBuffer, OverscanDimensions overscan, uint32_t lineWidth, uint32_t frameNumber)
|
||||
{
|
||||
auto lock = _commandLock.AcquireSafe();
|
||||
for(unique_ptr<DrawCommand> &command : _commands) {
|
||||
for (unique_ptr<DrawCommand>& command : _commands)
|
||||
{
|
||||
command->Draw(argbBuffer, overscan, lineWidth, frameNumber);
|
||||
}
|
||||
_commands.erase(std::remove_if(_commands.begin(), _commands.end(), [](const unique_ptr<DrawCommand>& c) { return c->Expired(); }), _commands.end());
|
||||
_commands.erase(std::remove_if(_commands.begin(), _commands.end(),
|
||||
[](const unique_ptr<DrawCommand>& c) { return c->Expired(); }), _commands.end());
|
||||
}
|
||||
|
||||
void DebugHud::DrawPixel(int x, int y, int color, int frameCount, int startFrame)
|
||||
{
|
||||
auto lock = _commandLock.AcquireSafe();
|
||||
if(_commands.size() < DebugHud::MaxCommandCount) {
|
||||
if (_commands.size() < DebugHud::MaxCommandCount)
|
||||
{
|
||||
_commands.push_back(unique_ptr<DrawPixelCommand>(new DrawPixelCommand(x, y, color, frameCount, startFrame)));
|
||||
}
|
||||
}
|
||||
|
@ -44,23 +47,29 @@ void DebugHud::DrawPixel(int x, int y, int color, int frameCount, int startFrame
|
|||
void DebugHud::DrawLine(int x, int y, int x2, int y2, int color, int frameCount, int startFrame)
|
||||
{
|
||||
auto lock = _commandLock.AcquireSafe();
|
||||
if(_commands.size() < DebugHud::MaxCommandCount) {
|
||||
_commands.push_back(unique_ptr<DrawLineCommand>(new DrawLineCommand(x, y, x2, y2, color, frameCount, startFrame)));
|
||||
if (_commands.size() < DebugHud::MaxCommandCount)
|
||||
{
|
||||
_commands.push_back(
|
||||
unique_ptr<DrawLineCommand>(new DrawLineCommand(x, y, x2, y2, color, frameCount, startFrame)));
|
||||
}
|
||||
}
|
||||
|
||||
void DebugHud::DrawRectangle(int x, int y, int width, int height, int color, bool fill, int frameCount, int startFrame)
|
||||
{
|
||||
auto lock = _commandLock.AcquireSafe();
|
||||
if(_commands.size() < DebugHud::MaxCommandCount) {
|
||||
_commands.push_back(unique_ptr<DrawRectangleCommand>(new DrawRectangleCommand(x, y, width, height, color, fill, frameCount, startFrame)));
|
||||
if (_commands.size() < DebugHud::MaxCommandCount)
|
||||
{
|
||||
_commands.push_back(
|
||||
unique_ptr<DrawRectangleCommand>(
|
||||
new DrawRectangleCommand(x, y, width, height, color, fill, frameCount, startFrame)));
|
||||
}
|
||||
}
|
||||
|
||||
void DebugHud::DrawScreenBuffer(uint32_t* screenBuffer, int startFrame)
|
||||
{
|
||||
auto lock = _commandLock.AcquireSafe();
|
||||
if(_commands.size() < DebugHud::MaxCommandCount) {
|
||||
if (_commands.size() < DebugHud::MaxCommandCount)
|
||||
{
|
||||
_commands.push_back(unique_ptr<DrawScreenBufferCommand>(new DrawScreenBufferCommand(screenBuffer, startFrame)));
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +77,9 @@ void DebugHud::DrawScreenBuffer(uint32_t* screenBuffer, int startFrame)
|
|||
void DebugHud::DrawString(int x, int y, string text, int color, int backColor, int frameCount, int startFrame)
|
||||
{
|
||||
auto lock = _commandLock.AcquireSafe();
|
||||
if(_commands.size() < DebugHud::MaxCommandCount) {
|
||||
_commands.push_back(unique_ptr<DrawStringCommand>(new DrawStringCommand(x, y, text, color, backColor, frameCount, startFrame)));
|
||||
if (_commands.size() < DebugHud::MaxCommandCount)
|
||||
{
|
||||
_commands.push_back(
|
||||
unique_ptr<DrawStringCommand>(new DrawStringCommand(x, y, text, color, backColor, frameCount, startFrame)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "DebugHud.h"
|
||||
#include "IAudioDevice.h"
|
||||
|
||||
void DebugStats::DisplayStats(Console *console, double lastFrameTime)
|
||||
void DebugStats::DisplayStats(Console* console, double lastFrameTime)
|
||||
{
|
||||
AudioStatistics stats = console->GetSoundMixer()->GetStatistics();
|
||||
AudioConfig audioCfg = console->GetSettings()->GetAudioConfig();
|
||||
|
@ -23,21 +23,29 @@ void DebugStats::DisplayStats(Console *console, double lastFrameTime)
|
|||
hud->DrawString(10, 10, "Audio Stats", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
||||
hud->DrawString(10, 21, "Latency: ", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
||||
|
||||
int color = (stats.AverageLatency > 0 && std::abs(stats.AverageLatency - audioCfg.AudioLatency) > 3) ? 0xFF0000 : 0xFFFFFF;
|
||||
int color = (stats.AverageLatency > 0 && std::abs(stats.AverageLatency - audioCfg.AudioLatency) > 3)
|
||||
? 0xFF0000
|
||||
: 0xFFFFFF;
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(2) << stats.AverageLatency << " ms";
|
||||
hud->DrawString(54, 21, ss.str(), color, 0xFF000000, 1, startFrame);
|
||||
|
||||
hud->DrawString(10, 30, "Underruns: " + std::to_string(stats.BufferUnderrunEventCount), 0xFFFFFF, 0xFF000000, 1, startFrame);
|
||||
hud->DrawString(10, 39, "Buffer Size: " + std::to_string(stats.BufferSize / 1024) + "kb", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
||||
hud->DrawString(10, 48, "Rate: " + std::to_string((uint32_t)(audioCfg.SampleRate * console->GetSoundMixer()->GetRateAdjustment())) + "Hz", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
||||
hud->DrawString(10, 30, "Underruns: " + std::to_string(stats.BufferUnderrunEventCount), 0xFFFFFF, 0xFF000000, 1,
|
||||
startFrame);
|
||||
hud->DrawString(10, 39, "Buffer Size: " + std::to_string(stats.BufferSize / 1024) + "kb", 0xFFFFFF, 0xFF000000, 1,
|
||||
startFrame);
|
||||
hud->DrawString(
|
||||
10, 48,
|
||||
"Rate: " + std::to_string((uint32_t)(audioCfg.SampleRate * console->GetSoundMixer()->GetRateAdjustment())) + "Hz",
|
||||
0xFFFFFF, 0xFF000000, 1, startFrame);
|
||||
|
||||
hud->DrawRectangle(132, 8, 115, 49, 0x40000000, true, 1, startFrame);
|
||||
hud->DrawRectangle(132, 8, 115, 49, 0xFFFFFF, false, 1, startFrame);
|
||||
hud->DrawString(134, 10, "Video Stats", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
||||
|
||||
double totalDuration = 0;
|
||||
for(int i = 0; i < 60; i++) {
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
totalDuration += _frameDurations[i];
|
||||
}
|
||||
|
||||
|
@ -49,10 +57,13 @@ void DebugStats::DisplayStats(Console *console, double lastFrameTime)
|
|||
ss << "Last Frame: " << std::fixed << std::setprecision(2) << lastFrameTime << " ms";
|
||||
hud->DrawString(134, 30, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
|
||||
|
||||
if(console->GetFrameCount() > 60) {
|
||||
if (console->GetFrameCount() > 60)
|
||||
{
|
||||
_lastFrameMin = std::min(lastFrameTime, _lastFrameMin);
|
||||
_lastFrameMax = std::max(lastFrameTime, _lastFrameMax);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastFrameMin = 9999;
|
||||
_lastFrameMax = 0;
|
||||
}
|
||||
|
|
|
@ -12,5 +12,5 @@ private:
|
|||
double _lastFrameMax = 0;
|
||||
|
||||
public:
|
||||
void DisplayStats(Console *console, double lastFrameTime);
|
||||
};
|
||||
void DisplayStats(Console* console, double lastFrameTime);
|
||||
};
|
||||
|
|
|
@ -241,7 +241,7 @@ enum class BreakSource
|
|||
BreakOnWdm = 5,
|
||||
BreakOnStp = 6,
|
||||
BreakOnUninitMemoryRead = 7,
|
||||
|
||||
|
||||
GbInvalidOamAccess = 8,
|
||||
GbInvalidVramAccess = 9,
|
||||
GbDisableLcdOutsideVblank = 10,
|
||||
|
|
|
@ -7,14 +7,15 @@ class DebugUtilities
|
|||
public:
|
||||
static SnesMemoryType GetCpuMemoryType(CpuType type)
|
||||
{
|
||||
switch(type) {
|
||||
case CpuType::Cpu: return SnesMemoryType::CpuMemory;
|
||||
case CpuType::Spc: return SnesMemoryType::SpcMemory;
|
||||
case CpuType::NecDsp: return SnesMemoryType::NecDspMemory;
|
||||
case CpuType::Sa1: return SnesMemoryType::Sa1Memory;
|
||||
case CpuType::Gsu: return SnesMemoryType::GsuMemory;
|
||||
case CpuType::Cx4: return SnesMemoryType::Cx4Memory;
|
||||
case CpuType::Gameboy: return SnesMemoryType::GameboyMemory;
|
||||
switch (type)
|
||||
{
|
||||
case CpuType::Cpu: return SnesMemoryType::CpuMemory;
|
||||
case CpuType::Spc: return SnesMemoryType::SpcMemory;
|
||||
case CpuType::NecDsp: return SnesMemoryType::NecDspMemory;
|
||||
case CpuType::Sa1: return SnesMemoryType::Sa1Memory;
|
||||
case CpuType::Gsu: return SnesMemoryType::GsuMemory;
|
||||
case CpuType::Cx4: return SnesMemoryType::Cx4Memory;
|
||||
case CpuType::Gameboy: return SnesMemoryType::GameboyMemory;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Invalid CPU type");
|
||||
|
@ -22,41 +23,42 @@ public:
|
|||
|
||||
static CpuType ToCpuType(SnesMemoryType type)
|
||||
{
|
||||
switch(type) {
|
||||
case SnesMemoryType::SpcMemory:
|
||||
case SnesMemoryType::SpcRam:
|
||||
case SnesMemoryType::SpcRom:
|
||||
return CpuType::Spc;
|
||||
switch (type)
|
||||
{
|
||||
case SnesMemoryType::SpcMemory:
|
||||
case SnesMemoryType::SpcRam:
|
||||
case SnesMemoryType::SpcRom:
|
||||
return CpuType::Spc;
|
||||
|
||||
case SnesMemoryType::GsuMemory:
|
||||
case SnesMemoryType::GsuWorkRam:
|
||||
return CpuType::Gsu;
|
||||
case SnesMemoryType::GsuMemory:
|
||||
case SnesMemoryType::GsuWorkRam:
|
||||
return CpuType::Gsu;
|
||||
|
||||
case SnesMemoryType::Sa1InternalRam:
|
||||
case SnesMemoryType::Sa1Memory:
|
||||
return CpuType::Sa1;
|
||||
case SnesMemoryType::Sa1InternalRam:
|
||||
case SnesMemoryType::Sa1Memory:
|
||||
return CpuType::Sa1;
|
||||
|
||||
case SnesMemoryType::DspDataRam:
|
||||
case SnesMemoryType::DspDataRom:
|
||||
case SnesMemoryType::DspProgramRom:
|
||||
return CpuType::NecDsp;
|
||||
case SnesMemoryType::DspDataRam:
|
||||
case SnesMemoryType::DspDataRom:
|
||||
case SnesMemoryType::DspProgramRom:
|
||||
return CpuType::NecDsp;
|
||||
|
||||
case SnesMemoryType::Cx4DataRam:
|
||||
case SnesMemoryType::Cx4Memory:
|
||||
return CpuType::Cx4;
|
||||
|
||||
case SnesMemoryType::GbPrgRom:
|
||||
case SnesMemoryType::GbWorkRam:
|
||||
case SnesMemoryType::GbCartRam:
|
||||
case SnesMemoryType::GbHighRam:
|
||||
case SnesMemoryType::GbBootRom:
|
||||
case SnesMemoryType::GbVideoRam:
|
||||
case SnesMemoryType::GbSpriteRam:
|
||||
case SnesMemoryType::GameboyMemory:
|
||||
return CpuType::Gameboy;
|
||||
case SnesMemoryType::Cx4DataRam:
|
||||
case SnesMemoryType::Cx4Memory:
|
||||
return CpuType::Cx4;
|
||||
|
||||
default:
|
||||
return CpuType::Cpu;
|
||||
case SnesMemoryType::GbPrgRom:
|
||||
case SnesMemoryType::GbWorkRam:
|
||||
case SnesMemoryType::GbCartRam:
|
||||
case SnesMemoryType::GbHighRam:
|
||||
case SnesMemoryType::GbBootRom:
|
||||
case SnesMemoryType::GbVideoRam:
|
||||
case SnesMemoryType::GbSpriteRam:
|
||||
case SnesMemoryType::GameboyMemory:
|
||||
return CpuType::Gameboy;
|
||||
|
||||
default:
|
||||
return CpuType::Cpu;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Invalid CPU type");
|
||||
|
@ -69,30 +71,32 @@ public:
|
|||
|
||||
static bool IsPpuMemory(SnesMemoryType memType)
|
||||
{
|
||||
switch(memType) {
|
||||
case SnesMemoryType::VideoRam:
|
||||
case SnesMemoryType::SpriteRam:
|
||||
case SnesMemoryType::CGRam:
|
||||
case SnesMemoryType::GbVideoRam:
|
||||
case SnesMemoryType::GbSpriteRam:
|
||||
return true;
|
||||
switch (memType)
|
||||
{
|
||||
case SnesMemoryType::VideoRam:
|
||||
case SnesMemoryType::SpriteRam:
|
||||
case SnesMemoryType::CGRam:
|
||||
case SnesMemoryType::GbVideoRam:
|
||||
case SnesMemoryType::GbSpriteRam:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsRomMemory(SnesMemoryType memType)
|
||||
{
|
||||
switch(memType) {
|
||||
case SnesMemoryType::PrgRom:
|
||||
case SnesMemoryType::GbPrgRom:
|
||||
case SnesMemoryType::GbBootRom:
|
||||
case SnesMemoryType::SaveRam: //Include save ram here to avoid uninit memory read warnings on save ram
|
||||
return true;
|
||||
switch (memType)
|
||||
{
|
||||
case SnesMemoryType::PrgRom:
|
||||
case SnesMemoryType::GbPrgRom:
|
||||
case SnesMemoryType::GbBootRom:
|
||||
case SnesMemoryType::SaveRam: //Include save ram here to avoid uninit memory read warnings on save ram
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,4 +104,4 @@ public:
|
|||
{
|
||||
return CpuType::Gameboy;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -75,7 +75,7 @@ private:
|
|||
shared_ptr<LabelManager> _labelManager;
|
||||
|
||||
unique_ptr<ExpressionEvaluator> _watchExpEval[(int)DebugUtilities::GetLastCpuType() + 1];
|
||||
|
||||
|
||||
SimpleLock _logLock;
|
||||
std::list<string> _debuggerLog;
|
||||
|
||||
|
@ -84,7 +84,7 @@ private:
|
|||
atomic<uint32_t> _suspendRequestCount;
|
||||
|
||||
bool _waitForBreakResume = false;
|
||||
|
||||
|
||||
void Reset();
|
||||
|
||||
public:
|
||||
|
@ -92,10 +92,10 @@ public:
|
|||
~Debugger();
|
||||
void Release();
|
||||
|
||||
template<CpuType type>
|
||||
template <CpuType type>
|
||||
void ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType);
|
||||
|
||||
template<CpuType type>
|
||||
|
||||
template <CpuType type>
|
||||
void ProcessMemoryWrite(uint32_t addr, uint8_t value, MemoryOperationType opType);
|
||||
|
||||
void ProcessWorkRamRead(uint32_t addr, uint8_t value);
|
||||
|
@ -104,15 +104,15 @@ public:
|
|||
void ProcessPpuRead(uint16_t addr, uint8_t value, SnesMemoryType memoryType);
|
||||
void ProcessPpuWrite(uint16_t addr, uint8_t value, SnesMemoryType memoryType);
|
||||
|
||||
template<CpuType cpuType>
|
||||
template <CpuType cpuType>
|
||||
void ProcessPpuCycle();
|
||||
|
||||
template<CpuType type>
|
||||
template <CpuType type>
|
||||
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);
|
||||
|
||||
void ProcessEvent(EventType type);
|
||||
|
||||
int32_t EvaluateExpression(string expression, CpuType cpuType, EvalResultType &resultType, bool useCache);
|
||||
int32_t EvaluateExpression(string expression, CpuType cpuType, EvalResultType& resultType, bool useCache);
|
||||
|
||||
void Run();
|
||||
void Step(CpuType cpuType, int32_t stepCount, StepType type);
|
||||
|
@ -124,7 +124,8 @@ public:
|
|||
|
||||
void BreakImmediately(BreakSource source);
|
||||
|
||||
void ProcessBreakConditions(bool needBreak, BreakpointManager *bpManager, MemoryOperationInfo &operation, AddressInfo &addressInfo, BreakSource source = BreakSource::Unspecified);
|
||||
void ProcessBreakConditions(bool needBreak, BreakpointManager* bpManager, MemoryOperationInfo& operation,
|
||||
AddressInfo& addressInfo, BreakSource source = BreakSource::Unspecified);
|
||||
void SleepUntilResume(BreakSource source, MemoryOperationInfo* operation = nullptr, int breakpointId = -1);
|
||||
|
||||
void GetState(DebugState& state, bool partialPpuState);
|
||||
|
@ -143,15 +144,15 @@ public:
|
|||
AddressInfo GetRelativeAddress(AddressInfo absAddress, CpuType cpuType);
|
||||
|
||||
void GetCdlData(uint32_t offset, uint32_t length, SnesMemoryType memoryType, uint8_t* cdlData);
|
||||
void SetCdlData(CpuType cpuType, uint8_t * cdlData, uint32_t length);
|
||||
void SetCdlData(CpuType cpuType, uint8_t* cdlData, uint32_t length);
|
||||
void MarkBytesAs(CpuType cpuType, uint32_t start, uint32_t end, uint8_t flags);
|
||||
|
||||
|
||||
void RefreshCodeCache();
|
||||
void RebuildPrgCache(CpuType cpuType);
|
||||
|
||||
void SetBreakpoints(Breakpoint breakpoints[], uint32_t length);
|
||||
void GetBreakpoints(CpuType cpuType, Breakpoint* breakpoints, int& execs, int& reads, int& writes);
|
||||
|
||||
|
||||
void Log(string message);
|
||||
string GetLog();
|
||||
|
||||
|
|
|
@ -20,14 +20,15 @@ void DefaultVideoFilter::InitConversionMatrix(double hueShift, double saturation
|
|||
double hue = hueShift * PI;
|
||||
double sat = saturationShift + 1;
|
||||
|
||||
double baseValues[6] = { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
|
||||
double baseValues[6] = {0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f};
|
||||
|
||||
double s = sin(hue) * sat;
|
||||
double c = cos(hue) * sat;
|
||||
|
||||
double *output = _yiqToRgbMatrix;
|
||||
double *input = baseValues;
|
||||
for(int n = 0; n < 3; n++) {
|
||||
double* output = _yiqToRgbMatrix;
|
||||
double* input = baseValues;
|
||||
for (int n = 0; n < 3; n++)
|
||||
{
|
||||
double i = *input++;
|
||||
double q = *input++;
|
||||
*output++ = i * c - q * s;
|
||||
|
@ -42,24 +43,29 @@ void DefaultVideoFilter::InitLookupTable()
|
|||
InitConversionMatrix(config.Hue, config.Saturation);
|
||||
|
||||
double y, i, q;
|
||||
for(int rgb555 = 0; rgb555 < 0x8000; rgb555++) {
|
||||
for (int rgb555 = 0; rgb555 < 0x8000; rgb555++)
|
||||
{
|
||||
uint8_t r = rgb555 & 0x1F;
|
||||
uint8_t g = (rgb555 >> 5) & 0x1F;
|
||||
uint8_t b = (rgb555 >> 10) & 0x1F;
|
||||
if(_gbcAdjustColors) {
|
||||
if (_gbcAdjustColors)
|
||||
{
|
||||
uint8_t r2 = std::min(240, (r * 26 + g * 4 + b * 2) >> 2);
|
||||
uint8_t g2 = std::min(240, (g * 24 + b * 8) >> 2);
|
||||
uint8_t b2 = std::min(240, (r * 6 + g * 4 + b * 22) >> 2);
|
||||
r = r2;
|
||||
g = g2;
|
||||
b = b2;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
r = To8Bit(r);
|
||||
g = To8Bit(g);
|
||||
b = To8Bit(b);
|
||||
}
|
||||
|
||||
if(config.Hue != 0 || config.Saturation != 0 || config.Brightness != 0 || config.Contrast != 0) {
|
||||
if (config.Hue != 0 || config.Saturation != 0 || config.Brightness != 0 || config.Contrast != 0)
|
||||
{
|
||||
double redChannel = r / 255.0;
|
||||
double greenChannel = g / 255.0;
|
||||
double blueChannel = b / 255.0;
|
||||
|
@ -74,7 +80,9 @@ void DefaultVideoFilter::InitLookupTable()
|
|||
int g = std::min(255, (int)(greenChannel * 255));
|
||||
int b = std::min(255, (int)(blueChannel * 255));
|
||||
_calculatedPalette[rgb555] = 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_calculatedPalette[rgb555] = 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
|
@ -89,11 +97,14 @@ void DefaultVideoFilter::OnBeforeApplyFilter()
|
|||
|
||||
ConsoleType consoleType = _console->GetConsoleType();
|
||||
bool adjustColors = gbConfig.GbcAdjustColors && consoleType == ConsoleType::GameboyColor;
|
||||
if(_videoConfig.Hue != config.Hue || _videoConfig.Saturation != config.Saturation || _videoConfig.Contrast != config.Contrast || _videoConfig.Brightness != config.Brightness || _gbcAdjustColors != adjustColors) {
|
||||
if (_videoConfig.Hue != config.Hue || _videoConfig.Saturation != config.Saturation || _videoConfig.Contrast != config
|
||||
.Contrast || _videoConfig.Brightness != config.Brightness || _gbcAdjustColors != adjustColors)
|
||||
{
|
||||
_gbcAdjustColors = adjustColors;
|
||||
InitLookupTable();
|
||||
}
|
||||
_gbBlendFrames = gbConfig.BlendFrames && (consoleType == ConsoleType::Gameboy || consoleType == ConsoleType::GameboyColor);
|
||||
_gbBlendFrames = gbConfig.BlendFrames && (consoleType == ConsoleType::Gameboy || consoleType ==
|
||||
ConsoleType::GameboyColor);
|
||||
_videoConfig = config;
|
||||
}
|
||||
|
||||
|
@ -111,63 +122,82 @@ uint32_t DefaultVideoFilter::ToArgb(uint16_t rgb555)
|
|||
return 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
void DefaultVideoFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
|
||||
void DefaultVideoFilter::ApplyFilter(uint16_t* ppuOutputBuffer)
|
||||
{
|
||||
uint32_t *out = GetOutputBuffer();
|
||||
uint32_t* out = GetOutputBuffer();
|
||||
FrameInfo frameInfo = GetFrameInfo();
|
||||
OverscanDimensions overscan = GetOverscan();
|
||||
|
||||
|
||||
int overscanMultiplier = _baseFrameInfo.Width == 512 ? 2 : 1;
|
||||
uint32_t width = _baseFrameInfo.Width;
|
||||
uint32_t xOffset = overscan.Left * overscanMultiplier;
|
||||
uint32_t yOffset = overscan.Top * overscanMultiplier * width;
|
||||
|
||||
uint8_t scanlineIntensity = (uint8_t)((1.0 - _console->GetSettings()->GetVideoConfig().ScanlineIntensity) * 255);
|
||||
if(scanlineIntensity < 255) {
|
||||
for(uint32_t i = 0; i < frameInfo.Height; i++) {
|
||||
if(i & 0x01) {
|
||||
for(uint32_t j = 0; j < frameInfo.Width; j++) {
|
||||
*out = ApplyScanlineEffect(GetPixel(ppuOutputBuffer, i * width + j + yOffset + xOffset), scanlineIntensity);
|
||||
if (scanlineIntensity < 255)
|
||||
{
|
||||
for (uint32_t i = 0; i < frameInfo.Height; i++)
|
||||
{
|
||||
if (i & 0x01)
|
||||
{
|
||||
for (uint32_t j = 0; j < frameInfo.Width; j++)
|
||||
{
|
||||
*out = ApplyScanlineEffect(GetPixel(ppuOutputBuffer, i * width + j + yOffset + xOffset),
|
||||
scanlineIntensity);
|
||||
out++;
|
||||
}
|
||||
} else {
|
||||
for(uint32_t j = 0; j < frameInfo.Width; j++) {
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint32_t j = 0; j < frameInfo.Width; j++)
|
||||
{
|
||||
*out = GetPixel(ppuOutputBuffer, i * width + j + yOffset + xOffset);
|
||||
out++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(uint32_t i = 0; i < frameInfo.Height; i++) {
|
||||
for(uint32_t j = 0; j < frameInfo.Width; j++) {
|
||||
out[i*frameInfo.Width+j] = GetPixel(ppuOutputBuffer, i * width + j + yOffset + xOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint32_t i = 0; i < frameInfo.Height; i++)
|
||||
{
|
||||
for (uint32_t j = 0; j < frameInfo.Width; j++)
|
||||
{
|
||||
out[i * frameInfo.Width + j] = GetPixel(ppuOutputBuffer, i * width + j + yOffset + xOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(_baseFrameInfo.Width == 512 && _videoConfig.BlendHighResolutionModes) {
|
||||
if (_baseFrameInfo.Width == 512 && _videoConfig.BlendHighResolutionModes)
|
||||
{
|
||||
//Very basic blend effect for high resolution modes
|
||||
for(uint32_t i = 0; i < frameInfo.Height; i+=2) {
|
||||
for(uint32_t j = 0; j < frameInfo.Width; j+=2) {
|
||||
uint32_t &pixel1 = out[i*frameInfo.Width + j];
|
||||
uint32_t &pixel2 = out[i*frameInfo.Width + j + 1];
|
||||
uint32_t &pixel3 = out[(i+1)*frameInfo.Width + j];
|
||||
uint32_t &pixel4 = out[(i+1)*frameInfo.Width + j + 1];
|
||||
for (uint32_t i = 0; i < frameInfo.Height; i += 2)
|
||||
{
|
||||
for (uint32_t j = 0; j < frameInfo.Width; j += 2)
|
||||
{
|
||||
uint32_t& pixel1 = out[i * frameInfo.Width + j];
|
||||
uint32_t& pixel2 = out[i * frameInfo.Width + j + 1];
|
||||
uint32_t& pixel3 = out[(i + 1) * frameInfo.Width + j];
|
||||
uint32_t& pixel4 = out[(i + 1) * frameInfo.Width + j + 1];
|
||||
pixel1 = pixel2 = pixel3 = pixel4 = BlendPixels(BlendPixels(BlendPixels(pixel1, pixel2), pixel3), pixel4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(_gbBlendFrames) {
|
||||
if (_gbBlendFrames)
|
||||
{
|
||||
std::copy(ppuOutputBuffer, ppuOutputBuffer + 256 * 240, _prevFrame);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DefaultVideoFilter::GetPixel(uint16_t* ppuFrame, uint32_t offset)
|
||||
{
|
||||
if(_gbBlendFrames) {
|
||||
if (_gbBlendFrames)
|
||||
{
|
||||
return BlendPixels(_calculatedPalette[_prevFrame[offset]], _calculatedPalette[ppuFrame[offset]]);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return _calculatedPalette[ppuFrame[offset]];
|
||||
}
|
||||
}
|
||||
|
@ -177,16 +207,16 @@ uint32_t DefaultVideoFilter::BlendPixels(uint32_t a, uint32_t b)
|
|||
return ((((a) ^ (b)) & 0xfffefefeL) >> 1) + ((a) & (b));
|
||||
}
|
||||
|
||||
void DefaultVideoFilter::RgbToYiq(double r, double g, double b, double &y, double &i, double &q)
|
||||
void DefaultVideoFilter::RgbToYiq(double r, double g, double b, double& y, double& i, double& q)
|
||||
{
|
||||
y = r * 0.299f + g * 0.587f + b * 0.114f;
|
||||
i = r * 0.596f - g * 0.275f - b * 0.321f;
|
||||
q = r * 0.212f - g * 0.523f + b * 0.311f;
|
||||
}
|
||||
|
||||
void DefaultVideoFilter::YiqToRgb(double y, double i, double q, double &r, double &g, double &b)
|
||||
void DefaultVideoFilter::YiqToRgb(double y, double i, double q, double& r, double& g, double& b)
|
||||
{
|
||||
r = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[0] * i + _yiqToRgbMatrix[1] * q)));
|
||||
g = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[2] * i + _yiqToRgbMatrix[3] * q)));
|
||||
b = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[4] * i + _yiqToRgbMatrix[5] * q)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ private:
|
|||
void InitConversionMatrix(double hueShift, double saturationShift);
|
||||
void InitLookupTable();
|
||||
|
||||
void RgbToYiq(double r, double g, double b, double &y, double &i, double &q);
|
||||
void YiqToRgb(double y, double i, double q, double &r, double &g, double &b);
|
||||
void RgbToYiq(double r, double g, double b, double& y, double& i, double& q);
|
||||
void YiqToRgb(double y, double i, double q, double& r, double& g, double& b);
|
||||
__forceinline static uint8_t To8Bit(uint8_t color);
|
||||
__forceinline static uint32_t BlendPixels(uint32_t a, uint32_t b);
|
||||
__forceinline uint32_t GetPixel(uint16_t* ppuFrame, uint32_t offset);
|
||||
|
@ -29,7 +29,7 @@ protected:
|
|||
|
||||
public:
|
||||
DefaultVideoFilter(shared_ptr<Console> console);
|
||||
void ApplyFilter(uint16_t *ppuOutputBuffer);
|
||||
void ApplyFilter(uint16_t* ppuOutputBuffer);
|
||||
|
||||
static uint32_t ToArgb(uint16_t rgb555);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -46,18 +46,21 @@ Disassembler::Disassembler(shared_ptr<Console> console, shared_ptr<CodeDataLogge
|
|||
_memoryDumper = _debugger->GetMemoryDumper().get();
|
||||
_memoryManager = console->GetMemoryManager().get();
|
||||
|
||||
for(int i = 0; i < (int)DebugUtilities::GetLastCpuType(); i++) {
|
||||
for (int i = 0; i < (int)DebugUtilities::GetLastCpuType(); i++)
|
||||
{
|
||||
_disassemblyResult[i] = vector<DisassemblyResult>();
|
||||
_needDisassemble[i] = true;
|
||||
}
|
||||
|
||||
for(int i = (int)SnesMemoryType::PrgRom; i < (int)SnesMemoryType::Register; i++) {
|
||||
for (int i = (int)SnesMemoryType::PrgRom; i < (int)SnesMemoryType::Register; i++)
|
||||
{
|
||||
InitSource((SnesMemoryType)i);
|
||||
}
|
||||
|
||||
if(_necDsp) {
|
||||
if (_necDsp)
|
||||
{
|
||||
//Build cache for the entire DSP chip (since it only contains instructions)
|
||||
AddressInfo dspStart = { 0, SnesMemoryType::DspProgramRom };
|
||||
AddressInfo dspStart = {0, SnesMemoryType::DspProgramRom};
|
||||
BuildCache(dspStart, 0, CpuType::NecDsp);
|
||||
}
|
||||
}
|
||||
|
@ -67,42 +70,49 @@ void Disassembler::InitSource(SnesMemoryType type)
|
|||
uint8_t* src = _memoryDumper->GetMemoryBuffer(type);
|
||||
uint32_t size = _memoryDumper->GetMemorySize(type);
|
||||
_disassemblyCache[(int)type] = vector<DisassemblyInfo>(size);
|
||||
_sources[(int)type] = { src, &_disassemblyCache[(int)type], size };
|
||||
_sources[(int)type] = {src, &_disassemblyCache[(int)type], size};
|
||||
}
|
||||
|
||||
DisassemblerSource& Disassembler::GetSource(SnesMemoryType type)
|
||||
{
|
||||
if(_sources[(int)type].Data == nullptr) {
|
||||
if (_sources[(int)type].Data == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Disassembler::GetSource() invalid memory type");
|
||||
}
|
||||
|
||||
return _sources[(int)type];
|
||||
}
|
||||
|
||||
uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuType type)
|
||||
uint32_t Disassembler::BuildCache(AddressInfo& addrInfo, uint8_t cpuFlags, CpuType type)
|
||||
{
|
||||
DisassemblerSource& src = GetSource(addrInfo.Type);
|
||||
|
||||
bool needDisassemble = false;
|
||||
int returnSize = 0;
|
||||
int32_t address = addrInfo.Address;
|
||||
while(address >= 0 && address < (int32_t)src.Cache->size()) {
|
||||
DisassemblyInfo &disInfo = (*src.Cache)[address];
|
||||
if(!disInfo.IsInitialized() || !disInfo.IsValid(cpuFlags)) {
|
||||
disInfo.Initialize(src.Data+address, cpuFlags, type);
|
||||
for(int i = 1; i < disInfo.GetOpSize(); i++) {
|
||||
while (address >= 0 && address < (int32_t)src.Cache->size())
|
||||
{
|
||||
DisassemblyInfo& disInfo = (*src.Cache)[address];
|
||||
if (!disInfo.IsInitialized() || !disInfo.IsValid(cpuFlags))
|
||||
{
|
||||
disInfo.Initialize(src.Data + address, cpuFlags, type);
|
||||
for (int i = 1; i < disInfo.GetOpSize(); i++)
|
||||
{
|
||||
//Clear any instructions that start in the middle of this one
|
||||
//(can happen when resizing an instruction after X/M updates)
|
||||
(*src.Cache)[address + i] = DisassemblyInfo();
|
||||
}
|
||||
needDisassemble = true;
|
||||
returnSize += disInfo.GetOpSize();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
returnSize += disInfo.GetOpSize();
|
||||
break;
|
||||
}
|
||||
|
||||
if(disInfo.IsUnconditionalJump()) {
|
||||
if (disInfo.IsUnconditionalJump())
|
||||
{
|
||||
//Can't assume what follows is code, stop disassembling
|
||||
break;
|
||||
}
|
||||
|
@ -111,7 +121,8 @@ uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuTy
|
|||
address += disInfo.GetOpSize();
|
||||
}
|
||||
|
||||
if(needDisassemble) {
|
||||
if (needDisassemble)
|
||||
{
|
||||
SetDisassembleFlag(type);
|
||||
}
|
||||
|
||||
|
@ -120,12 +131,15 @@ uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuTy
|
|||
|
||||
void Disassembler::SetDisassembleFlag(CpuType type)
|
||||
{
|
||||
if(type == CpuType::Cpu || type == CpuType::Sa1 || type == CpuType::Gsu || type == CpuType::Cx4) {
|
||||
if (type == CpuType::Cpu || type == CpuType::Sa1 || type == CpuType::Gsu || type == CpuType::Cx4)
|
||||
{
|
||||
_needDisassemble[(int)CpuType::Cpu] = true;
|
||||
_needDisassemble[(int)CpuType::Sa1] = true;
|
||||
_needDisassemble[(int)CpuType::Gsu] = true;
|
||||
_needDisassemble[(int)CpuType::Cx4] = true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_needDisassemble[(int)type] = true;
|
||||
}
|
||||
}
|
||||
|
@ -146,85 +160,97 @@ void Disassembler::InvalidateCache(AddressInfo addrInfo, CpuType type)
|
|||
DisassemblerSource src = GetSource(addrInfo.Type);
|
||||
bool needDisassemble = false;
|
||||
|
||||
if(addrInfo.Address >= 0) {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(addrInfo.Address >= i) {
|
||||
if((*src.Cache)[addrInfo.Address - i].IsInitialized()) {
|
||||
if (addrInfo.Address >= 0)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (addrInfo.Address >= i)
|
||||
{
|
||||
if ((*src.Cache)[addrInfo.Address - i].IsInitialized())
|
||||
{
|
||||
(*src.Cache)[addrInfo.Address - i].Reset();
|
||||
needDisassemble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(needDisassemble) {
|
||||
|
||||
if (needDisassemble)
|
||||
{
|
||||
SetDisassembleFlag(type);
|
||||
}
|
||||
}
|
||||
|
||||
void Disassembler::Disassemble(CpuType cpuType)
|
||||
{
|
||||
if(!_needDisassemble[(int)cpuType]) {
|
||||
if (!_needDisassemble[(int)cpuType])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_needDisassemble[(int)cpuType] = false;
|
||||
|
||||
auto lock = _disassemblyLock.AcquireSafe();
|
||||
|
||||
MemoryMappings *mappings = nullptr;
|
||||
auto lock = _disassemblyLock.AcquireSafe();
|
||||
|
||||
MemoryMappings* mappings = nullptr;
|
||||
int32_t maxAddr = 0xFFFFFF;
|
||||
switch(cpuType) {
|
||||
case CpuType::Cpu:
|
||||
mappings = _memoryManager->GetMemoryMappings();
|
||||
break;
|
||||
switch (cpuType)
|
||||
{
|
||||
case CpuType::Cpu:
|
||||
mappings = _memoryManager->GetMemoryMappings();
|
||||
break;
|
||||
|
||||
case CpuType::Sa1:
|
||||
if(!_sa1) {
|
||||
return;
|
||||
}
|
||||
mappings = _sa1->GetMemoryMappings();
|
||||
break;
|
||||
case CpuType::Sa1:
|
||||
if (!_sa1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mappings = _sa1->GetMemoryMappings();
|
||||
break;
|
||||
|
||||
case CpuType::Gsu:
|
||||
if(!_gsu) {
|
||||
return;
|
||||
}
|
||||
mappings = _gsu->GetMemoryMappings();
|
||||
break;
|
||||
case CpuType::Gsu:
|
||||
if (!_gsu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mappings = _gsu->GetMemoryMappings();
|
||||
break;
|
||||
|
||||
case CpuType::NecDsp:
|
||||
if(!_console->GetCartridge()->GetDsp()) {
|
||||
return;
|
||||
}
|
||||
mappings = nullptr;
|
||||
maxAddr = _necDsp->DebugGetProgramRomSize() - 1;
|
||||
break;
|
||||
case CpuType::NecDsp:
|
||||
if (!_console->GetCartridge()->GetDsp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
mappings = nullptr;
|
||||
maxAddr = _necDsp->DebugGetProgramRomSize() - 1;
|
||||
break;
|
||||
|
||||
case CpuType::Spc:
|
||||
mappings = nullptr;
|
||||
maxAddr = 0xFFFF;
|
||||
break;
|
||||
case CpuType::Spc:
|
||||
mappings = nullptr;
|
||||
maxAddr = 0xFFFF;
|
||||
break;
|
||||
|
||||
case CpuType::Gameboy:
|
||||
if(!_gameboy) {
|
||||
return;
|
||||
}
|
||||
mappings = nullptr;
|
||||
maxAddr = 0xFFFF;
|
||||
break;
|
||||
case CpuType::Gameboy:
|
||||
if (!_gameboy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mappings = nullptr;
|
||||
maxAddr = 0xFFFF;
|
||||
break;
|
||||
|
||||
case CpuType::Cx4:
|
||||
if(!_console->GetCartridge()->GetCx4()) {
|
||||
return;
|
||||
}
|
||||
mappings = _console->GetCartridge()->GetCx4()->GetMemoryMappings();
|
||||
break;
|
||||
case CpuType::Cx4:
|
||||
if (!_console->GetCartridge()->GetCx4())
|
||||
{
|
||||
return;
|
||||
}
|
||||
mappings = _console->GetCartridge()->GetCx4()->GetMemoryMappings();
|
||||
break;
|
||||
|
||||
default: throw std::runtime_error("Disassemble(): Invalid cpu type");
|
||||
default: throw std::runtime_error("Disassemble(): Invalid cpu type");
|
||||
}
|
||||
|
||||
vector<DisassemblyResult> &results = _disassemblyResult[(int)cpuType];
|
||||
vector<DisassemblyResult>& results = _disassemblyResult[(int)cpuType];
|
||||
results.clear();
|
||||
|
||||
bool disUnident = _settings->CheckDebuggerFlag(DebuggerFlags::DisassembleUnidentifiedData);
|
||||
|
@ -238,54 +264,73 @@ void Disassembler::Disassemble(CpuType cpuType)
|
|||
AddressInfo addrInfo = {};
|
||||
AddressInfo prevAddrInfo = {};
|
||||
int byteCounter = 0;
|
||||
for(int32_t i = 0; i <= maxAddr; i++) {
|
||||
for (int32_t i = 0; i <= maxAddr; i++)
|
||||
{
|
||||
prevAddrInfo = addrInfo;
|
||||
switch(cpuType) {
|
||||
case CpuType::Spc: addrInfo = _spc->GetAbsoluteAddress(i); break;
|
||||
case CpuType::NecDsp: addrInfo = { i, SnesMemoryType::DspProgramRom }; break;
|
||||
case CpuType::Gameboy: addrInfo = _gameboy->GetAbsoluteAddress(i); break;
|
||||
default: addrInfo = mappings->GetAbsoluteAddress(i); break;
|
||||
switch (cpuType)
|
||||
{
|
||||
case CpuType::Spc: addrInfo = _spc->GetAbsoluteAddress(i);
|
||||
break;
|
||||
case CpuType::NecDsp: addrInfo = {i, SnesMemoryType::DspProgramRom};
|
||||
break;
|
||||
case CpuType::Gameboy: addrInfo = _gameboy->GetAbsoluteAddress(i);
|
||||
break;
|
||||
default: addrInfo = mappings->GetAbsoluteAddress(i);
|
||||
break;
|
||||
}
|
||||
|
||||
if(addrInfo.Address < 0) {
|
||||
if (addrInfo.Address < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DisassemblerSource src = GetSource(addrInfo.Type);
|
||||
|
||||
DisassemblyInfo disassemblyInfo = (*src.Cache)[addrInfo.Address];
|
||||
|
||||
|
||||
uint8_t opSize = 0;
|
||||
uint8_t opCode = (src.Data + addrInfo.Address)[0];
|
||||
|
||||
bool isCode = addrInfo.Type == SnesMemoryType::PrgRom ? _cdl->IsCode(addrInfo.Address) : false;
|
||||
bool isData = addrInfo.Type == SnesMemoryType::PrgRom ? _cdl->IsData(addrInfo.Address) : false;
|
||||
|
||||
if(disassemblyInfo.IsInitialized()) {
|
||||
if (disassemblyInfo.IsInitialized())
|
||||
{
|
||||
opSize = disassemblyInfo.GetOpSize();
|
||||
} else if((isData && disData) || (!isData && !isCode && disUnident)) {
|
||||
}
|
||||
else if ((isData && disData) || (!isData && !isCode && disUnident))
|
||||
{
|
||||
opSize = DisassemblyInfo::GetOpSize(opCode, 0, cpuType);
|
||||
}
|
||||
|
||||
if(opSize > 0) {
|
||||
if(inUnknownBlock || inVerifiedBlock) {
|
||||
int flags = LineFlags::BlockEnd | (inVerifiedBlock ? LineFlags::VerifiedData : 0) | (((inVerifiedBlock && showData) || (inUnknownBlock && showUnident)) ? LineFlags::ShowAsData : 0);
|
||||
if (opSize > 0)
|
||||
{
|
||||
if (inUnknownBlock || inVerifiedBlock)
|
||||
{
|
||||
int flags = LineFlags::BlockEnd | (inVerifiedBlock ? LineFlags::VerifiedData : 0) | (
|
||||
((inVerifiedBlock && showData) || (inUnknownBlock && showUnident)) ? LineFlags::ShowAsData : 0);
|
||||
results.push_back(DisassemblyResult(prevAddrInfo, i - 1, flags));
|
||||
inUnknownBlock = false;
|
||||
inVerifiedBlock = false;
|
||||
}
|
||||
byteCounter = 0;
|
||||
|
||||
if(addrInfo.Type == SnesMemoryType::PrgRom && _cdl->IsSubEntryPoint(addrInfo.Address)) {
|
||||
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::SubStart | LineFlags::BlockStart | LineFlags::VerifiedCode));
|
||||
if (addrInfo.Type == SnesMemoryType::PrgRom && _cdl->IsSubEntryPoint(addrInfo.Address))
|
||||
{
|
||||
results.push_back(DisassemblyResult(addrInfo, i,
|
||||
LineFlags::SubStart | LineFlags::BlockStart | LineFlags::VerifiedCode));
|
||||
}
|
||||
|
||||
if(_labelManager->GetLabelAndComment(addrInfo, labelInfo)) {
|
||||
if (_labelManager->GetLabelAndComment(addrInfo, labelInfo))
|
||||
{
|
||||
bool hasMultipleComment = labelInfo.Comment.find_first_of('\n') != string::npos;
|
||||
if(hasMultipleComment) {
|
||||
if (hasMultipleComment)
|
||||
{
|
||||
int16_t lineCount = 0;
|
||||
for(char c : labelInfo.Comment) {
|
||||
if(c == '\n') {
|
||||
for (char c : labelInfo.Comment)
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Comment, lineCount));
|
||||
lineCount++;
|
||||
}
|
||||
|
@ -293,42 +338,62 @@ void Disassembler::Disassemble(CpuType cpuType)
|
|||
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Comment, lineCount));
|
||||
}
|
||||
|
||||
if(labelInfo.Label.size()) {
|
||||
if (labelInfo.Label.size())
|
||||
{
|
||||
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Label));
|
||||
}
|
||||
|
||||
if(!hasMultipleComment && labelInfo.Comment.size()) {
|
||||
if (!hasMultipleComment && labelInfo.Comment.size())
|
||||
{
|
||||
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::Comment));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
results.push_back(DisassemblyResult(addrInfo, i));
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
results.push_back(DisassemblyResult(addrInfo, i));
|
||||
}
|
||||
|
||||
//Move to the end of the instruction (but realign disassembly if another valid instruction is found)
|
||||
//This can sometimes happen if the 2nd byte of BRK/COP is reused as the first byte of the next instruction
|
||||
//Also required when disassembling unvalidated data as code (to realign once we find verified code)
|
||||
for(int j = 1, max = (int)(*src.Cache).size(); j < opSize && addrInfo.Address + j < max; j++) {
|
||||
if((*src.Cache)[addrInfo.Address + j].IsInitialized()) {
|
||||
for (int j = 1, max = (int)(*src.Cache).size(); j < opSize && addrInfo.Address + j < max; j++)
|
||||
{
|
||||
if ((*src.Cache)[addrInfo.Address + j].IsInitialized())
|
||||
{
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if(DisassemblyInfo::IsReturnInstruction(opCode, cpuType)) {
|
||||
if (DisassemblyInfo::IsReturnInstruction(opCode, cpuType))
|
||||
{
|
||||
//End of function
|
||||
results.push_back(DisassemblyResult(-1, LineFlags::VerifiedCode | LineFlags::BlockEnd));
|
||||
}
|
||||
} else {
|
||||
if(showData || showUnident) {
|
||||
if((isData && inUnknownBlock) || (!isData && inVerifiedBlock)) {
|
||||
if(isData && inUnknownBlock) {
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (showData || showUnident)
|
||||
{
|
||||
if ((isData && inUnknownBlock) || (!isData && inVerifiedBlock))
|
||||
{
|
||||
if (isData && inUnknownBlock)
|
||||
{
|
||||
//In an unknown block and the next byte is data, end the block
|
||||
results.push_back(DisassemblyResult(prevAddrInfo, i - 1, LineFlags::BlockEnd | (showUnident ? LineFlags::ShowAsData : 0)));
|
||||
} else if(!isData && inVerifiedBlock) {
|
||||
results.push_back(DisassemblyResult(prevAddrInfo, i - 1,
|
||||
LineFlags::BlockEnd | (showUnident ? LineFlags::ShowAsData : 0)));
|
||||
}
|
||||
else if (!isData && inVerifiedBlock)
|
||||
{
|
||||
//In a verified data block and the next byte is unknown, end the block
|
||||
results.push_back(DisassemblyResult(prevAddrInfo, i - 1, LineFlags::BlockEnd | LineFlags::VerifiedData | (showData ? LineFlags::ShowAsData : 0)));
|
||||
results.push_back(DisassemblyResult(prevAddrInfo, i - 1,
|
||||
LineFlags::BlockEnd | LineFlags::VerifiedData | (showData
|
||||
? LineFlags::ShowAsData
|
||||
: 0)));
|
||||
}
|
||||
inUnknownBlock = false;
|
||||
inVerifiedBlock = false;
|
||||
|
@ -336,29 +401,45 @@ void Disassembler::Disassemble(CpuType cpuType)
|
|||
}
|
||||
}
|
||||
|
||||
if(byteCounter > 0) {
|
||||
if (byteCounter > 0)
|
||||
{
|
||||
//If showing as hex data, add a new data line every 8 bytes
|
||||
byteCounter--;
|
||||
if(byteCounter == 0) {
|
||||
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::ShowAsData | (isData ? LineFlags::VerifiedData : 0)));
|
||||
if (byteCounter == 0)
|
||||
{
|
||||
results.push_back(DisassemblyResult(addrInfo, i,
|
||||
LineFlags::ShowAsData | (isData ? LineFlags::VerifiedData : 0)));
|
||||
byteCounter = 8;
|
||||
}
|
||||
} else if(!inUnknownBlock && !inVerifiedBlock) {
|
||||
}
|
||||
else if (!inUnknownBlock && !inVerifiedBlock)
|
||||
{
|
||||
//If not in a block, start a new block based on the current byte's type (data vs unidentified)
|
||||
bool showAsData = (isData && showData) || ((!isData && !isCode) && showUnident);
|
||||
if(isData) {
|
||||
if (isData)
|
||||
{
|
||||
inVerifiedBlock = true;
|
||||
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::BlockStart | LineFlags::VerifiedData | (showAsData ? LineFlags::ShowAsData : 0)));
|
||||
} else {
|
||||
results.push_back(DisassemblyResult(addrInfo, i,
|
||||
LineFlags::BlockStart | LineFlags::VerifiedData | (showAsData
|
||||
? LineFlags::ShowAsData
|
||||
: 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
inUnknownBlock = true;
|
||||
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::BlockStart | (showAsData ? LineFlags::ShowAsData : 0)));
|
||||
results.push_back(DisassemblyResult(addrInfo, i,
|
||||
LineFlags::BlockStart | (showAsData ? LineFlags::ShowAsData : 0)));
|
||||
}
|
||||
|
||||
if(showAsData) {
|
||||
if (showAsData)
|
||||
{
|
||||
//If showing data as hex, add the first row of the block
|
||||
results.push_back(DisassemblyResult(addrInfo, i, LineFlags::ShowAsData | (isData ? LineFlags::VerifiedData : 0)));
|
||||
results.push_back(DisassemblyResult(addrInfo, i,
|
||||
LineFlags::ShowAsData | (isData ? LineFlags::VerifiedData : 0)));
|
||||
byteCounter = 8;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//If not showing the data at all, display 1 empty line
|
||||
results.push_back(DisassemblyResult(-1, LineFlags::None | (isData ? LineFlags::VerifiedData : 0)));
|
||||
}
|
||||
|
@ -366,16 +447,19 @@ void Disassembler::Disassemble(CpuType cpuType)
|
|||
}
|
||||
}
|
||||
|
||||
if(inUnknownBlock || inVerifiedBlock) {
|
||||
int flags = LineFlags::BlockEnd | (inVerifiedBlock ? LineFlags::VerifiedData : 0) | (((inVerifiedBlock && showData) || (inUnknownBlock && showUnident)) ? LineFlags::ShowAsData : 0);
|
||||
if (inUnknownBlock || inVerifiedBlock)
|
||||
{
|
||||
int flags = LineFlags::BlockEnd | (inVerifiedBlock ? LineFlags::VerifiedData : 0) | (
|
||||
((inVerifiedBlock && showData) || (inUnknownBlock && showUnident)) ? LineFlags::ShowAsData : 0);
|
||||
results.push_back(DisassemblyResult(addrInfo, maxAddr, flags));
|
||||
}
|
||||
}
|
||||
|
||||
DisassemblyInfo Disassembler::GetDisassemblyInfo(AddressInfo &info, uint32_t cpuAddress, uint8_t cpuFlags, CpuType type)
|
||||
DisassemblyInfo Disassembler::GetDisassemblyInfo(AddressInfo& info, uint32_t cpuAddress, uint8_t cpuFlags, CpuType type)
|
||||
{
|
||||
DisassemblyInfo disassemblyInfo = (*GetSource(info.Type).Cache)[info.Address];
|
||||
if(!disassemblyInfo.IsInitialized()) {
|
||||
if (!disassemblyInfo.IsInitialized())
|
||||
{
|
||||
disassemblyInfo.Initialize(cpuAddress, cpuFlags, type, _memoryDumper);
|
||||
}
|
||||
return disassemblyInfo;
|
||||
|
@ -400,14 +484,20 @@ uint32_t Disassembler::GetLineIndex(CpuType type, uint32_t cpuAddress)
|
|||
auto lock = _disassemblyLock.AcquireSafe();
|
||||
vector<DisassemblyResult>& source = _disassemblyResult[(int)type];
|
||||
uint32_t lastAddress = 0;
|
||||
for(size_t i = 1; i < source.size(); i++) {
|
||||
if(source[i].CpuAddress < 0 || (source[i].Flags & LineFlags::SubStart) | (source[i].Flags & LineFlags::Label) || ((source[i].Flags & LineFlags::Comment) && source[i].CommentLine >= 0)) {
|
||||
for (size_t i = 1; i < source.size(); i++)
|
||||
{
|
||||
if (source[i].CpuAddress < 0 || (source[i].Flags & LineFlags::SubStart) | (source[i].Flags & LineFlags::Label) ||
|
||||
((source[i].Flags & LineFlags::Comment) && source[i].CommentLine >= 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(cpuAddress == (uint32_t)source[i].CpuAddress) {
|
||||
if (cpuAddress == (uint32_t)source[i].CpuAddress)
|
||||
{
|
||||
return (uint32_t)i;
|
||||
} else if(cpuAddress >= lastAddress && cpuAddress < (uint32_t)source[i].CpuAddress) {
|
||||
}
|
||||
else if (cpuAddress >= lastAddress && cpuAddress < (uint32_t)source[i].CpuAddress)
|
||||
{
|
||||
return (uint32_t)i - 1;
|
||||
}
|
||||
|
||||
|
@ -416,53 +506,68 @@ uint32_t Disassembler::GetLineIndex(CpuType type, uint32_t cpuAddress)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &data)
|
||||
bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData& data)
|
||||
{
|
||||
auto lock =_disassemblyLock.AcquireSafe();
|
||||
auto lock = _disassemblyLock.AcquireSafe();
|
||||
|
||||
vector<DisassemblyResult>& source = _disassemblyResult[(int)type];
|
||||
SnesMemoryType memType = DebugUtilities::GetCpuMemoryType(type);
|
||||
int32_t maxAddr = type == CpuType::Spc ? 0xFFFF : 0xFFFFFF;
|
||||
if(lineIndex < source.size()) {
|
||||
if (lineIndex < source.size())
|
||||
{
|
||||
DisassemblyResult result = source[lineIndex];
|
||||
data.Address = -1;
|
||||
data.AbsoluteAddress = -1;
|
||||
data.EffectiveAddress = -1;
|
||||
data.Flags = result.Flags;
|
||||
|
||||
switch(result.Address.Type) {
|
||||
default: break;
|
||||
case SnesMemoryType::GbPrgRom:
|
||||
case SnesMemoryType::PrgRom: data.Flags |= (uint8_t)LineFlags::PrgRom; break;
|
||||
switch (result.Address.Type)
|
||||
{
|
||||
default: break;
|
||||
case SnesMemoryType::GbPrgRom:
|
||||
case SnesMemoryType::PrgRom: data.Flags |= (uint8_t)LineFlags::PrgRom;
|
||||
break;
|
||||
|
||||
case SnesMemoryType::GbWorkRam:
|
||||
case SnesMemoryType::WorkRam: data.Flags |= (uint8_t)LineFlags::WorkRam; break;
|
||||
|
||||
case SnesMemoryType::GbCartRam:
|
||||
case SnesMemoryType::SaveRam: data.Flags |= (uint8_t)LineFlags::SaveRam; break;
|
||||
case SnesMemoryType::GbWorkRam:
|
||||
case SnesMemoryType::WorkRam: data.Flags |= (uint8_t)LineFlags::WorkRam;
|
||||
break;
|
||||
|
||||
case SnesMemoryType::GbCartRam:
|
||||
case SnesMemoryType::SaveRam: data.Flags |= (uint8_t)LineFlags::SaveRam;
|
||||
break;
|
||||
}
|
||||
|
||||
bool isBlockStartEnd = (data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd)) != 0;
|
||||
if(!isBlockStartEnd && result.Address.Address >= 0) {
|
||||
if((data.Flags & LineFlags::ShowAsData)) {
|
||||
if (!isBlockStartEnd && result.Address.Address >= 0)
|
||||
{
|
||||
if ((data.Flags & LineFlags::ShowAsData))
|
||||
{
|
||||
FastString str(".db", 3);
|
||||
int nextAddr = lineIndex < source.size() - 2 ? (source[lineIndex+1].CpuAddress + 1) : (maxAddr + 1);
|
||||
for(int i = 0; i < 8 && result.CpuAddress+i < nextAddr; i++) {
|
||||
int nextAddr = lineIndex < source.size() - 2 ? (source[lineIndex + 1].CpuAddress + 1) : (maxAddr + 1);
|
||||
for (int i = 0; i < 8 && result.CpuAddress + i < nextAddr; i++)
|
||||
{
|
||||
str.Write(" $", 2);
|
||||
str.Write(HexUtilities::ToHexChar(_memoryDumper->GetMemoryValue(memType, result.CpuAddress + i)), 2);
|
||||
}
|
||||
data.Address = result.CpuAddress;
|
||||
data.AbsoluteAddress = result.Address.Address;
|
||||
memcpy(data.Text, str.ToString(), str.GetSize());
|
||||
} else if((data.Flags & LineFlags::Comment) && result.CommentLine >= 0) {
|
||||
string comment = ";" + StringUtilities::Split(_labelManager->GetComment(result.Address), '\n')[result.CommentLine];
|
||||
}
|
||||
else if ((data.Flags & LineFlags::Comment) && result.CommentLine >= 0)
|
||||
{
|
||||
string comment = ";" + StringUtilities::Split(_labelManager->GetComment(result.Address), '\n')[result.
|
||||
CommentLine];
|
||||
data.Flags |= LineFlags::VerifiedCode;
|
||||
memcpy(data.Comment, comment.c_str(), std::min<int>((int)comment.size(), 1000));
|
||||
} else if(data.Flags & LineFlags::Label) {
|
||||
}
|
||||
else if (data.Flags & LineFlags::Label)
|
||||
{
|
||||
string label = _labelManager->GetLabel(result.Address) + ":";
|
||||
data.Flags |= LineFlags::VerifiedCode;
|
||||
memcpy(data.Text, label.c_str(), std::min<int>((int)label.size(), 1000));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
DisassemblerSource src = GetSource(result.Address.Type);
|
||||
DisassemblyInfo disInfo = (*src.Cache)[result.Address.Address];
|
||||
CpuType lineCpuType = disInfo.IsInitialized() ? disInfo.GetCpuType() : type;
|
||||
|
@ -470,88 +575,119 @@ bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &d
|
|||
data.Address = result.CpuAddress;
|
||||
data.AbsoluteAddress = result.Address.Address;
|
||||
|
||||
switch(lineCpuType) {
|
||||
case CpuType::Cpu:
|
||||
case CpuType::Sa1: {
|
||||
switch (lineCpuType)
|
||||
{
|
||||
case CpuType::Cpu:
|
||||
case CpuType::Sa1:
|
||||
{
|
||||
CpuState state = type == CpuType::Sa1 ? _sa1->GetCpuState() : _cpu->GetState();
|
||||
state.PC = (uint16_t)result.CpuAddress;
|
||||
state.K = (result.CpuAddress >> 16);
|
||||
|
||||
if(!disInfo.IsInitialized()) {
|
||||
if (!disInfo.IsInitialized())
|
||||
{
|
||||
disInfo = DisassemblyInfo(src.Data + result.Address.Address, state.PS, lineCpuType);
|
||||
} else {
|
||||
data.Flags |= (result.Address.Type != SnesMemoryType::PrgRom || _cdl->IsCode(data.AbsoluteAddress)) ? LineFlags::VerifiedCode : LineFlags::UnexecutedCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Flags |= (result.Address.Type != SnesMemoryType::PrgRom || _cdl->IsCode(data.AbsoluteAddress))
|
||||
? LineFlags::VerifiedCode
|
||||
: LineFlags::UnexecutedCode;
|
||||
}
|
||||
|
||||
data.OpSize = disInfo.GetOpSize();
|
||||
data.EffectiveAddress = disInfo.GetEffectiveAddress(_console, &state, lineCpuType);
|
||||
|
||||
if(data.EffectiveAddress >= 0) {
|
||||
if (data.EffectiveAddress >= 0)
|
||||
{
|
||||
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType, data.ValueSize);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ValueSize = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CpuType::Spc: {
|
||||
|
||||
case CpuType::Spc:
|
||||
{
|
||||
SpcState state = _spc->GetState();
|
||||
state.PC = (uint16_t)result.CpuAddress;
|
||||
|
||||
if(!disInfo.IsInitialized()) {
|
||||
if (!disInfo.IsInitialized())
|
||||
{
|
||||
disInfo = DisassemblyInfo(src.Data + result.Address.Address, 0, CpuType::Spc);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Flags |= LineFlags::VerifiedCode;
|
||||
}
|
||||
|
||||
data.OpSize = disInfo.GetOpSize();
|
||||
data.EffectiveAddress = disInfo.GetEffectiveAddress(_console, &state, lineCpuType);
|
||||
if(data.EffectiveAddress >= 0) {
|
||||
if (data.EffectiveAddress >= 0)
|
||||
{
|
||||
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType, data.ValueSize);
|
||||
data.ValueSize = 1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ValueSize = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CpuType::Gsu: {
|
||||
case CpuType::Gsu:
|
||||
{
|
||||
GsuState state = _gsu->GetState();
|
||||
if(!disInfo.IsInitialized()) {
|
||||
if (!disInfo.IsInitialized())
|
||||
{
|
||||
disInfo = DisassemblyInfo(src.Data + result.Address.Address, 0, CpuType::Gsu);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Flags |= LineFlags::VerifiedCode;
|
||||
}
|
||||
|
||||
data.OpSize = disInfo.GetOpSize();
|
||||
data.EffectiveAddress = disInfo.GetEffectiveAddress(_console, &state, lineCpuType);
|
||||
if(data.EffectiveAddress >= 0) {
|
||||
if (data.EffectiveAddress >= 0)
|
||||
{
|
||||
data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType, data.ValueSize);
|
||||
data.ValueSize = 2;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ValueSize = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CpuType::NecDsp:
|
||||
case CpuType::Cx4:
|
||||
if(!disInfo.IsInitialized()) {
|
||||
disInfo = DisassemblyInfo(src.Data + result.Address.Address, 0, type);
|
||||
} else {
|
||||
data.Flags |= LineFlags::VerifiedCode;
|
||||
}
|
||||
|
||||
data.OpSize = disInfo.GetOpSize();
|
||||
data.EffectiveAddress = -1;
|
||||
data.ValueSize = 0;
|
||||
break;
|
||||
case CpuType::NecDsp:
|
||||
case CpuType::Cx4:
|
||||
if (!disInfo.IsInitialized())
|
||||
{
|
||||
disInfo = DisassemblyInfo(src.Data + result.Address.Address, 0, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Flags |= LineFlags::VerifiedCode;
|
||||
}
|
||||
|
||||
case CpuType::Gameboy: {
|
||||
data.OpSize = disInfo.GetOpSize();
|
||||
data.EffectiveAddress = -1;
|
||||
data.ValueSize = 0;
|
||||
break;
|
||||
|
||||
case CpuType::Gameboy:
|
||||
{
|
||||
GbCpuState state = _gameboy->GetCpu()->GetState();
|
||||
if(!disInfo.IsInitialized()) {
|
||||
if (!disInfo.IsInitialized())
|
||||
{
|
||||
disInfo = DisassemblyInfo(src.Data + result.Address.Address, 0, CpuType::Gameboy);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Flags |= LineFlags::VerifiedCode;
|
||||
}
|
||||
|
||||
|
@ -568,27 +704,38 @@ bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &d
|
|||
|
||||
disInfo.GetByteCode(data.ByteCode);
|
||||
|
||||
if(data.Flags & LineFlags::Comment) {
|
||||
if (data.Flags & LineFlags::Comment)
|
||||
{
|
||||
string comment = ";" + _labelManager->GetComment(result.Address);
|
||||
memcpy(data.Comment, comment.c_str(), std::min<int>((int)comment.size(), 1000));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Comment[0] = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(data.Flags & LineFlags::SubStart) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data.Flags & LineFlags::SubStart)
|
||||
{
|
||||
string label = _labelManager->GetLabel(result.Address);
|
||||
if(label.empty()) {
|
||||
if (label.empty())
|
||||
{
|
||||
label = "sub start";
|
||||
}
|
||||
memcpy(data.Text, label.c_str(), label.size() + 1);
|
||||
} else if(data.Flags & LineFlags::BlockStart) {
|
||||
}
|
||||
else if (data.Flags & LineFlags::BlockStart)
|
||||
{
|
||||
string label = (data.Flags & LineFlags::VerifiedData) ? "data" : "unidentified";
|
||||
memcpy(data.Text, label.c_str(), label.size() + 1);
|
||||
}
|
||||
|
||||
if(data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd)) {
|
||||
if(!(data.Flags & (LineFlags::ShowAsData | LineFlags::SubStart))) {
|
||||
if (data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd))
|
||||
{
|
||||
if (!(data.Flags & (LineFlags::ShowAsData | LineFlags::SubStart)))
|
||||
{
|
||||
//For hidden blocks, give the start/end lines an address
|
||||
data.Address = result.CpuAddress;
|
||||
data.AbsoluteAddress = result.Address.Address;
|
||||
|
@ -600,25 +747,31 @@ bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &d
|
|||
return false;
|
||||
}
|
||||
|
||||
int32_t Disassembler::SearchDisassembly(CpuType type, const char *searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards)
|
||||
int32_t Disassembler::SearchDisassembly(CpuType type, const char* searchString, int32_t startPosition,
|
||||
int32_t endPosition, bool searchBackwards)
|
||||
{
|
||||
auto lock = _disassemblyLock.AcquireSafe();
|
||||
vector<DisassemblyResult>& source = _disassemblyResult[(int)type];
|
||||
int step = searchBackwards ? -1 : 1;
|
||||
CodeLineData lineData = {};
|
||||
for(int i = startPosition; i != endPosition; i += step) {
|
||||
for (int i = startPosition; i != endPosition; i += step)
|
||||
{
|
||||
GetLineData(type, i, lineData);
|
||||
string line = lineData.Text;
|
||||
std::transform(line.begin(), line.end(), line.begin(), ::tolower);
|
||||
|
||||
if(line.find(searchString) != string::npos) {
|
||||
if (line.find(searchString) != string::npos)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
//Continue search from start/end of document
|
||||
if(!searchBackwards && i == (int)(source.size() - 1)) {
|
||||
if (!searchBackwards && i == (int)(source.size() - 1))
|
||||
{
|
||||
i = 0;
|
||||
} else if(searchBackwards && i == 0) {
|
||||
}
|
||||
else if (searchBackwards && i == 0)
|
||||
{
|
||||
i = (int32_t)(source.size() - 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,16 +24,16 @@ enum class CpuType : uint8_t;
|
|||
|
||||
struct DisassemblerSource
|
||||
{
|
||||
uint8_t *Data;
|
||||
vector<DisassemblyInfo> *Cache;
|
||||
uint8_t* Data;
|
||||
vector<DisassemblyInfo>* Cache;
|
||||
uint32_t Size;
|
||||
};
|
||||
|
||||
class Disassembler
|
||||
{
|
||||
private:
|
||||
MemoryManager *_memoryManager;
|
||||
Console *_console;
|
||||
MemoryManager* _memoryManager;
|
||||
Console* _console;
|
||||
Cpu* _cpu;
|
||||
Spc* _spc;
|
||||
Gsu* _gsu;
|
||||
|
@ -42,17 +42,17 @@ private:
|
|||
NecDsp* _necDsp;
|
||||
Gameboy* _gameboy;
|
||||
EmuSettings* _settings;
|
||||
Debugger *_debugger;
|
||||
Debugger* _debugger;
|
||||
shared_ptr<CodeDataLogger> _cdl;
|
||||
shared_ptr<LabelManager> _labelManager;
|
||||
MemoryDumper *_memoryDumper;
|
||||
MemoryDumper* _memoryDumper;
|
||||
|
||||
DisassemblerSource _sources[(int)SnesMemoryType::Register] = {};
|
||||
vector<DisassemblyInfo> _disassemblyCache[(int)SnesMemoryType::Register];
|
||||
|
||||
SimpleLock _disassemblyLock;
|
||||
vector<DisassemblyResult> _disassemblyResult[(int)DebugUtilities::GetLastCpuType()+1];
|
||||
bool _needDisassemble[(int)DebugUtilities::GetLastCpuType()+1];
|
||||
vector<DisassemblyResult> _disassemblyResult[(int)DebugUtilities::GetLastCpuType() + 1];
|
||||
bool _needDisassemble[(int)DebugUtilities::GetLastCpuType() + 1];
|
||||
|
||||
void InitSource(SnesMemoryType type);
|
||||
DisassemblerSource& GetSource(SnesMemoryType type);
|
||||
|
@ -61,16 +61,17 @@ private:
|
|||
public:
|
||||
Disassembler(shared_ptr<Console> console, shared_ptr<CodeDataLogger> cdl, Debugger* debugger);
|
||||
|
||||
uint32_t BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuType type);
|
||||
uint32_t BuildCache(AddressInfo& addrInfo, uint8_t cpuFlags, CpuType type);
|
||||
void ResetPrgCache();
|
||||
void InvalidateCache(AddressInfo addrInfo, CpuType type);
|
||||
void Disassemble(CpuType cpuType);
|
||||
|
||||
DisassemblyInfo GetDisassemblyInfo(AddressInfo &info, uint32_t cpuAddress, uint8_t cpuFlags, CpuType type);
|
||||
DisassemblyInfo GetDisassemblyInfo(AddressInfo& info, uint32_t cpuAddress, uint8_t cpuFlags, CpuType type);
|
||||
|
||||
void RefreshDisassembly(CpuType type);
|
||||
uint32_t GetLineCount(CpuType type);
|
||||
uint32_t GetLineIndex(CpuType type, uint32_t cpuAddress);
|
||||
bool GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &data);
|
||||
int32_t SearchDisassembly(CpuType type, const char* searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards);
|
||||
};
|
||||
bool GetLineData(CpuType type, uint32_t lineIndex, CodeLineData& data);
|
||||
int32_t SearchDisassembly(CpuType type, const char* searchString, int32_t startPosition, int32_t endPosition,
|
||||
bool searchBackwards);
|
||||
};
|
||||
|
|
|
@ -18,12 +18,12 @@ DisassemblyInfo::DisassemblyInfo()
|
|||
{
|
||||
}
|
||||
|
||||
DisassemblyInfo::DisassemblyInfo(uint8_t *opPointer, uint8_t cpuFlags, CpuType type)
|
||||
DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, uint8_t cpuFlags, CpuType type)
|
||||
{
|
||||
Initialize(opPointer, cpuFlags, type);
|
||||
}
|
||||
|
||||
void DisassemblyInfo::Initialize(uint8_t *opPointer, uint8_t cpuFlags, CpuType type)
|
||||
void DisassemblyInfo::Initialize(uint8_t* opPointer, uint8_t cpuFlags, CpuType type)
|
||||
{
|
||||
_cpuType = type;
|
||||
_flags = cpuFlags;
|
||||
|
@ -43,8 +43,9 @@ void DisassemblyInfo::Initialize(uint32_t cpuAddress, uint8_t cpuFlags, CpuType
|
|||
|
||||
_opSize = GetOpSize(_byteCode[0], _flags, _cpuType);
|
||||
|
||||
for(int i = 1; i < _opSize; i++) {
|
||||
_byteCode[i] = memoryDumper->GetMemoryValue(cpuMemType, cpuAddress+i);
|
||||
for (int i = 1; i < _opSize; i++)
|
||||
{
|
||||
_byteCode[i] = memoryDumper->GetMemoryValue(cpuMemType, cpuAddress + i);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
|
@ -65,37 +66,45 @@ void DisassemblyInfo::Reset()
|
|||
_initialized = false;
|
||||
}
|
||||
|
||||
void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings)
|
||||
void DisassemblyInfo::GetDisassembly(string& out, uint32_t memoryAddr, LabelManager* labelManager,
|
||||
EmuSettings* settings)
|
||||
{
|
||||
switch(_cpuType) {
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
CpuDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings);
|
||||
break;
|
||||
switch (_cpuType)
|
||||
{
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
CpuDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings);
|
||||
break;
|
||||
|
||||
case CpuType::Spc: SpcDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break;
|
||||
case CpuType::NecDsp: NecDspDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break;
|
||||
case CpuType::Gsu: GsuDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break;
|
||||
case CpuType::Cx4: Cx4DisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break;
|
||||
case CpuType::Gameboy: GameboyDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break;
|
||||
case CpuType::Spc: SpcDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings);
|
||||
break;
|
||||
case CpuType::NecDsp: NecDspDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings);
|
||||
break;
|
||||
case CpuType::Gsu: GsuDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings);
|
||||
break;
|
||||
case CpuType::Cx4: Cx4DisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings);
|
||||
break;
|
||||
case CpuType::Gameboy: GameboyDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t DisassemblyInfo::GetEffectiveAddress(Console *console, void *cpuState, CpuType cpuType)
|
||||
int32_t DisassemblyInfo::GetEffectiveAddress(Console* console, void* cpuState, CpuType cpuType)
|
||||
{
|
||||
switch(_cpuType) {
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
return CpuDisUtils::GetEffectiveAddress(*this, console, *(CpuState*)cpuState, cpuType);
|
||||
switch (_cpuType)
|
||||
{
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
return CpuDisUtils::GetEffectiveAddress(*this, console, *(CpuState*)cpuState, cpuType);
|
||||
|
||||
case CpuType::Spc: return SpcDisUtils::GetEffectiveAddress(*this, console, *(SpcState*)cpuState);
|
||||
case CpuType::Gsu: return GsuDisUtils::GetEffectiveAddress(*this, console, *(GsuState*)cpuState);
|
||||
case CpuType::Spc: return SpcDisUtils::GetEffectiveAddress(*this, console, *(SpcState*)cpuState);
|
||||
case CpuType::Gsu: return GsuDisUtils::GetEffectiveAddress(*this, console, *(GsuState*)cpuState);
|
||||
|
||||
case CpuType::Cx4:
|
||||
case CpuType::NecDsp:
|
||||
return -1;
|
||||
case CpuType::Cx4:
|
||||
case CpuType::NecDsp:
|
||||
return -1;
|
||||
|
||||
case CpuType::Gameboy: return GameboyDisUtils::GetEffectiveAddress(*this, console, *(GbCpuState*)cpuState);
|
||||
case CpuType::Gameboy: return GameboyDisUtils::GetEffectiveAddress(*this, console, *(GbCpuState*)cpuState);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -130,12 +139,14 @@ void DisassemblyInfo::GetByteCode(uint8_t copyBuffer[4])
|
|||
memcpy(copyBuffer, _byteCode, _opSize);
|
||||
}
|
||||
|
||||
void DisassemblyInfo::GetByteCode(string &out)
|
||||
void DisassemblyInfo::GetByteCode(string& out)
|
||||
{
|
||||
FastString str;
|
||||
for(int i = 0; i < _opSize; i++) {
|
||||
for (int i = 0; i < _opSize; i++)
|
||||
{
|
||||
str.WriteAll('$', HexUtilities::ToHex(_byteCode[i]));
|
||||
if(i < _opSize - 1) {
|
||||
if (i < _opSize - 1)
|
||||
{
|
||||
str.Write(' ');
|
||||
}
|
||||
}
|
||||
|
@ -144,27 +155,33 @@ void DisassemblyInfo::GetByteCode(string &out)
|
|||
|
||||
uint8_t DisassemblyInfo::GetOpSize(uint8_t opCode, uint8_t flags, CpuType type)
|
||||
{
|
||||
switch(type) {
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
return CpuDisUtils::GetOpSize(opCode, flags);
|
||||
switch (type)
|
||||
{
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
return CpuDisUtils::GetOpSize(opCode, flags);
|
||||
|
||||
case CpuType::Spc: return SpcDisUtils::GetOpSize(opCode);
|
||||
|
||||
case CpuType::Gsu:
|
||||
if(opCode >= 0x05 && opCode <= 0x0F) {
|
||||
return 2;
|
||||
} else if(opCode >= 0xA0 && opCode <= 0xAF) {
|
||||
return 2;
|
||||
} else if(opCode >= 0xF0 && opCode <= 0xFF) {
|
||||
return 3;
|
||||
}
|
||||
return 1;
|
||||
case CpuType::Spc: return SpcDisUtils::GetOpSize(opCode);
|
||||
|
||||
case CpuType::NecDsp: return 3;
|
||||
case CpuType::Cx4: return 2;
|
||||
|
||||
case CpuType::Gameboy: return GameboyDisUtils::GetOpSize(opCode);
|
||||
case CpuType::Gsu:
|
||||
if (opCode >= 0x05 && opCode <= 0x0F)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (opCode >= 0xA0 && opCode <= 0xAF)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (opCode >= 0xF0 && opCode <= 0xFF)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
return 1;
|
||||
|
||||
case CpuType::NecDsp: return 3;
|
||||
case CpuType::Cx4: return 2;
|
||||
|
||||
case CpuType::Gameboy: return GameboyDisUtils::GetOpSize(opCode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -172,19 +189,20 @@ uint8_t DisassemblyInfo::GetOpSize(uint8_t opCode, uint8_t flags, CpuType type)
|
|||
//TODO: This is never called, removed?
|
||||
bool DisassemblyInfo::IsJumpToSub(uint8_t opCode, CpuType type)
|
||||
{
|
||||
switch(type) {
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
return opCode == 0x20 || opCode == 0x22 || opCode == 0xFC; //JSR, JSL
|
||||
switch (type)
|
||||
{
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
return opCode == 0x20 || opCode == 0x22 || opCode == 0xFC; //JSR, JSL
|
||||
|
||||
case CpuType::Spc: return opCode == 0x3F || opCode == 0x0F; //JSR, BRK
|
||||
|
||||
case CpuType::Gameboy: return GameboyDisUtils::IsJumpToSub(opCode);
|
||||
|
||||
case CpuType::Gsu:
|
||||
case CpuType::NecDsp:
|
||||
case CpuType::Cx4:
|
||||
return false;
|
||||
case CpuType::Spc: return opCode == 0x3F || opCode == 0x0F; //JSR, BRK
|
||||
|
||||
case CpuType::Gameboy: return GameboyDisUtils::IsJumpToSub(opCode);
|
||||
|
||||
case CpuType::Gsu:
|
||||
case CpuType::NecDsp:
|
||||
case CpuType::Cx4:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -192,52 +210,62 @@ bool DisassemblyInfo::IsJumpToSub(uint8_t opCode, CpuType type)
|
|||
bool DisassemblyInfo::IsReturnInstruction(uint8_t opCode, CpuType type)
|
||||
{
|
||||
//RTS/RTI
|
||||
switch(type) {
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
return opCode == 0x60 || opCode == 0x6B || opCode == 0x40;
|
||||
switch (type)
|
||||
{
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
return opCode == 0x60 || opCode == 0x6B || opCode == 0x40;
|
||||
|
||||
case CpuType::Spc: return opCode == 0x6F || opCode == 0x7F;
|
||||
case CpuType::Spc: return opCode == 0x6F || opCode == 0x7F;
|
||||
|
||||
case CpuType::Gameboy: return GameboyDisUtils::IsReturnInstruction(opCode);
|
||||
case CpuType::Gameboy: return GameboyDisUtils::IsReturnInstruction(opCode);
|
||||
|
||||
case CpuType::Gsu:
|
||||
case CpuType::NecDsp:
|
||||
case CpuType::Cx4:
|
||||
return false;
|
||||
case CpuType::Gsu:
|
||||
case CpuType::NecDsp:
|
||||
case CpuType::Cx4:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DisassemblyInfo::IsUnconditionalJump()
|
||||
{
|
||||
uint8_t opCode = GetOpCode();
|
||||
switch(_cpuType) {
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
if(opCode == 0x00 || opCode == 0x20 || opCode == 0x40 || opCode == 0x60 || opCode == 0x80 || opCode == 0x22 || opCode == 0xFC || opCode == 0x6B || opCode == 0x4C || opCode == 0x5C || opCode == 0x6C || opCode == 0x7C || opCode == 0x02) {
|
||||
//Jumps, RTI, RTS, BRK, COP, etc., stop disassembling
|
||||
return true;
|
||||
} else if(opCode == 0x28) {
|
||||
//PLP, stop disassembling because the 8-bit/16-bit flags could change
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case CpuType::Gameboy:
|
||||
if(opCode == 0x18 || opCode == 0xC3 || opCode == 0xEA || opCode == 0xCD || opCode == 0xC9 || opCode == 0xD9 || opCode == 0xC7 || opCode == 0xCF || opCode == 0xD7 || opCode == 0xDF || opCode == 0xE7 || opCode == 0xEF || opCode == 0xF7 || opCode == 0xFF) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case CpuType::Gsu:
|
||||
case CpuType::Spc:
|
||||
case CpuType::Cx4:
|
||||
switch (_cpuType)
|
||||
{
|
||||
case CpuType::Sa1:
|
||||
case CpuType::Cpu:
|
||||
if (opCode == 0x00 || opCode == 0x20 || opCode == 0x40 || opCode == 0x60 || opCode == 0x80 || opCode == 0x22 ||
|
||||
opCode == 0xFC || opCode == 0x6B || opCode == 0x4C || opCode == 0x5C || opCode == 0x6C || opCode == 0x7C ||
|
||||
opCode == 0x02)
|
||||
{
|
||||
//Jumps, RTI, RTS, BRK, COP, etc., stop disassembling
|
||||
return true;
|
||||
}
|
||||
else if (opCode == 0x28)
|
||||
{
|
||||
//PLP, stop disassembling because the 8-bit/16-bit flags could change
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case CpuType::NecDsp:
|
||||
return false;
|
||||
case CpuType::Gameboy:
|
||||
if (opCode == 0x18 || opCode == 0xC3 || opCode == 0xEA || opCode == 0xCD || opCode == 0xC9 || opCode == 0xD9 ||
|
||||
opCode == 0xC7 || opCode == 0xCF || opCode == 0xD7 || opCode == 0xDF || opCode == 0xE7 || opCode == 0xEF ||
|
||||
opCode == 0xF7 || opCode == 0xFF)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case CpuType::Gsu:
|
||||
case CpuType::Spc:
|
||||
case CpuType::Cx4:
|
||||
return true;
|
||||
|
||||
case CpuType::NecDsp:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -245,13 +273,17 @@ bool DisassemblyInfo::IsUnconditionalJump()
|
|||
|
||||
void DisassemblyInfo::UpdateCpuFlags(uint8_t& cpuFlags)
|
||||
{
|
||||
if(_cpuType == CpuType::Cpu || _cpuType == CpuType::Sa1) {
|
||||
if (_cpuType == CpuType::Cpu || _cpuType == CpuType::Sa1)
|
||||
{
|
||||
uint8_t opCode = GetOpCode();
|
||||
if(opCode == 0xC2) {
|
||||
if (opCode == 0xC2)
|
||||
{
|
||||
//REP, update the flags and keep disassembling
|
||||
uint8_t flags = GetByteCode()[1];
|
||||
cpuFlags &= ~flags;
|
||||
} else if(opCode == 0xE2) {
|
||||
}
|
||||
else if (opCode == 0xE2)
|
||||
{
|
||||
//SEP, update the flags and keep disassembling
|
||||
uint8_t flags = GetByteCode()[1];
|
||||
cpuFlags |= flags;
|
||||
|
@ -259,12 +291,16 @@ void DisassemblyInfo::UpdateCpuFlags(uint8_t& cpuFlags)
|
|||
}
|
||||
}
|
||||
|
||||
uint16_t DisassemblyInfo::GetMemoryValue(uint32_t effectiveAddress, MemoryDumper *memoryDumper, SnesMemoryType memType, uint8_t &valueSize)
|
||||
uint16_t DisassemblyInfo::GetMemoryValue(uint32_t effectiveAddress, MemoryDumper* memoryDumper, SnesMemoryType memType,
|
||||
uint8_t& valueSize)
|
||||
{
|
||||
if((_cpuType == CpuType::Spc || _cpuType == CpuType::Gameboy) || (_flags & ProcFlags::MemoryMode8)) {
|
||||
if ((_cpuType == CpuType::Spc || _cpuType == CpuType::Gameboy) || (_flags & ProcFlags::MemoryMode8))
|
||||
{
|
||||
valueSize = 1;
|
||||
return memoryDumper->GetMemoryValue(memType, effectiveAddress);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
valueSize = 2;
|
||||
return memoryDumper->GetMemoryValueWord(memType, effectiveAddress);
|
||||
}
|
||||
|
|
|
@ -20,16 +20,16 @@ private:
|
|||
|
||||
public:
|
||||
DisassemblyInfo();
|
||||
DisassemblyInfo(uint8_t *opPointer, uint8_t cpuFlags, CpuType type);
|
||||
DisassemblyInfo(uint8_t* opPointer, uint8_t cpuFlags, CpuType type);
|
||||
|
||||
void Initialize(uint8_t *opPointer, uint8_t cpuFlags, CpuType type);
|
||||
void Initialize(uint8_t* opPointer, uint8_t cpuFlags, CpuType type);
|
||||
void Initialize(uint32_t cpuAddress, uint8_t cpuFlags, CpuType type, MemoryDumper* memoryDumper);
|
||||
bool IsInitialized();
|
||||
bool IsValid(uint8_t cpuFlags);
|
||||
void Reset();
|
||||
|
||||
void GetDisassembly(string &out, uint32_t memoryAddr, LabelManager *labelManager, EmuSettings* settings);
|
||||
|
||||
void GetDisassembly(string& out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings);
|
||||
|
||||
CpuType GetCpuType();
|
||||
uint8_t GetOpCode();
|
||||
uint8_t GetOpSize();
|
||||
|
@ -37,7 +37,7 @@ public:
|
|||
uint8_t* GetByteCode();
|
||||
|
||||
void GetByteCode(uint8_t copyBuffer[4]);
|
||||
void GetByteCode(string &out);
|
||||
void GetByteCode(string& out);
|
||||
|
||||
static uint8_t GetOpSize(uint8_t opCode, uint8_t flags, CpuType type);
|
||||
static bool IsJumpToSub(uint8_t opCode, CpuType type);
|
||||
|
@ -46,7 +46,7 @@ public:
|
|||
bool IsUnconditionalJump();
|
||||
void UpdateCpuFlags(uint8_t& cpuFlags);
|
||||
|
||||
int32_t GetEffectiveAddress(Console *console, void *cpuState, CpuType type);
|
||||
uint16_t GetMemoryValue(uint32_t effectiveAddress, MemoryDumper *memoryDumper, SnesMemoryType memType, uint8_t &valueSize);
|
||||
int32_t GetEffectiveAddress(Console* console, void* cpuState, CpuType type);
|
||||
uint16_t GetMemoryValue(uint32_t effectiveAddress, MemoryDumper* memoryDumper, SnesMemoryType memType,
|
||||
uint8_t& valueSize);
|
||||
};
|
||||
|
||||
|
|
|
@ -5,19 +5,21 @@
|
|||
#include "MessageManager.h"
|
||||
#include "../Utilities/Serializer.h"
|
||||
|
||||
static constexpr uint8_t _transferByteCount[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
|
||||
static constexpr uint8_t _transferByteCount[8] = {1, 2, 2, 4, 4, 4, 2, 4};
|
||||
static constexpr uint8_t _transferOffset[8][4] = {
|
||||
{ 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 0, 0, 0, 0 }, { 0, 0, 1, 1 },
|
||||
{ 0, 1, 2, 3 }, { 0, 1, 0, 1 }, { 0, 0, 0, 0 }, { 0, 0, 1, 1 }
|
||||
{0, 0, 0, 0}, {0, 1, 0, 1}, {0, 0, 0, 0}, {0, 0, 1, 1},
|
||||
{0, 1, 2, 3}, {0, 1, 0, 1}, {0, 0, 0, 0}, {0, 0, 1, 1}
|
||||
};
|
||||
|
||||
DmaController::DmaController(MemoryManager *memoryManager)
|
||||
DmaController::DmaController(MemoryManager* memoryManager)
|
||||
{
|
||||
_memoryManager = memoryManager;
|
||||
Reset();
|
||||
|
||||
for(int j = 0; j < 8; j++) {
|
||||
for(int i = 0; i <= 0x0A; i++) {
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
for (int i = 0; i <= 0x0A; i++)
|
||||
{
|
||||
Write(0x4300 | i | (j << 4), 0xFF);
|
||||
}
|
||||
}
|
||||
|
@ -33,36 +35,47 @@ void DmaController::Reset()
|
|||
_dmaPending = false;
|
||||
_needToProcess = false;
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
_channel[i].DmaActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DmaController::CopyDmaByte(uint32_t addressBusA, uint16_t addressBusB, bool fromBtoA)
|
||||
{
|
||||
if(fromBtoA) {
|
||||
if(addressBusB != 0x2180 || !_memoryManager->IsWorkRam(addressBusA)) {
|
||||
if (fromBtoA)
|
||||
{
|
||||
if (addressBusB != 0x2180 || !_memoryManager->IsWorkRam(addressBusA))
|
||||
{
|
||||
uint8_t valToWrite = _memoryManager->ReadDma(addressBusB, false);
|
||||
_memoryManager->WriteDma(addressBusA, valToWrite, true);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//$2180->WRAM do cause a write to occur (but no read), but the value written is invalid
|
||||
_memoryManager->IncMasterClock4();
|
||||
_memoryManager->WriteDma(addressBusA, 0xFF, true);
|
||||
}
|
||||
} else {
|
||||
if(addressBusB != 0x2180 || !_memoryManager->IsWorkRam(addressBusA)) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (addressBusB != 0x2180 || !_memoryManager->IsWorkRam(addressBusA))
|
||||
{
|
||||
uint8_t valToWrite = _memoryManager->ReadDma(addressBusA, true);
|
||||
_memoryManager->WriteDma(addressBusB, valToWrite, false);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//WRAM->$2180 does not cause a write to occur
|
||||
_memoryManager->IncMasterClock8();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DmaController::RunDma(DmaChannelConfig &channel)
|
||||
void DmaController::RunDma(DmaChannelConfig& channel)
|
||||
{
|
||||
if(!channel.DmaActive) {
|
||||
if (!channel.DmaActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -70,10 +83,11 @@ void DmaController::RunDma(DmaChannelConfig &channel)
|
|||
_memoryManager->IncMasterClock8();
|
||||
ProcessPendingTransfers();
|
||||
|
||||
const uint8_t *transferOffsets = _transferOffset[channel.TransferMode];
|
||||
const uint8_t* transferOffsets = _transferOffset[channel.TransferMode];
|
||||
|
||||
uint8_t i = 0;
|
||||
do {
|
||||
do
|
||||
{
|
||||
//Manual DMA transfers run to the end of the transfer when started
|
||||
CopyDmaByte(
|
||||
(channel.SrcBank << 16) | channel.SrcAddress,
|
||||
|
@ -81,14 +95,16 @@ void DmaController::RunDma(DmaChannelConfig &channel)
|
|||
channel.InvertDirection
|
||||
);
|
||||
|
||||
if(!channel.FixedTransfer) {
|
||||
if (!channel.FixedTransfer)
|
||||
{
|
||||
channel.SrcAddress += channel.Decrement ? -1 : 1;
|
||||
}
|
||||
|
||||
channel.TransferSize--;
|
||||
i++;
|
||||
ProcessPendingTransfers();
|
||||
} while(channel.TransferSize > 0 && channel.DmaActive);
|
||||
}
|
||||
while (channel.TransferSize > 0 && channel.DmaActive);
|
||||
|
||||
channel.DmaActive = false;
|
||||
}
|
||||
|
@ -97,31 +113,37 @@ bool DmaController::InitHdmaChannels()
|
|||
{
|
||||
_hdmaInitPending = false;
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
//Reset internal flags on every frame, whether or not the channels are enabled
|
||||
_channel[i].HdmaFinished = false;
|
||||
_channel[i].DoTransfer = false; //not resetting this causes graphical glitches in some games (Aladdin, Super Ghouls and Ghosts)
|
||||
_channel[i].DoTransfer = false;
|
||||
//not resetting this causes graphical glitches in some games (Aladdin, Super Ghouls and Ghosts)
|
||||
}
|
||||
|
||||
if(!_hdmaChannels) {
|
||||
if (!_hdmaChannels)
|
||||
{
|
||||
//No channels are enabled, no more processing needs to be done
|
||||
UpdateNeedToProcessFlag();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool needSync = !HasActiveDmaChannel();
|
||||
if(needSync) {
|
||||
if (needSync)
|
||||
{
|
||||
SyncStartDma();
|
||||
}
|
||||
_memoryManager->IncMasterClock8();
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
DmaChannelConfig &ch = _channel[i];
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
DmaChannelConfig& ch = _channel[i];
|
||||
|
||||
//Set DoTransfer to true for all channels if any HDMA channel is enabled
|
||||
ch.DoTransfer = true;
|
||||
|
||||
if(_hdmaChannels & (1 << i)) {
|
||||
if (_hdmaChannels & (1 << i))
|
||||
{
|
||||
//"1. Copy AAddress into Address."
|
||||
ch.HdmaTableAddress = ch.SrcAddress;
|
||||
ch.DmaActive = false;
|
||||
|
@ -131,12 +153,14 @@ bool DmaController::InitHdmaChannels()
|
|||
_memoryManager->IncMasterClock4();
|
||||
|
||||
ch.HdmaTableAddress++;
|
||||
if(ch.HdmaLineCounterAndRepeat == 0) {
|
||||
if (ch.HdmaLineCounterAndRepeat == 0)
|
||||
{
|
||||
ch.HdmaFinished = true;
|
||||
}
|
||||
|
||||
//3. Load Indirect Address, if necessary.
|
||||
if(ch.HdmaIndirectAddressing) {
|
||||
if (ch.HdmaIndirectAddressing)
|
||||
{
|
||||
uint8_t lsb = _memoryManager->ReadDma((ch.SrcBank << 16) | ch.HdmaTableAddress++, true);
|
||||
_memoryManager->IncMasterClock4();
|
||||
uint8_t msb = _memoryManager->ReadDma((ch.SrcBank << 16) | ch.HdmaTableAddress++, true);
|
||||
|
@ -146,7 +170,8 @@ bool DmaController::InitHdmaChannels()
|
|||
}
|
||||
}
|
||||
|
||||
if(needSync) {
|
||||
if (needSync)
|
||||
{
|
||||
SyncEndDma();
|
||||
}
|
||||
|
||||
|
@ -154,15 +179,17 @@ bool DmaController::InitHdmaChannels()
|
|||
return true;
|
||||
}
|
||||
|
||||
void DmaController::RunHdmaTransfer(DmaChannelConfig &channel)
|
||||
void DmaController::RunHdmaTransfer(DmaChannelConfig& channel)
|
||||
{
|
||||
const uint8_t *transferOffsets = _transferOffset[channel.TransferMode];
|
||||
const uint8_t* transferOffsets = _transferOffset[channel.TransferMode];
|
||||
uint8_t transferByteCount = _transferByteCount[channel.TransferMode];
|
||||
channel.DmaActive = false;
|
||||
|
||||
uint8_t i = 0;
|
||||
if(channel.HdmaIndirectAddressing) {
|
||||
do {
|
||||
if (channel.HdmaIndirectAddressing)
|
||||
{
|
||||
do
|
||||
{
|
||||
CopyDmaByte(
|
||||
(channel.HdmaBank << 16) | channel.TransferSize,
|
||||
0x2100 | (channel.DestAddress + transferOffsets[i]),
|
||||
|
@ -170,9 +197,13 @@ void DmaController::RunHdmaTransfer(DmaChannelConfig &channel)
|
|||
);
|
||||
channel.TransferSize++;
|
||||
i++;
|
||||
} while(i < transferByteCount);
|
||||
} else {
|
||||
do {
|
||||
}
|
||||
while (i < transferByteCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
CopyDmaByte(
|
||||
(channel.SrcBank << 16) | channel.HdmaTableAddress,
|
||||
0x2100 | (channel.DestAddress + transferOffsets[i]),
|
||||
|
@ -180,7 +211,8 @@ void DmaController::RunHdmaTransfer(DmaChannelConfig &channel)
|
|||
);
|
||||
channel.HdmaTableAddress++;
|
||||
i++;
|
||||
} while(i < transferByteCount);
|
||||
}
|
||||
while (i < transferByteCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,13 +227,16 @@ void DmaController::SyncEndDma()
|
|||
{
|
||||
//"Then wait 2-8 master cycles to reach a whole number of CPU Clock cycles since the pause"
|
||||
uint8_t cpuSpeed = _memoryManager->GetCpuSpeed();
|
||||
_memoryManager->IncrementMasterClockValue(cpuSpeed - ((_memoryManager->GetMasterClock() - _dmaStartClock) % cpuSpeed));
|
||||
_memoryManager->IncrementMasterClockValue(
|
||||
cpuSpeed - ((_memoryManager->GetMasterClock() - _dmaStartClock) % cpuSpeed));
|
||||
}
|
||||
|
||||
bool DmaController::HasActiveDmaChannel()
|
||||
{
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(_channel[i].DmaActive) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (_channel[i].DmaActive)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -212,13 +247,15 @@ bool DmaController::ProcessHdmaChannels()
|
|||
{
|
||||
_hdmaPending = false;
|
||||
|
||||
if(!_hdmaChannels) {
|
||||
if (!_hdmaChannels)
|
||||
{
|
||||
UpdateNeedToProcessFlag();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool needSync = !HasActiveDmaChannel();
|
||||
if(needSync) {
|
||||
if (needSync)
|
||||
{
|
||||
SyncStartDma();
|
||||
}
|
||||
_memoryManager->IncMasterClock8();
|
||||
|
@ -226,20 +263,24 @@ bool DmaController::ProcessHdmaChannels()
|
|||
uint8_t originalActiveChannel = _activeChannel;
|
||||
|
||||
//Run all the DMA transfers for each channel first, before fetching data for the next scanline
|
||||
for(int i = 0; i < 8; i++) {
|
||||
DmaChannelConfig &ch = _channel[i];
|
||||
if((_hdmaChannels & (1 << i)) == 0) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
DmaChannelConfig& ch = _channel[i];
|
||||
if ((_hdmaChannels & (1 << i)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ch.DmaActive = false;
|
||||
|
||||
if(ch.HdmaFinished) {
|
||||
if (ch.HdmaFinished)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
//1. If DoTransfer is false, skip to step 3.
|
||||
if(ch.DoTransfer) {
|
||||
if (ch.DoTransfer)
|
||||
{
|
||||
//2. For the number of bytes (1, 2, or 4) required for this Transfer Mode...
|
||||
_activeChannel = DmaController::HdmaChannelFlag | i;
|
||||
RunHdmaTransfer(ch);
|
||||
|
@ -247,9 +288,11 @@ bool DmaController::ProcessHdmaChannels()
|
|||
}
|
||||
|
||||
//Update the channel's state & fetch data for the next scanline
|
||||
for(int i = 0; i < 8; i++) {
|
||||
DmaChannelConfig &ch = _channel[i];
|
||||
if((_hdmaChannels & (1 << i)) == 0 || ch.HdmaFinished) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
DmaChannelConfig& ch = _channel[i];
|
||||
if ((_hdmaChannels & (1 << i)) == 0 || ch.HdmaFinished)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -265,32 +308,38 @@ bool DmaController::ProcessHdmaChannels()
|
|||
_memoryManager->IncMasterClock4();
|
||||
|
||||
//5. If Line Counter is zero...
|
||||
if((ch.HdmaLineCounterAndRepeat & 0x7F) == 0) {
|
||||
if ((ch.HdmaLineCounterAndRepeat & 0x7F) == 0)
|
||||
{
|
||||
ch.HdmaLineCounterAndRepeat = newCounter;
|
||||
ch.HdmaTableAddress++;
|
||||
|
||||
//"b. If Addressing Mode is Indirect, read two bytes from Address into Indirect Address(and increment Address by two bytes)."
|
||||
if(ch.HdmaIndirectAddressing) {
|
||||
if(ch.HdmaLineCounterAndRepeat == 0 && IsLastActiveHdmaChannel(i)) {
|
||||
if (ch.HdmaIndirectAddressing)
|
||||
{
|
||||
if (ch.HdmaLineCounterAndRepeat == 0 && IsLastActiveHdmaChannel(i))
|
||||
{
|
||||
//"One oddity: if $43xA is 0 and this is the last active HDMA channel for this scanline, only load one byte for Address,
|
||||
//and use the $00 for the low byte.So Address ends up incremented one less than otherwise expected, and one less CPU Cycle is used."
|
||||
uint8_t msb = _memoryManager->ReadDma((ch.SrcBank << 16) | ch.HdmaTableAddress++, true);
|
||||
_memoryManager->IncMasterClock4();
|
||||
ch.TransferSize = (msb << 8);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//"If a new indirect address is required, 16 master cycles are taken to load it."
|
||||
uint8_t lsb = _memoryManager->ReadDma((ch.SrcBank << 16) | ch.HdmaTableAddress++, true);
|
||||
_memoryManager->IncMasterClock4();
|
||||
|
||||
|
||||
uint8_t msb = _memoryManager->ReadDma((ch.SrcBank << 16) | ch.HdmaTableAddress++, true);
|
||||
_memoryManager->IncMasterClock4();
|
||||
|
||||
ch.TransferSize = (msb << 8) | lsb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//"c. If $43xA is zero, terminate this HDMA channel for this frame. The bit in $420c is not cleared, though, so it may be automatically restarted next frame."
|
||||
if(ch.HdmaLineCounterAndRepeat == 0) {
|
||||
if (ch.HdmaLineCounterAndRepeat == 0)
|
||||
{
|
||||
ch.HdmaFinished = true;
|
||||
}
|
||||
|
||||
|
@ -299,7 +348,8 @@ bool DmaController::ProcessHdmaChannels()
|
|||
}
|
||||
}
|
||||
|
||||
if(needSync) {
|
||||
if (needSync)
|
||||
{
|
||||
//If we ran a HDMA transfer, sync
|
||||
SyncEndDma();
|
||||
}
|
||||
|
@ -312,8 +362,10 @@ bool DmaController::ProcessHdmaChannels()
|
|||
|
||||
bool DmaController::IsLastActiveHdmaChannel(uint8_t channel)
|
||||
{
|
||||
for(int i = channel + 1; i < 8; i++) {
|
||||
if((_hdmaChannels & (1 << i)) && !_channel[i].HdmaFinished) {
|
||||
for (int i = channel + 1; i < 8; i++)
|
||||
{
|
||||
if ((_hdmaChannels & (1 << i)) && !_channel[i].HdmaFinished)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -328,7 +380,8 @@ void DmaController::UpdateNeedToProcessFlag()
|
|||
|
||||
void DmaController::BeginHdmaTransfer()
|
||||
{
|
||||
if(_hdmaChannels) {
|
||||
if (_hdmaChannels)
|
||||
{
|
||||
_hdmaPending = true;
|
||||
UpdateNeedToProcessFlag();
|
||||
}
|
||||
|
@ -342,33 +395,42 @@ void DmaController::BeginHdmaInit()
|
|||
|
||||
bool DmaController::ProcessPendingTransfers()
|
||||
{
|
||||
if(!_needToProcess) {
|
||||
if (!_needToProcess)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_dmaStartDelay) {
|
||||
if (_dmaStartDelay)
|
||||
{
|
||||
_dmaStartDelay = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_hdmaPending) {
|
||||
if (_hdmaPending)
|
||||
{
|
||||
return ProcessHdmaChannels();
|
||||
} else if(_hdmaInitPending) {
|
||||
}
|
||||
else if (_hdmaInitPending)
|
||||
{
|
||||
return InitHdmaChannels();
|
||||
} else if(_dmaPending) {
|
||||
}
|
||||
else if (_dmaPending)
|
||||
{
|
||||
_dmaPending = false;
|
||||
|
||||
SyncStartDma();
|
||||
_memoryManager->IncMasterClock8();
|
||||
ProcessPendingTransfers();
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(_channel[i].DmaActive) {
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (_channel[i].DmaActive)
|
||||
{
|
||||
_activeChannel = i;
|
||||
RunDma(_channel[i]);
|
||||
RunDma(_channel[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SyncEndDma();
|
||||
UpdateNeedToProcessFlag();
|
||||
|
||||
|
@ -380,16 +442,21 @@ bool DmaController::ProcessPendingTransfers()
|
|||
|
||||
void DmaController::Write(uint16_t addr, uint8_t value)
|
||||
{
|
||||
switch(addr) {
|
||||
case 0x420B: {
|
||||
switch (addr)
|
||||
{
|
||||
case 0x420B:
|
||||
{
|
||||
//MDMAEN - DMA Enable
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(value & (1 << i)) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (value & (1 << i))
|
||||
{
|
||||
_channel[i].DmaActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(value) {
|
||||
if (value)
|
||||
{
|
||||
_dmaPending = true;
|
||||
_dmaStartDelay = true;
|
||||
UpdateNeedToProcessFlag();
|
||||
|
@ -397,15 +464,22 @@ void DmaController::Write(uint16_t addr, uint8_t value)
|
|||
break;
|
||||
}
|
||||
|
||||
case 0x420C:
|
||||
//HDMAEN - HDMA Enable
|
||||
_hdmaChannels = value;
|
||||
break;
|
||||
case 0x420C:
|
||||
//HDMAEN - HDMA Enable
|
||||
_hdmaChannels = value;
|
||||
break;
|
||||
|
||||
case 0x4300: case 0x4310: case 0x4320: case 0x4330: case 0x4340: case 0x4350: case 0x4360: case 0x4370:
|
||||
case 0x4300:
|
||||
case 0x4310:
|
||||
case 0x4320:
|
||||
case 0x4330:
|
||||
case 0x4340:
|
||||
case 0x4350:
|
||||
case 0x4360:
|
||||
case 0x4370:
|
||||
{
|
||||
//DMAPx - DMA Control for Channel x
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.InvertDirection = (value & 0x80) != 0;
|
||||
channel.HdmaIndirectAddressing = (value & 0x40) != 0;
|
||||
channel.UnusedFlag = (value & 0x20) != 0;
|
||||
|
@ -415,101 +489,192 @@ void DmaController::Write(uint16_t addr, uint8_t value)
|
|||
break;
|
||||
}
|
||||
|
||||
case 0x4301: case 0x4311: case 0x4321: case 0x4331: case 0x4341: case 0x4351: case 0x4361: case 0x4371:
|
||||
case 0x4301:
|
||||
case 0x4311:
|
||||
case 0x4321:
|
||||
case 0x4331:
|
||||
case 0x4341:
|
||||
case 0x4351:
|
||||
case 0x4361:
|
||||
case 0x4371:
|
||||
{
|
||||
//BBADx - DMA Destination Register for Channel x
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.DestAddress = value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4302: case 0x4312: case 0x4322: case 0x4332: case 0x4342: case 0x4352: case 0x4362: case 0x4372:
|
||||
case 0x4302:
|
||||
case 0x4312:
|
||||
case 0x4322:
|
||||
case 0x4332:
|
||||
case 0x4342:
|
||||
case 0x4352:
|
||||
case 0x4362:
|
||||
case 0x4372:
|
||||
{
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.SrcAddress = (channel.SrcAddress & 0xFF00) | value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4303: case 0x4313: case 0x4323: case 0x4333: case 0x4343: case 0x4353: case 0x4363: case 0x4373:
|
||||
case 0x4303:
|
||||
case 0x4313:
|
||||
case 0x4323:
|
||||
case 0x4333:
|
||||
case 0x4343:
|
||||
case 0x4353:
|
||||
case 0x4363:
|
||||
case 0x4373:
|
||||
{
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.SrcAddress = (channel.SrcAddress & 0xFF) | (value << 8);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4304: case 0x4314: case 0x4324: case 0x4334: case 0x4344: case 0x4354: case 0x4364: case 0x4374:
|
||||
case 0x4304:
|
||||
case 0x4314:
|
||||
case 0x4324:
|
||||
case 0x4334:
|
||||
case 0x4344:
|
||||
case 0x4354:
|
||||
case 0x4364:
|
||||
case 0x4374:
|
||||
{
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.SrcBank = value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4305: case 0x4315: case 0x4325: case 0x4335: case 0x4345: case 0x4355: case 0x4365: case 0x4375:
|
||||
case 0x4305:
|
||||
case 0x4315:
|
||||
case 0x4325:
|
||||
case 0x4335:
|
||||
case 0x4345:
|
||||
case 0x4355:
|
||||
case 0x4365:
|
||||
case 0x4375:
|
||||
{
|
||||
//DASxL - DMA Size / HDMA Indirect Address low byte(x = 0 - 7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.TransferSize = (channel.TransferSize & 0xFF00) | value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4306: case 0x4316: case 0x4326: case 0x4336: case 0x4346: case 0x4356: case 0x4366: case 0x4376:
|
||||
case 0x4306:
|
||||
case 0x4316:
|
||||
case 0x4326:
|
||||
case 0x4336:
|
||||
case 0x4346:
|
||||
case 0x4356:
|
||||
case 0x4366:
|
||||
case 0x4376:
|
||||
{
|
||||
//DASxL - DMA Size / HDMA Indirect Address low byte(x = 0 - 7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.TransferSize = (channel.TransferSize & 0xFF) | (value << 8);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4307: case 0x4317: case 0x4327: case 0x4337: case 0x4347: case 0x4357: case 0x4367: case 0x4377:
|
||||
case 0x4307:
|
||||
case 0x4317:
|
||||
case 0x4327:
|
||||
case 0x4337:
|
||||
case 0x4347:
|
||||
case 0x4357:
|
||||
case 0x4367:
|
||||
case 0x4377:
|
||||
{
|
||||
//DASBx - HDMA Indirect Address bank byte (x=0-7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.HdmaBank = value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4308: case 0x4318: case 0x4328: case 0x4338: case 0x4348: case 0x4358: case 0x4368: case 0x4378:
|
||||
case 0x4308:
|
||||
case 0x4318:
|
||||
case 0x4328:
|
||||
case 0x4338:
|
||||
case 0x4348:
|
||||
case 0x4358:
|
||||
case 0x4368:
|
||||
case 0x4378:
|
||||
{
|
||||
//A2AxL - HDMA Table Address low byte (x=0-7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.HdmaTableAddress = (channel.HdmaTableAddress & 0xFF00) | value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x4309: case 0x4319: case 0x4329: case 0x4339: case 0x4349: case 0x4359: case 0x4369: case 0x4379:
|
||||
case 0x4309:
|
||||
case 0x4319:
|
||||
case 0x4329:
|
||||
case 0x4339:
|
||||
case 0x4349:
|
||||
case 0x4359:
|
||||
case 0x4369:
|
||||
case 0x4379:
|
||||
{
|
||||
//A2AxH - HDMA Table Address high byte (x=0-7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.HdmaTableAddress = (value << 8) | (channel.HdmaTableAddress & 0xFF);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x430A: case 0x431A: case 0x432A: case 0x433A: case 0x434A: case 0x435A: case 0x436A: case 0x437A:
|
||||
case 0x430A:
|
||||
case 0x431A:
|
||||
case 0x432A:
|
||||
case 0x433A:
|
||||
case 0x434A:
|
||||
case 0x435A:
|
||||
case 0x436A:
|
||||
case 0x437A:
|
||||
{
|
||||
//DASBx - HDMA Indirect Address bank byte (x=0-7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.HdmaLineCounterAndRepeat = value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x430B: case 0x431B: case 0x432B: case 0x433B: case 0x434B: case 0x435B: case 0x436B: case 0x437B:
|
||||
case 0x430F: case 0x431F: case 0x432F: case 0x433F: case 0x434F: case 0x435F: case 0x436F: case 0x437F:
|
||||
case 0x430B:
|
||||
case 0x431B:
|
||||
case 0x432B:
|
||||
case 0x433B:
|
||||
case 0x434B:
|
||||
case 0x435B:
|
||||
case 0x436B:
|
||||
case 0x437B:
|
||||
case 0x430F:
|
||||
case 0x431F:
|
||||
case 0x432F:
|
||||
case 0x433F:
|
||||
case 0x434F:
|
||||
case 0x435F:
|
||||
case 0x436F:
|
||||
case 0x437F:
|
||||
{
|
||||
//UNUSEDx - HDMA Indirect Address bank byte (x=0-7)
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
channel.UnusedByte = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t DmaController::Read(uint16_t addr)
|
||||
{
|
||||
switch(addr) {
|
||||
case 0x4300: case 0x4310: case 0x4320: case 0x4330: case 0x4340: case 0x4350: case 0x4360: case 0x4370:
|
||||
switch (addr)
|
||||
{
|
||||
case 0x4300:
|
||||
case 0x4310:
|
||||
case 0x4320:
|
||||
case 0x4330:
|
||||
case 0x4340:
|
||||
case 0x4350:
|
||||
case 0x4360:
|
||||
case 0x4370:
|
||||
{
|
||||
//DMAPx - DMA Control for Channel x
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return (
|
||||
(channel.InvertDirection ? 0x80 : 0) |
|
||||
(channel.HdmaIndirectAddressing ? 0x40 : 0) |
|
||||
|
@ -520,81 +685,164 @@ uint8_t DmaController::Read(uint16_t addr)
|
|||
);
|
||||
}
|
||||
|
||||
case 0x4301: case 0x4311: case 0x4321: case 0x4331: case 0x4341: case 0x4351: case 0x4361: case 0x4371:
|
||||
case 0x4301:
|
||||
case 0x4311:
|
||||
case 0x4321:
|
||||
case 0x4331:
|
||||
case 0x4341:
|
||||
case 0x4351:
|
||||
case 0x4361:
|
||||
case 0x4371:
|
||||
{
|
||||
//BBADx - DMA Destination Register for Channel x
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return channel.DestAddress;
|
||||
}
|
||||
|
||||
case 0x4302: case 0x4312: case 0x4322: case 0x4332: case 0x4342: case 0x4352: case 0x4362: case 0x4372:
|
||||
case 0x4302:
|
||||
case 0x4312:
|
||||
case 0x4322:
|
||||
case 0x4332:
|
||||
case 0x4342:
|
||||
case 0x4352:
|
||||
case 0x4362:
|
||||
case 0x4372:
|
||||
{
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return channel.SrcAddress & 0xFF;
|
||||
}
|
||||
|
||||
case 0x4303: case 0x4313: case 0x4323: case 0x4333: case 0x4343: case 0x4353: case 0x4363: case 0x4373:
|
||||
case 0x4303:
|
||||
case 0x4313:
|
||||
case 0x4323:
|
||||
case 0x4333:
|
||||
case 0x4343:
|
||||
case 0x4353:
|
||||
case 0x4363:
|
||||
case 0x4373:
|
||||
{
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return (channel.SrcAddress >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
case 0x4304: case 0x4314: case 0x4324: case 0x4334: case 0x4344: case 0x4354: case 0x4364: case 0x4374:
|
||||
case 0x4304:
|
||||
case 0x4314:
|
||||
case 0x4324:
|
||||
case 0x4334:
|
||||
case 0x4344:
|
||||
case 0x4354:
|
||||
case 0x4364:
|
||||
case 0x4374:
|
||||
{
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return channel.SrcBank;
|
||||
}
|
||||
|
||||
case 0x4305: case 0x4315: case 0x4325: case 0x4335: case 0x4345: case 0x4355: case 0x4365: case 0x4375:
|
||||
case 0x4305:
|
||||
case 0x4315:
|
||||
case 0x4325:
|
||||
case 0x4335:
|
||||
case 0x4345:
|
||||
case 0x4355:
|
||||
case 0x4365:
|
||||
case 0x4375:
|
||||
{
|
||||
//DASxL - DMA Size / HDMA Indirect Address low byte(x = 0 - 7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return channel.TransferSize & 0xFF;
|
||||
}
|
||||
|
||||
case 0x4306: case 0x4316: case 0x4326: case 0x4336: case 0x4346: case 0x4356: case 0x4366: case 0x4376:
|
||||
case 0x4306:
|
||||
case 0x4316:
|
||||
case 0x4326:
|
||||
case 0x4336:
|
||||
case 0x4346:
|
||||
case 0x4356:
|
||||
case 0x4366:
|
||||
case 0x4376:
|
||||
{
|
||||
//DASxL - DMA Size / HDMA Indirect Address low byte(x = 0 - 7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return (channel.TransferSize >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
case 0x4307: case 0x4317: case 0x4327: case 0x4337: case 0x4347: case 0x4357: case 0x4367: case 0x4377:
|
||||
case 0x4307:
|
||||
case 0x4317:
|
||||
case 0x4327:
|
||||
case 0x4337:
|
||||
case 0x4347:
|
||||
case 0x4357:
|
||||
case 0x4367:
|
||||
case 0x4377:
|
||||
{
|
||||
//DASBx - HDMA Indirect Address bank byte (x=0-7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return channel.HdmaBank;
|
||||
}
|
||||
|
||||
case 0x4308: case 0x4318: case 0x4328: case 0x4338: case 0x4348: case 0x4358: case 0x4368: case 0x4378:
|
||||
case 0x4308:
|
||||
case 0x4318:
|
||||
case 0x4328:
|
||||
case 0x4338:
|
||||
case 0x4348:
|
||||
case 0x4358:
|
||||
case 0x4368:
|
||||
case 0x4378:
|
||||
{
|
||||
//A2AxL - HDMA Table Address low byte (x=0-7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return channel.HdmaTableAddress & 0xFF;
|
||||
}
|
||||
|
||||
case 0x4309: case 0x4319: case 0x4329: case 0x4339: case 0x4349: case 0x4359: case 0x4369: case 0x4379:
|
||||
case 0x4309:
|
||||
case 0x4319:
|
||||
case 0x4329:
|
||||
case 0x4339:
|
||||
case 0x4349:
|
||||
case 0x4359:
|
||||
case 0x4369:
|
||||
case 0x4379:
|
||||
{
|
||||
//A2AxH - HDMA Table Address high byte (x=0-7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return (channel.HdmaTableAddress >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
case 0x430A: case 0x431A: case 0x432A: case 0x433A: case 0x434A: case 0x435A: case 0x436A: case 0x437A:
|
||||
case 0x430A:
|
||||
case 0x431A:
|
||||
case 0x432A:
|
||||
case 0x433A:
|
||||
case 0x434A:
|
||||
case 0x435A:
|
||||
case 0x436A:
|
||||
case 0x437A:
|
||||
{
|
||||
//DASBx - HDMA Indirect Address bank byte (x=0-7)
|
||||
DmaChannelConfig &channel = _channel[(addr & 0x70) >> 4];
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return channel.HdmaLineCounterAndRepeat;
|
||||
}
|
||||
|
||||
case 0x430B: case 0x431B: case 0x432B: case 0x433B: case 0x434B: case 0x435B: case 0x436B: case 0x437B:
|
||||
case 0x430F: case 0x431F: case 0x432F: case 0x433F: case 0x434F: case 0x435F: case 0x436F: case 0x437F:
|
||||
case 0x430B:
|
||||
case 0x431B:
|
||||
case 0x432B:
|
||||
case 0x433B:
|
||||
case 0x434B:
|
||||
case 0x435B:
|
||||
case 0x436B:
|
||||
case 0x437B:
|
||||
case 0x430F:
|
||||
case 0x431F:
|
||||
case 0x432F:
|
||||
case 0x433F:
|
||||
case 0x434F:
|
||||
case 0x435F:
|
||||
case 0x436F:
|
||||
case 0x437F:
|
||||
{
|
||||
//UNUSEDx - HDMA Indirect Address bank byte (x=0-7)
|
||||
DmaChannelConfig& channel = _channel[(addr & 0x70) >> 4];
|
||||
return channel.UnusedByte;
|
||||
}
|
||||
|
||||
}
|
||||
return _memoryManager->GetOpenBus();
|
||||
}
|
||||
|
@ -609,10 +857,11 @@ DmaChannelConfig DmaController::GetChannelConfig(uint8_t channel)
|
|||
return _channel[channel];
|
||||
}
|
||||
|
||||
void DmaController::Serialize(Serializer &s)
|
||||
void DmaController::Serialize(Serializer& s)
|
||||
{
|
||||
s.Stream(_hdmaPending, _hdmaChannels, _dmaPending, _dmaStartClock, _hdmaInitPending, _dmaStartDelay, _needToProcess);
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
s.Stream(
|
||||
_channel[i].Decrement, _channel[i].DestAddress, _channel[i].DoTransfer, _channel[i].FixedTransfer,
|
||||
_channel[i].HdmaBank, _channel[i].HdmaFinished, _channel[i].HdmaIndirectAddressing,
|
||||
|
|
|
@ -18,17 +18,17 @@ private:
|
|||
uint8_t _hdmaChannels = 0;
|
||||
bool _dmaPending = false;
|
||||
uint64_t _dmaStartClock = 0;
|
||||
|
||||
|
||||
uint8_t _activeChannel = 0; //Used by debugger's event viewer
|
||||
|
||||
DmaChannelConfig _channel[8] = {};
|
||||
MemoryManager *_memoryManager;
|
||||
|
||||
MemoryManager* _memoryManager;
|
||||
|
||||
void CopyDmaByte(uint32_t addressBusA, uint16_t addressBusB, bool fromBtoA);
|
||||
|
||||
void RunDma(DmaChannelConfig &channel);
|
||||
|
||||
void RunHdmaTransfer(DmaChannelConfig &channel);
|
||||
void RunDma(DmaChannelConfig& channel);
|
||||
|
||||
void RunHdmaTransfer(DmaChannelConfig& channel);
|
||||
bool ProcessHdmaChannels();
|
||||
bool IsLastActiveHdmaChannel(uint8_t channel);
|
||||
bool InitHdmaChannels();
|
||||
|
@ -40,7 +40,7 @@ private:
|
|||
bool HasActiveDmaChannel();
|
||||
|
||||
public:
|
||||
DmaController(MemoryManager *memoryManager);
|
||||
DmaController(MemoryManager* memoryManager);
|
||||
|
||||
void Reset();
|
||||
|
||||
|
@ -55,5 +55,5 @@ public:
|
|||
uint8_t GetActiveChannel();
|
||||
DmaChannelConfig GetChannelConfig(uint8_t channel);
|
||||
|
||||
void Serialize(Serializer &s) override;
|
||||
};
|
||||
void Serialize(Serializer& s) override;
|
||||
};
|
||||
|
|
|
@ -26,4 +26,4 @@ struct DmaChannelConfig
|
|||
bool UnusedFlag;
|
||||
|
||||
uint8_t UnusedByte; // 43xB and 43xF
|
||||
};
|
||||
};
|
||||
|
|
|
@ -18,34 +18,50 @@ protected:
|
|||
int _yScale;
|
||||
|
||||
virtual void InternalDraw() = 0;
|
||||
|
||||
__forceinline void DrawPixel(uint32_t x, uint32_t y, int color)
|
||||
{
|
||||
if(x < _overscan.Left || x >= (256 - _overscan.Right) || y < _overscan.Top || y >= (239 - _overscan.Bottom)) {
|
||||
if (x < _overscan.Left || x >= (256 - _overscan.Right) || y < _overscan.Top || y >= (239 - _overscan.Bottom))
|
||||
{
|
||||
//Out of bounds, skip drawing
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t alpha = (color & 0xFF000000);
|
||||
if(alpha > 0) {
|
||||
if(_yScale == 1) {
|
||||
if(alpha != 0xFF000000) {
|
||||
BlendColors((uint8_t*)&_argbBuffer[(y - _overscan.Top)*_lineWidth + (x - _overscan.Left)], (uint8_t*)&color);
|
||||
} else {
|
||||
_argbBuffer[(y - _overscan.Top)*_lineWidth + (x - _overscan.Left)] = color;
|
||||
if (alpha > 0)
|
||||
{
|
||||
if (_yScale == 1)
|
||||
{
|
||||
if (alpha != 0xFF000000)
|
||||
{
|
||||
BlendColors((uint8_t*)&_argbBuffer[(y - _overscan.Top) * _lineWidth + (x - _overscan.Left)],
|
||||
(uint8_t*)&color);
|
||||
}
|
||||
} else {
|
||||
int xPixelCount = _useIntegerScaling ? _yScale : (int)((x + 1)*_xScale) - (int)(x*_xScale);
|
||||
else
|
||||
{
|
||||
_argbBuffer[(y - _overscan.Top) * _lineWidth + (x - _overscan.Left)] = color;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int xPixelCount = _useIntegerScaling ? _yScale : (int)((x + 1) * _xScale) - (int)(x * _xScale);
|
||||
x = (int)(x * (_useIntegerScaling ? _yScale : _xScale));
|
||||
y = (int)(y * _yScale);
|
||||
int top = (int)(_overscan.Top * _yScale);
|
||||
int left = (int)(_overscan.Left * _xScale);
|
||||
|
||||
for(int i = 0; i < _yScale; i++) {
|
||||
for(int j = 0; j < xPixelCount; j++) {
|
||||
if(alpha != 0xFF000000) {
|
||||
BlendColors((uint8_t*)&_argbBuffer[(y - top)*_lineWidth + i*_lineWidth + (x - left)+j], (uint8_t*)&color);
|
||||
} else {
|
||||
_argbBuffer[(y - top)*_lineWidth + i*_lineWidth + (x - left) +j] = color;
|
||||
for (int i = 0; i < _yScale; i++)
|
||||
{
|
||||
for (int j = 0; j < xPixelCount; j++)
|
||||
{
|
||||
if (alpha != 0xFF000000)
|
||||
{
|
||||
BlendColors((uint8_t*)&_argbBuffer[(y - top) * _lineWidth + i * _lineWidth + (x - left) + j],
|
||||
(uint8_t*)&color);
|
||||
}
|
||||
else
|
||||
{
|
||||
_argbBuffer[(y - top) * _lineWidth + i * _lineWidth + (x - left) + j] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +81,7 @@ protected:
|
|||
|
||||
public:
|
||||
DrawCommand(int startFrame, int frameCount, bool useIntegerScaling = false)
|
||||
{
|
||||
{
|
||||
_frameCount = frameCount > 0 ? frameCount : -1;
|
||||
_startFrame = startFrame;
|
||||
_useIntegerScaling = useIntegerScaling;
|
||||
|
@ -75,9 +91,10 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void Draw(uint32_t* argbBuffer, OverscanDimensions &overscan, uint32_t lineWidth, uint32_t frameNumber)
|
||||
void Draw(uint32_t* argbBuffer, OverscanDimensions& overscan, uint32_t lineWidth, uint32_t frameNumber)
|
||||
{
|
||||
if(_startFrame <= frameNumber) {
|
||||
if (_startFrame <= frameNumber)
|
||||
{
|
||||
_argbBuffer = argbBuffer;
|
||||
_overscan = overscan;
|
||||
_lineWidth = lineWidth;
|
||||
|
|
|
@ -16,18 +16,24 @@ protected:
|
|||
int dy = abs(_y2 - y), sy = y < _y2 ? 1 : -1;
|
||||
int err = (dx > dy ? dx : -dy) / 2, e2;
|
||||
|
||||
while(true) {
|
||||
while (true)
|
||||
{
|
||||
DrawPixel(x, y, _color);
|
||||
if(x == _x2 && y == _y2) {
|
||||
if (x == _x2 && y == _y2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
e2 = err;
|
||||
if(e2 > -dx) {
|
||||
err -= dy; x += sx;
|
||||
if (e2 > -dx)
|
||||
{
|
||||
err -= dy;
|
||||
x += sx;
|
||||
}
|
||||
if(e2 < dy) {
|
||||
err += dx; y += sy;
|
||||
if (e2 < dy)
|
||||
{
|
||||
err += dx;
|
||||
y += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,18 +11,25 @@ private:
|
|||
protected:
|
||||
void InternalDraw()
|
||||
{
|
||||
if(_fill) {
|
||||
for(int j = 0; j < _height; j++) {
|
||||
for(int i = 0; i < _width; i++) {
|
||||
if (_fill)
|
||||
{
|
||||
for (int j = 0; j < _height; j++)
|
||||
{
|
||||
for (int i = 0; i < _width; i++)
|
||||
{
|
||||
DrawPixel(_x + i, _y + j, _color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < _width; i++) {
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < _width; i++)
|
||||
{
|
||||
DrawPixel(_x + i, _y, _color);
|
||||
DrawPixel(_x + i, _y + _height - 1, _color);
|
||||
}
|
||||
for(int i = 1; i < _height - 1; i++) {
|
||||
for (int i = 1; i < _height - 1; i++)
|
||||
{
|
||||
DrawPixel(_x, _y + i, _color);
|
||||
DrawPixel(_x + _width - 1, _y + i, _color);
|
||||
}
|
||||
|
@ -33,11 +40,13 @@ public:
|
|||
DrawRectangleCommand(int x, int y, int width, int height, int color, bool fill, int frameCount, int startFrame) :
|
||||
DrawCommand(startFrame, frameCount), _x(x), _y(y), _width(width), _height(height), _color(color), _fill(fill)
|
||||
{
|
||||
if(width < 0) {
|
||||
if (width < 0)
|
||||
{
|
||||
_x += width + 1;
|
||||
_width = -width;
|
||||
}
|
||||
if(height < 0) {
|
||||
if (height < 0)
|
||||
{
|
||||
_y += height + 1;
|
||||
_height = -height;
|
||||
}
|
||||
|
@ -45,4 +54,4 @@ public:
|
|||
//Invert alpha byte - 0 = opaque, 255 = transparent (this way, no need to specifiy alpha channel all the time)
|
||||
_color = (~color & 0xFF000000) | (color & 0xFFFFFF);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,13 +5,15 @@
|
|||
class DrawScreenBufferCommand : public DrawCommand
|
||||
{
|
||||
private:
|
||||
uint32_t _screenBuffer[256*240];
|
||||
uint32_t _screenBuffer[256 * 240];
|
||||
|
||||
protected:
|
||||
void InternalDraw()
|
||||
{
|
||||
for(int y = 0; y < 240; y++) {
|
||||
for(int x = 0; x < 256; x++) {
|
||||
for (int y = 0; y < 240; y++)
|
||||
{
|
||||
for (int x = 0; x < 256; x++)
|
||||
{
|
||||
DrawPixel(x, y, _screenBuffer[(y << 8) + x]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,101 +11,101 @@ private:
|
|||
//Taken from FCEUX's LUA code
|
||||
const int _tabSpace = 4;
|
||||
const uint8_t _font[792] = {
|
||||
6, 0, 0, 0, 0, 0, 0, 0, // 0x20 - Spacebar
|
||||
3, 64, 64, 64, 64, 64, 0, 64,
|
||||
5, 80, 80, 80, 0, 0, 0, 0,
|
||||
6, 80, 80,248, 80,248, 80, 80,
|
||||
6, 32,120,160,112, 40,240, 32,
|
||||
6, 64,168, 80, 32, 80,168, 16,
|
||||
6, 96,144,160, 64,168,144,104,
|
||||
3, 64, 64, 0, 0, 0, 0, 0,
|
||||
6, 0, 0, 0, 0, 0, 0, 0, // 0x20 - Spacebar
|
||||
3, 64, 64, 64, 64, 64, 0, 64,
|
||||
5, 80, 80, 80, 0, 0, 0, 0,
|
||||
6, 80, 80, 248, 80, 248, 80, 80,
|
||||
6, 32, 120, 160, 112, 40, 240, 32,
|
||||
6, 64, 168, 80, 32, 80, 168, 16,
|
||||
6, 96, 144, 160, 64, 168, 144, 104,
|
||||
3, 64, 64, 0, 0, 0, 0, 0,
|
||||
4, 32, 64, 64, 64, 64, 64, 32,
|
||||
4, 64, 32, 32, 32, 32, 32, 64,
|
||||
6, 0, 80, 32,248, 32, 80, 0,
|
||||
6, 0, 32, 32,248, 32, 32, 0,
|
||||
3, 0, 0, 0, 0, 0, 64,128,
|
||||
5, 0, 0, 0,240, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 64,
|
||||
6, 0, 80, 32, 248, 32, 80, 0,
|
||||
6, 0, 32, 32, 248, 32, 32, 0,
|
||||
3, 0, 0, 0, 0, 0, 64, 128,
|
||||
5, 0, 0, 0, 240, 0, 0, 0,
|
||||
3, 0, 0, 0, 0, 0, 0, 64,
|
||||
5, 16, 16, 32, 32, 32, 64, 64,
|
||||
6,112,136,136,136,136,136,112, // 0x30 - 0
|
||||
6, 112, 136, 136, 136, 136, 136, 112, // 0x30 - 0
|
||||
6, 32, 96, 32, 32, 32, 32, 32,
|
||||
6,112,136, 8, 48, 64,128,248,
|
||||
6,112,136, 8, 48, 8,136,112,
|
||||
6, 16, 48, 80,144,248, 16, 16,
|
||||
6,248,128,128,240, 8, 8,240,
|
||||
6, 48, 64,128,240,136,136,112,
|
||||
6,248, 8, 16, 16, 32, 32, 32,
|
||||
6,112,136,136,112,136,136,112,
|
||||
6,112,136,136,120, 8, 16, 96,
|
||||
3, 0, 0, 64, 0, 0, 64, 0,
|
||||
3, 0, 0, 64, 0, 0, 64,128,
|
||||
4, 0, 32, 64,128, 64, 32, 0,
|
||||
5, 0, 0,240, 0,240, 0, 0,
|
||||
4, 0,128, 64, 32, 64,128, 0,
|
||||
6,112,136, 8, 16, 32, 0, 32, // 0x3F - ?
|
||||
6,112,136,136,184,176,128,112, // 0x40 - @
|
||||
6,112,136,136,248,136,136,136, // 0x41 - A
|
||||
6,240,136,136,240,136,136,240,
|
||||
6,112,136,128,128,128,136,112,
|
||||
6,224,144,136,136,136,144,224,
|
||||
6,248,128,128,240,128,128,248,
|
||||
6,248,128,128,240,128,128,128,
|
||||
6,112,136,128,184,136,136,120,
|
||||
6,136,136,136,248,136,136,136,
|
||||
4,224, 64, 64, 64, 64, 64,224,
|
||||
6, 8, 8, 8, 8, 8,136,112,
|
||||
6,136,144,160,192,160,144,136,
|
||||
6,128,128,128,128,128,128,248,
|
||||
6,136,216,168,168,136,136,136,
|
||||
6,136,136,200,168,152,136,136,
|
||||
7, 48, 72,132,132,132, 72, 48,
|
||||
6,240,136,136,240,128,128,128,
|
||||
6,112,136,136,136,168,144,104,
|
||||
6,240,136,136,240,144,136,136,
|
||||
6,112,136,128,112, 8,136,112,
|
||||
6,248, 32, 32, 32, 32, 32, 32,
|
||||
6,136,136,136,136,136,136,112,
|
||||
6,136,136,136, 80, 80, 32, 32,
|
||||
6,136,136,136,136,168,168, 80,
|
||||
6,136,136, 80, 32, 80,136,136,
|
||||
6,136,136, 80, 32, 32, 32, 32,
|
||||
6,248, 8, 16, 32, 64,128,248,
|
||||
3,192,128,128,128,128,128,192,
|
||||
6, 112, 136, 8, 48, 64, 128, 248,
|
||||
6, 112, 136, 8, 48, 8, 136, 112,
|
||||
6, 16, 48, 80, 144, 248, 16, 16,
|
||||
6, 248, 128, 128, 240, 8, 8, 240,
|
||||
6, 48, 64, 128, 240, 136, 136, 112,
|
||||
6, 248, 8, 16, 16, 32, 32, 32,
|
||||
6, 112, 136, 136, 112, 136, 136, 112,
|
||||
6, 112, 136, 136, 120, 8, 16, 96,
|
||||
3, 0, 0, 64, 0, 0, 64, 0,
|
||||
3, 0, 0, 64, 0, 0, 64, 128,
|
||||
4, 0, 32, 64, 128, 64, 32, 0,
|
||||
5, 0, 0, 240, 0, 240, 0, 0,
|
||||
4, 0, 128, 64, 32, 64, 128, 0,
|
||||
6, 112, 136, 8, 16, 32, 0, 32, // 0x3F - ?
|
||||
6, 112, 136, 136, 184, 176, 128, 112, // 0x40 - @
|
||||
6, 112, 136, 136, 248, 136, 136, 136, // 0x41 - A
|
||||
6, 240, 136, 136, 240, 136, 136, 240,
|
||||
6, 112, 136, 128, 128, 128, 136, 112,
|
||||
6, 224, 144, 136, 136, 136, 144, 224,
|
||||
6, 248, 128, 128, 240, 128, 128, 248,
|
||||
6, 248, 128, 128, 240, 128, 128, 128,
|
||||
6, 112, 136, 128, 184, 136, 136, 120,
|
||||
6, 136, 136, 136, 248, 136, 136, 136,
|
||||
4, 224, 64, 64, 64, 64, 64, 224,
|
||||
6, 8, 8, 8, 8, 8, 136, 112,
|
||||
6, 136, 144, 160, 192, 160, 144, 136,
|
||||
6, 128, 128, 128, 128, 128, 128, 248,
|
||||
6, 136, 216, 168, 168, 136, 136, 136,
|
||||
6, 136, 136, 200, 168, 152, 136, 136,
|
||||
7, 48, 72, 132, 132, 132, 72, 48,
|
||||
6, 240, 136, 136, 240, 128, 128, 128,
|
||||
6, 112, 136, 136, 136, 168, 144, 104,
|
||||
6, 240, 136, 136, 240, 144, 136, 136,
|
||||
6, 112, 136, 128, 112, 8, 136, 112,
|
||||
6, 248, 32, 32, 32, 32, 32, 32,
|
||||
6, 136, 136, 136, 136, 136, 136, 112,
|
||||
6, 136, 136, 136, 80, 80, 32, 32,
|
||||
6, 136, 136, 136, 136, 168, 168, 80,
|
||||
6, 136, 136, 80, 32, 80, 136, 136,
|
||||
6, 136, 136, 80, 32, 32, 32, 32,
|
||||
6, 248, 8, 16, 32, 64, 128, 248,
|
||||
3, 192, 128, 128, 128, 128, 128, 192,
|
||||
5, 64, 64, 32, 32, 32, 16, 16,
|
||||
3,192, 64, 64, 64, 64, 64,192,
|
||||
4, 64,160, 0, 0, 0, 0, 0,
|
||||
6, 0, 0, 0, 0, 0, 0,248,
|
||||
3,128, 64, 0, 0, 0, 0, 0,
|
||||
5, 0, 0, 96, 16,112,144,112, // 0x61 - a
|
||||
5,128,128,224,144,144,144,224,
|
||||
5, 0, 0,112,128,128,128,112,
|
||||
5, 16, 16,112,144,144,144,112,
|
||||
5, 0, 0, 96,144,240,128,112,
|
||||
5, 48, 64,224, 64, 64, 64, 64,
|
||||
5, 0,112,144,144,112, 16,224,
|
||||
5,128,128,224,144,144,144,144,
|
||||
2,128, 0,128,128,128,128,128,
|
||||
4, 32, 0, 32, 32, 32, 32,192,
|
||||
5,128,128,144,160,192,160,144,
|
||||
2,128,128,128,128,128,128,128,
|
||||
6, 0, 0,208,168,168,168,168,
|
||||
5, 0, 0,224,144,144,144,144,
|
||||
5, 0, 0, 96,144,144,144, 96,
|
||||
5, 0,224,144,144,224,128,128,
|
||||
5, 0,112,144,144,112, 16, 16,
|
||||
5, 0, 0,176,192,128,128,128,
|
||||
5, 0, 0,112,128, 96, 16,224,
|
||||
4, 64, 64,224, 64, 64, 64, 32,
|
||||
5, 0, 0,144,144,144,144,112,
|
||||
5, 0, 0,144,144,144,160,192,
|
||||
6, 0, 0,136,136,168,168, 80,
|
||||
5, 0, 0,144,144, 96,144,144,
|
||||
5, 0,144,144,144,112, 16, 96,
|
||||
5, 0, 0,240, 32, 64,128,240,
|
||||
4, 32, 64, 64,128, 64, 64, 32,
|
||||
3, 192, 64, 64, 64, 64, 64, 192,
|
||||
4, 64, 160, 0, 0, 0, 0, 0,
|
||||
6, 0, 0, 0, 0, 0, 0, 248,
|
||||
3, 128, 64, 0, 0, 0, 0, 0,
|
||||
5, 0, 0, 96, 16, 112, 144, 112, // 0x61 - a
|
||||
5, 128, 128, 224, 144, 144, 144, 224,
|
||||
5, 0, 0, 112, 128, 128, 128, 112,
|
||||
5, 16, 16, 112, 144, 144, 144, 112,
|
||||
5, 0, 0, 96, 144, 240, 128, 112,
|
||||
5, 48, 64, 224, 64, 64, 64, 64,
|
||||
5, 0, 112, 144, 144, 112, 16, 224,
|
||||
5, 128, 128, 224, 144, 144, 144, 144,
|
||||
2, 128, 0, 128, 128, 128, 128, 128,
|
||||
4, 32, 0, 32, 32, 32, 32, 192,
|
||||
5, 128, 128, 144, 160, 192, 160, 144,
|
||||
2, 128, 128, 128, 128, 128, 128, 128,
|
||||
6, 0, 0, 208, 168, 168, 168, 168,
|
||||
5, 0, 0, 224, 144, 144, 144, 144,
|
||||
5, 0, 0, 96, 144, 144, 144, 96,
|
||||
5, 0, 224, 144, 144, 224, 128, 128,
|
||||
5, 0, 112, 144, 144, 112, 16, 16,
|
||||
5, 0, 0, 176, 192, 128, 128, 128,
|
||||
5, 0, 0, 112, 128, 96, 16, 224,
|
||||
4, 64, 64, 224, 64, 64, 64, 32,
|
||||
5, 0, 0, 144, 144, 144, 144, 112,
|
||||
5, 0, 0, 144, 144, 144, 160, 192,
|
||||
6, 0, 0, 136, 136, 168, 168, 80,
|
||||
5, 0, 0, 144, 144, 96, 144, 144,
|
||||
5, 0, 144, 144, 144, 112, 16, 96,
|
||||
5, 0, 0, 240, 32, 64, 128, 240,
|
||||
4, 32, 64, 64, 128, 64, 64, 32,
|
||||
3, 64, 64, 64, 64, 64, 64, 64,
|
||||
4,128, 64, 64, 32, 64, 64,128,
|
||||
6, 0,104,176, 0, 0, 0, 0
|
||||
4, 128, 64, 64, 32, 64, 64, 128,
|
||||
6, 0, 104, 176, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
int GetCharNumber(char ch)
|
||||
|
@ -125,24 +125,35 @@ protected:
|
|||
int startX = (int)(_x * _xScale / _yScale);
|
||||
int x = startX;
|
||||
int y = _y;
|
||||
for(char c : _text) {
|
||||
if(c == '\n') {
|
||||
for (char c : _text)
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
x = startX;
|
||||
y += 9;
|
||||
} else if(c == '\t') {
|
||||
}
|
||||
else if (c == '\t')
|
||||
{
|
||||
x += (_tabSpace - (((x - startX) / 8) % _tabSpace)) * 8;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
int ch = GetCharNumber(c);
|
||||
int width = GetCharWidth(c);
|
||||
int rowOffset = (c == 'y' || c == 'g' || c == 'p' || c == 'q') ? 1 : 0;
|
||||
for(int j = 0; j < 8; j++) {
|
||||
uint8_t rowData = ((j == 7 && rowOffset == 0) || (j == 0 && rowOffset == 1)) ? 0 : _font[ch * 8 + 1 + j - rowOffset];
|
||||
for(int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
uint8_t rowData = ((j == 7 && rowOffset == 0) || (j == 0 && rowOffset == 1))
|
||||
? 0
|
||||
: _font[ch * 8 + 1 + j - rowOffset];
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
int drawFg = (rowData >> (7 - i)) & 0x01;
|
||||
DrawPixel(x + i, y + j, drawFg ? _color : _backColor);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < width; i++) {
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
DrawPixel(x + i, y - 1, _backColor);
|
||||
}
|
||||
x += width;
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
DummyCpu::DummyCpu(Console* console, CpuType type)
|
||||
{
|
||||
_console = console;
|
||||
_memoryMappings = type == CpuType::Cpu ? console->GetMemoryManager()->GetMemoryMappings() : console->GetCartridge()->GetSa1()->GetMemoryMappings();
|
||||
_memoryMappings = type == CpuType::Cpu
|
||||
? console->GetMemoryManager()->GetMemoryMappings()
|
||||
: console->GetCartridge()->GetSa1()->GetMemoryMappings();
|
||||
_dmaController = nullptr;
|
||||
_memoryManager = nullptr;
|
||||
}
|
||||
|
@ -33,7 +35,7 @@ void DummyCpu::Write(uint32_t addr, uint8_t value, MemoryOperationType type)
|
|||
LogWrite(addr, value);
|
||||
}
|
||||
|
||||
void DummyCpu::SetDummyState(CpuState &state)
|
||||
void DummyCpu::SetDummyState(CpuState& state)
|
||||
{
|
||||
_state = state;
|
||||
_state.StopState = CpuStopState::Running;
|
||||
|
@ -65,13 +67,13 @@ void DummyCpu::LogWrite(uint32_t addr, uint8_t value)
|
|||
_writeCounter++;
|
||||
}
|
||||
|
||||
void DummyCpu::GetWriteInfo(uint32_t index, uint32_t &addr, uint8_t &value)
|
||||
void DummyCpu::GetWriteInfo(uint32_t index, uint32_t& addr, uint8_t& value)
|
||||
{
|
||||
addr = _writeAddresses[index];
|
||||
value = _writeValue[index];
|
||||
}
|
||||
|
||||
void DummyCpu::GetReadInfo(uint32_t index, uint32_t &addr, uint8_t &value)
|
||||
void DummyCpu::GetReadInfo(uint32_t index, uint32_t& addr, uint8_t& value)
|
||||
{
|
||||
addr = _readAddresses[index];
|
||||
value = _readValue[index];
|
||||
|
@ -80,4 +82,4 @@ void DummyCpu::GetReadInfo(uint32_t index, uint32_t &addr, uint8_t &value)
|
|||
int32_t DummyCpu::GetLastOperand()
|
||||
{
|
||||
return _operand;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include "Spc.h"
|
||||
|
||||
DummySpc::DummySpc(uint8_t *spcRam, SpcState &state)
|
||||
DummySpc::DummySpc(uint8_t* spcRam, SpcState& state)
|
||||
{
|
||||
_ram = spcRam;
|
||||
|
||||
|
@ -37,9 +37,11 @@ DummySpc::~DummySpc()
|
|||
|
||||
void DummySpc::Step()
|
||||
{
|
||||
do {
|
||||
do
|
||||
{
|
||||
ProcessCycle();
|
||||
} while(_opStep != SpcOpStep::ReadOpCode);
|
||||
}
|
||||
while (_opStep != SpcOpStep::ReadOpCode);
|
||||
}
|
||||
|
||||
uint32_t DummySpc::GetWriteCount()
|
||||
|
@ -66,14 +68,14 @@ void DummySpc::LogWrite(uint32_t addr, uint8_t value)
|
|||
_writeCounter++;
|
||||
}
|
||||
|
||||
void DummySpc::GetWriteInfo(uint32_t index, uint32_t &addr, uint8_t &value)
|
||||
void DummySpc::GetWriteInfo(uint32_t index, uint32_t& addr, uint8_t& value)
|
||||
{
|
||||
addr = _writeAddresses[index];
|
||||
value = _writeValue[index];
|
||||
}
|
||||
|
||||
void DummySpc::GetReadInfo(uint32_t index, uint32_t &addr, uint8_t &value)
|
||||
void DummySpc::GetReadInfo(uint32_t index, uint32_t& addr, uint8_t& value)
|
||||
{
|
||||
addr = _readAddresses[index];
|
||||
value = _readValue[index];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,15 +31,19 @@ uint32_t EmuSettings::GetVersion()
|
|||
string EmuSettings::GetVersionString()
|
||||
{
|
||||
uint32_t version = GetVersion();
|
||||
return std::to_string(version >> 16) + "." + std::to_string((version >> 8) & 0xFF) + "." + std::to_string(version & 0xFF);
|
||||
return std::to_string(version >> 16) + "." + std::to_string((version >> 8) & 0xFF) + "." + std::to_string(
|
||||
version & 0xFF);
|
||||
}
|
||||
|
||||
void EmuSettings::ProcessString(string & str, const char ** strPointer)
|
||||
void EmuSettings::ProcessString(string& str, const char** strPointer)
|
||||
{
|
||||
//Make a copy of the string and keep it (the original pointer will not be valid after the call is over)
|
||||
if(*strPointer) {
|
||||
if (*strPointer)
|
||||
{
|
||||
str = *strPointer;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
str.clear();
|
||||
}
|
||||
*strPointer = str.c_str();
|
||||
|
@ -70,14 +74,16 @@ AudioConfig EmuSettings::GetAudioConfig()
|
|||
void EmuSettings::SetInputConfig(InputConfig config)
|
||||
{
|
||||
bool controllersChanged = false;
|
||||
for(int i = 0; i < 5; i++) {
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
controllersChanged |= _input.Controllers[i].Type != config.Controllers[i].Type;
|
||||
}
|
||||
|
||||
_input = config;
|
||||
_inputConfigVersion++;
|
||||
|
||||
if(controllersChanged) {
|
||||
if (controllersChanged)
|
||||
{
|
||||
//Used by net play
|
||||
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ConfigChanged);
|
||||
}
|
||||
|
@ -85,8 +91,10 @@ void EmuSettings::SetInputConfig(InputConfig config)
|
|||
|
||||
InputConfig EmuSettings::GetInputConfig()
|
||||
{
|
||||
if(CheckFlag(EmulationFlags::GameboyMode)) {
|
||||
if(_input.Controllers[0].Type != ControllerType::SnesController) {
|
||||
if (CheckFlag(EmulationFlags::GameboyMode))
|
||||
{
|
||||
if (_input.Controllers[0].Type != ControllerType::SnesController)
|
||||
{
|
||||
//Force SNES controller for P1 for gameboy-only mode
|
||||
InputConfig input = _input;
|
||||
input.Controllers[0].Type = ControllerType::SnesController;
|
||||
|
@ -103,14 +111,20 @@ uint32_t EmuSettings::GetInputConfigVersion()
|
|||
|
||||
void EmuSettings::SetEmulationConfig(EmulationConfig config)
|
||||
{
|
||||
bool prevOverclockEnabled = _emulation.PpuExtraScanlinesAfterNmi > 0 || _emulation.PpuExtraScanlinesBeforeNmi > 0 || _emulation.GsuClockSpeed > 100;
|
||||
bool overclockEnabled = config.PpuExtraScanlinesAfterNmi > 0 || config.PpuExtraScanlinesBeforeNmi > 0 || config.GsuClockSpeed > 100;
|
||||
bool prevOverclockEnabled = _emulation.PpuExtraScanlinesAfterNmi > 0 || _emulation.PpuExtraScanlinesBeforeNmi > 0 ||
|
||||
_emulation.GsuClockSpeed > 100;
|
||||
bool overclockEnabled = config.PpuExtraScanlinesAfterNmi > 0 || config.PpuExtraScanlinesBeforeNmi > 0 || config.
|
||||
GsuClockSpeed > 100;
|
||||
_emulation = config;
|
||||
|
||||
if(prevOverclockEnabled != overclockEnabled) {
|
||||
if(overclockEnabled) {
|
||||
if (prevOverclockEnabled != overclockEnabled)
|
||||
{
|
||||
if (overclockEnabled)
|
||||
{
|
||||
MessageManager::DisplayMessage("Overclock", "OverclockEnabled");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageManager::DisplayMessage("Overclock", "OverclockDisabled");
|
||||
}
|
||||
|
||||
|
@ -177,11 +191,16 @@ void EmuSettings::SetShortcutKey(EmulatorShortcut shortcut, KeyCombination keyCo
|
|||
{
|
||||
_emulatorKeys[keySetIndex][(uint32_t)shortcut] = keyCombination;
|
||||
|
||||
for(int i = 0; i < 3; i++) {
|
||||
for(std::pair<const uint32_t, KeyCombination> &kvp : _emulatorKeys[i]) {
|
||||
if(keyCombination.IsSubsetOf(kvp.second)) {
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for (std::pair<const uint32_t, KeyCombination>& kvp : _emulatorKeys[i])
|
||||
{
|
||||
if (keyCombination.IsSubsetOf(kvp.second))
|
||||
{
|
||||
_shortcutSupersets[keySetIndex][(uint32_t)shortcut].push_back(kvp.second);
|
||||
} else if(kvp.second.IsSubsetOf(keyCombination)) {
|
||||
}
|
||||
else if (kvp.second.IsSubsetOf(keyCombination))
|
||||
{
|
||||
_shortcutSupersets[i][kvp.first].push_back(keyCombination);
|
||||
}
|
||||
}
|
||||
|
@ -192,10 +211,14 @@ void EmuSettings::SetShortcutKeys(vector<ShortcutKeyInfo> shortcuts)
|
|||
{
|
||||
ClearShortcutKeys();
|
||||
|
||||
for(ShortcutKeyInfo &shortcut : shortcuts) {
|
||||
if(_emulatorKeys[0][(uint32_t)shortcut.Shortcut].GetKeys().empty()) {
|
||||
for (ShortcutKeyInfo& shortcut : shortcuts)
|
||||
{
|
||||
if (_emulatorKeys[0][(uint32_t)shortcut.Shortcut].GetKeys().empty())
|
||||
{
|
||||
SetShortcutKey(shortcut.Shortcut, shortcut.Keys, 0);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetShortcutKey(shortcut.Shortcut, shortcut.Keys, 1);
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +227,8 @@ void EmuSettings::SetShortcutKeys(vector<ShortcutKeyInfo> shortcuts)
|
|||
KeyCombination EmuSettings::GetShortcutKey(EmulatorShortcut shortcut, int keySetIndex)
|
||||
{
|
||||
auto result = _emulatorKeys[keySetIndex].find((int)shortcut);
|
||||
if(result != _emulatorKeys[keySetIndex].end()) {
|
||||
if (result != _emulatorKeys[keySetIndex].end())
|
||||
{
|
||||
return result->second;
|
||||
}
|
||||
return {};
|
||||
|
@ -218,13 +242,16 @@ vector<KeyCombination> EmuSettings::GetShortcutSupersets(EmulatorShortcut shortc
|
|||
OverscanDimensions EmuSettings::GetOverscan()
|
||||
{
|
||||
OverscanDimensions overscan;
|
||||
if(CheckFlag(EmulationFlags::GameboyMode)) {
|
||||
if (CheckFlag(EmulationFlags::GameboyMode))
|
||||
{
|
||||
//Force overscan values for gameboy-only mode (not SGB)
|
||||
overscan.Left = 0;
|
||||
overscan.Right = 256 - 160;
|
||||
overscan.Top = 0;
|
||||
overscan.Bottom = 239 - 144;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
overscan.Left = _video.OverscanLeft;
|
||||
overscan.Right = _video.OverscanRight;
|
||||
overscan.Top = _video.OverscanTop;
|
||||
|
@ -240,50 +267,63 @@ uint32_t EmuSettings::GetRewindBufferSize()
|
|||
|
||||
uint32_t EmuSettings::GetEmulationSpeed()
|
||||
{
|
||||
if(CheckFlag(EmulationFlags::MaximumSpeed)) {
|
||||
if (CheckFlag(EmulationFlags::MaximumSpeed))
|
||||
{
|
||||
return 0;
|
||||
} else if(CheckFlag(EmulationFlags::Turbo)) {
|
||||
}
|
||||
else if (CheckFlag(EmulationFlags::Turbo))
|
||||
{
|
||||
return _emulation.TurboSpeed;
|
||||
} else if(CheckFlag(EmulationFlags::Rewind)) {
|
||||
}
|
||||
else if (CheckFlag(EmulationFlags::Rewind))
|
||||
{
|
||||
return _emulation.RewindSpeed;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return _emulation.EmulationSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
double EmuSettings::GetAspectRatio(ConsoleRegion region)
|
||||
{
|
||||
switch(_video.AspectRatio) {
|
||||
case VideoAspectRatio::NoStretching: return 0.0;
|
||||
case VideoAspectRatio::Auto: return region == ConsoleRegion::Pal ? (11.0 / 8.0) : (8.0 / 7.0);
|
||||
case VideoAspectRatio::NTSC: return 8.0 / 7.0;
|
||||
case VideoAspectRatio::PAL: return 11.0 / 8.0;
|
||||
case VideoAspectRatio::Standard: return 4.0 / 3.0;
|
||||
case VideoAspectRatio::Widescreen: return 16.0 / 9.0;
|
||||
case VideoAspectRatio::Custom: return _video.CustomAspectRatio;
|
||||
switch (_video.AspectRatio)
|
||||
{
|
||||
case VideoAspectRatio::NoStretching: return 0.0;
|
||||
case VideoAspectRatio::Auto: return region == ConsoleRegion::Pal ? (11.0 / 8.0) : (8.0 / 7.0);
|
||||
case VideoAspectRatio::NTSC: return 8.0 / 7.0;
|
||||
case VideoAspectRatio::PAL: return 11.0 / 8.0;
|
||||
case VideoAspectRatio::Standard: return 4.0 / 3.0;
|
||||
case VideoAspectRatio::Widescreen: return 16.0 / 9.0;
|
||||
case VideoAspectRatio::Custom: return _video.CustomAspectRatio;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void EmuSettings::SetFlag(EmulationFlags flag)
|
||||
{
|
||||
if((_flags & (int)flag) == 0) {
|
||||
if ((_flags & (int)flag) == 0)
|
||||
{
|
||||
_flags |= (int)flag;
|
||||
}
|
||||
}
|
||||
|
||||
void EmuSettings::SetFlagState(EmulationFlags flag, bool enabled)
|
||||
{
|
||||
if(enabled) {
|
||||
if (enabled)
|
||||
{
|
||||
SetFlag(flag);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearFlag(flag);
|
||||
}
|
||||
}
|
||||
|
||||
void EmuSettings::ClearFlag(EmulationFlags flag)
|
||||
{
|
||||
if((_flags & (int)flag) != 0) {
|
||||
if ((_flags & (int)flag) != 0)
|
||||
{
|
||||
_flags &= ~(int)flag;
|
||||
}
|
||||
}
|
||||
|
@ -295,12 +335,17 @@ bool EmuSettings::CheckFlag(EmulationFlags flag)
|
|||
|
||||
void EmuSettings::SetDebuggerFlag(DebuggerFlags flag, bool enabled)
|
||||
{
|
||||
if(enabled) {
|
||||
if((_debuggerFlags & (int)flag) == 0) {
|
||||
if (enabled)
|
||||
{
|
||||
if ((_debuggerFlags & (int)flag) == 0)
|
||||
{
|
||||
_debuggerFlags |= (int)flag;
|
||||
}
|
||||
} else {
|
||||
if((_debuggerFlags & (int)flag) != 0) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((_debuggerFlags & (int)flag) != 0)
|
||||
{
|
||||
_debuggerFlags &= ~(int)flag;
|
||||
}
|
||||
}
|
||||
|
@ -313,16 +358,20 @@ bool EmuSettings::CheckDebuggerFlag(DebuggerFlags flag)
|
|||
|
||||
void EmuSettings::InitializeRam(void* data, uint32_t length)
|
||||
{
|
||||
switch(_emulation.RamPowerOnState) {
|
||||
default:
|
||||
case RamState::AllZeros: memset(data, 0, length); break;
|
||||
case RamState::AllOnes: memset(data, 0xFF, length); break;
|
||||
case RamState::Random:
|
||||
std::uniform_int_distribution<> dist(0, 255);
|
||||
for(uint32_t i = 0; i < length; i++) {
|
||||
((uint8_t*)data)[i] = dist(_mt);
|
||||
}
|
||||
break;
|
||||
switch (_emulation.RamPowerOnState)
|
||||
{
|
||||
default:
|
||||
case RamState::AllZeros: memset(data, 0, length);
|
||||
break;
|
||||
case RamState::AllOnes: memset(data, 0xFF, length);
|
||||
break;
|
||||
case RamState::Random:
|
||||
std::uniform_int_distribution<> dist(0, 255);
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
{
|
||||
((uint8_t*)data)[i] = dist(_mt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,8 +382,8 @@ int EmuSettings::GetRandomValue(int maxValue)
|
|||
}
|
||||
|
||||
bool EmuSettings::GetRandomBool()
|
||||
{
|
||||
return GetRandomValue(1) == 1;
|
||||
{
|
||||
return GetRandomValue(1) == 1;
|
||||
}
|
||||
|
||||
bool EmuSettings::IsInputEnabled()
|
||||
|
@ -344,12 +393,13 @@ bool EmuSettings::IsInputEnabled()
|
|||
|
||||
double EmuSettings::GetControllerDeadzoneRatio()
|
||||
{
|
||||
switch(_input.ControllerDeadzoneSize) {
|
||||
case 0: return 0.5;
|
||||
case 1: return 0.75;
|
||||
case 2: return 1;
|
||||
case 3: return 1.25;
|
||||
case 4: return 1.5;
|
||||
switch (_input.ControllerDeadzoneSize)
|
||||
{
|
||||
case 0: return 0.5;
|
||||
case 1: return 0.75;
|
||||
case 2: return 1;
|
||||
case 3: return 1.25;
|
||||
case 4: return 1.5;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ private:
|
|||
std::unordered_map<uint32_t, KeyCombination> _emulatorKeys[3];
|
||||
std::unordered_map<uint32_t, vector<KeyCombination>> _shortcutSupersets[3];
|
||||
|
||||
void ProcessString(string &str, const char** strPointer);
|
||||
void ProcessString(string& str, const char** strPointer);
|
||||
|
||||
void ClearShortcutKeys();
|
||||
void SetShortcutKey(EmulatorShortcut shortcut, KeyCombination keyCombination, int keySetIndex);
|
||||
|
@ -77,11 +77,11 @@ public:
|
|||
|
||||
void SetDebuggerFlag(DebuggerFlags flag, bool enabled);
|
||||
bool CheckDebuggerFlag(DebuggerFlags flags);
|
||||
|
||||
|
||||
int GetRandomValue(int maxValue);
|
||||
bool GetRandomBool();
|
||||
void InitializeRam(void* data, uint32_t length);
|
||||
|
||||
bool IsInputEnabled();
|
||||
double GetControllerDeadzoneRatio();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
#include "DefaultVideoFilter.h"
|
||||
#include "BaseEventManager.h"
|
||||
|
||||
EventManager::EventManager(Debugger *debugger, Cpu *cpu, Ppu *ppu, MemoryManager *memoryManager, DmaController *dmaController)
|
||||
EventManager::EventManager(Debugger* debugger, Cpu* cpu, Ppu* ppu, MemoryManager* memoryManager,
|
||||
DmaController* dmaController)
|
||||
{
|
||||
_debugger = debugger;
|
||||
_cpu = cpu;
|
||||
|
@ -27,7 +28,7 @@ EventManager::~EventManager()
|
|||
delete[] _ppuBuffer;
|
||||
}
|
||||
|
||||
void EventManager::AddEvent(DebugEventType type, MemoryOperationInfo &operation, int32_t breakpointId)
|
||||
void EventManager::AddEvent(DebugEventType type, MemoryOperationInfo& operation, int32_t breakpointId)
|
||||
{
|
||||
DebugEventInfo evt = {};
|
||||
evt.Type = type;
|
||||
|
@ -36,10 +37,13 @@ void EventManager::AddEvent(DebugEventType type, MemoryOperationInfo &operation,
|
|||
evt.Cycle = _memoryManager->GetHClock();
|
||||
evt.BreakpointId = breakpointId;
|
||||
|
||||
if(operation.Type == MemoryOperationType::DmaRead || operation.Type == MemoryOperationType::DmaWrite) {
|
||||
if (operation.Type == MemoryOperationType::DmaRead || operation.Type == MemoryOperationType::DmaWrite)
|
||||
{
|
||||
evt.DmaChannel = _dmaController->GetActiveChannel();
|
||||
evt.DmaChannelInfo = _dmaController->GetChannelConfig(evt.DmaChannel & 0x07);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
evt.DmaChannel = -1;
|
||||
}
|
||||
|
||||
|
@ -57,14 +61,14 @@ void EventManager::AddEvent(DebugEventType type)
|
|||
evt.Cycle = _memoryManager->GetHClock();
|
||||
evt.BreakpointId = -1;
|
||||
evt.DmaChannel = -1;
|
||||
|
||||
|
||||
CpuState state = _cpu->GetState();
|
||||
evt.ProgramCounter = (state.K << 16) | state.PC;
|
||||
|
||||
_debugEvents.push_back(evt);
|
||||
}
|
||||
|
||||
void EventManager::GetEvents(DebugEventInfo *eventArray, uint32_t &maxEventCount)
|
||||
void EventManager::GetEvents(DebugEventInfo* eventArray, uint32_t& maxEventCount)
|
||||
{
|
||||
auto lock = _lock.AcquireSafe();
|
||||
uint32_t eventCount = std::min(maxEventCount, (uint32_t)_sentEvents.size());
|
||||
|
@ -72,12 +76,14 @@ void EventManager::GetEvents(DebugEventInfo *eventArray, uint32_t &maxEventCount
|
|||
maxEventCount = eventCount;
|
||||
}
|
||||
|
||||
DebugEventInfo EventManager::GetEvent(uint16_t scanline, uint16_t cycle, EventViewerDisplayOptions &options)
|
||||
DebugEventInfo EventManager::GetEvent(uint16_t scanline, uint16_t cycle, EventViewerDisplayOptions& options)
|
||||
{
|
||||
auto lock = _lock.AcquireSafe();
|
||||
|
||||
for(DebugEventInfo &evt : _sentEvents) {
|
||||
if(evt.Cycle == cycle && evt.Scanline == scanline) {
|
||||
for (DebugEventInfo& evt : _sentEvents)
|
||||
{
|
||||
if (evt.Cycle == cycle && evt.Scanline == scanline)
|
||||
{
|
||||
return evt;
|
||||
}
|
||||
}
|
||||
|
@ -100,120 +106,191 @@ void EventManager::ClearFrameEvents()
|
|||
_debugEvents.clear();
|
||||
}
|
||||
|
||||
void EventManager::FilterEvents(EventViewerDisplayOptions &options)
|
||||
void EventManager::FilterEvents(EventViewerDisplayOptions& options)
|
||||
{
|
||||
auto lock = _lock.AcquireSafe();
|
||||
_sentEvents.clear();
|
||||
|
||||
vector<DebugEventInfo> events = _snapshot;
|
||||
if(options.ShowPreviousFrameEvents && _snapshotScanline != 0) {
|
||||
if (options.ShowPreviousFrameEvents && _snapshotScanline != 0)
|
||||
{
|
||||
uint32_t key = (_snapshotScanline << 16) + _snapshotCycle;
|
||||
for(DebugEventInfo &evt : _prevDebugEvents) {
|
||||
for (DebugEventInfo& evt : _prevDebugEvents)
|
||||
{
|
||||
uint32_t evtKey = (evt.Scanline << 16) + evt.Cycle;
|
||||
if(evtKey > key) {
|
||||
if (evtKey > key)
|
||||
{
|
||||
events.push_back(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(DebugEventInfo &evt : events) {
|
||||
bool isWrite = evt.Operation.Type == MemoryOperationType::Write || evt.Operation.Type == MemoryOperationType::DmaWrite;
|
||||
bool isDma = evt.Operation.Type == MemoryOperationType::DmaWrite || evt.Operation.Type == MemoryOperationType::DmaRead;
|
||||
for (DebugEventInfo& evt : events)
|
||||
{
|
||||
bool isWrite = evt.Operation.Type == MemoryOperationType::Write || evt.Operation.Type ==
|
||||
MemoryOperationType::DmaWrite;
|
||||
bool isDma = evt.Operation.Type == MemoryOperationType::DmaWrite || evt.Operation.Type ==
|
||||
MemoryOperationType::DmaRead;
|
||||
bool showEvent = false;
|
||||
switch(evt.Type) {
|
||||
case DebugEventType::Breakpoint: showEvent = options.ShowMarkedBreakpoints;break;
|
||||
case DebugEventType::Irq: showEvent = options.ShowIrq; break;
|
||||
case DebugEventType::Nmi: showEvent = options.ShowNmi; break;
|
||||
case DebugEventType::Register:
|
||||
if(isDma && !options.ShowDmaChannels[evt.DmaChannel & 0x07]) {
|
||||
showEvent = false;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t reg = evt.Operation.Address & 0xFFFF;
|
||||
if(reg <= 0x213F) {
|
||||
if(isWrite) {
|
||||
if(reg >= 0x2101 && reg <= 0x2104) {
|
||||
showEvent = options.ShowPpuRegisterOamWrites;
|
||||
} else if(reg >= 0x2105 && reg <= 0x210C) {
|
||||
showEvent = options.ShowPpuRegisterBgOptionWrites;
|
||||
} else if(reg >= 0x210D && reg <= 0x2114) {
|
||||
showEvent = options.ShowPpuRegisterBgScrollWrites;
|
||||
} else if(reg >= 0x2115 && reg <= 0x2119) {
|
||||
showEvent = options.ShowPpuRegisterVramWrites;
|
||||
} else if(reg >= 0x211A && reg <= 0x2120) {
|
||||
showEvent = options.ShowPpuRegisterMode7Writes;
|
||||
} else if(reg >= 0x2121 && reg <= 0x2122) {
|
||||
showEvent = options.ShowPpuRegisterCgramWrites;
|
||||
} else if(reg >= 0x2123 && reg <= 0x212B) {
|
||||
showEvent = options.ShowPpuRegisterWindowWrites;
|
||||
} else {
|
||||
showEvent = options.ShowPpuRegisterOtherWrites;
|
||||
}
|
||||
} else {
|
||||
showEvent = options.ShowPpuRegisterReads;
|
||||
}
|
||||
} else if(reg <= 0x217F) {
|
||||
showEvent = isWrite ? options.ShowApuRegisterWrites : options.ShowApuRegisterReads;
|
||||
} else if(reg <= 0x2183) {
|
||||
showEvent = isWrite ? options.ShowWorkRamRegisterWrites : options.ShowWorkRamRegisterReads;
|
||||
} else if(reg >= 0x4000) {
|
||||
showEvent = isWrite ? options.ShowCpuRegisterWrites : options.ShowCpuRegisterReads;
|
||||
}
|
||||
switch (evt.Type)
|
||||
{
|
||||
case DebugEventType::Breakpoint: showEvent = options.ShowMarkedBreakpoints;
|
||||
break;
|
||||
case DebugEventType::Irq: showEvent = options.ShowIrq;
|
||||
break;
|
||||
case DebugEventType::Nmi: showEvent = options.ShowNmi;
|
||||
break;
|
||||
case DebugEventType::Register:
|
||||
if (isDma && !options.ShowDmaChannels[evt.DmaChannel & 0x07])
|
||||
{
|
||||
showEvent = false;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t reg = evt.Operation.Address & 0xFFFF;
|
||||
if (reg <= 0x213F)
|
||||
{
|
||||
if (isWrite)
|
||||
{
|
||||
if (reg >= 0x2101 && reg <= 0x2104)
|
||||
{
|
||||
showEvent = options.ShowPpuRegisterOamWrites;
|
||||
}
|
||||
else if (reg >= 0x2105 && reg <= 0x210C)
|
||||
{
|
||||
showEvent = options.ShowPpuRegisterBgOptionWrites;
|
||||
}
|
||||
else if (reg >= 0x210D && reg <= 0x2114)
|
||||
{
|
||||
showEvent = options.ShowPpuRegisterBgScrollWrites;
|
||||
}
|
||||
else if (reg >= 0x2115 && reg <= 0x2119)
|
||||
{
|
||||
showEvent = options.ShowPpuRegisterVramWrites;
|
||||
}
|
||||
else if (reg >= 0x211A && reg <= 0x2120)
|
||||
{
|
||||
showEvent = options.ShowPpuRegisterMode7Writes;
|
||||
}
|
||||
else if (reg >= 0x2121 && reg <= 0x2122)
|
||||
{
|
||||
showEvent = options.ShowPpuRegisterCgramWrites;
|
||||
}
|
||||
else if (reg >= 0x2123 && reg <= 0x212B)
|
||||
{
|
||||
showEvent = options.ShowPpuRegisterWindowWrites;
|
||||
}
|
||||
else
|
||||
{
|
||||
showEvent = options.ShowPpuRegisterOtherWrites;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
showEvent = options.ShowPpuRegisterReads;
|
||||
}
|
||||
}
|
||||
else if (reg <= 0x217F)
|
||||
{
|
||||
showEvent = isWrite ? options.ShowApuRegisterWrites : options.ShowApuRegisterReads;
|
||||
}
|
||||
else if (reg <= 0x2183)
|
||||
{
|
||||
showEvent = isWrite ? options.ShowWorkRamRegisterWrites : options.ShowWorkRamRegisterReads;
|
||||
}
|
||||
else if (reg >= 0x4000)
|
||||
{
|
||||
showEvent = isWrite ? options.ShowCpuRegisterWrites : options.ShowCpuRegisterReads;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(showEvent) {
|
||||
if (showEvent)
|
||||
{
|
||||
_sentEvents.push_back(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::DrawEvent(DebugEventInfo &evt, bool drawBackground, uint32_t *buffer, EventViewerDisplayOptions &options)
|
||||
void EventManager::DrawEvent(DebugEventInfo& evt, bool drawBackground, uint32_t* buffer,
|
||||
EventViewerDisplayOptions& options)
|
||||
{
|
||||
bool isWrite = evt.Operation.Type == MemoryOperationType::Write || evt.Operation.Type == MemoryOperationType::DmaWrite;
|
||||
bool isWrite = evt.Operation.Type == MemoryOperationType::Write || evt.Operation.Type ==
|
||||
MemoryOperationType::DmaWrite;
|
||||
uint32_t color = 0;
|
||||
switch(evt.Type) {
|
||||
case DebugEventType::Breakpoint: color = options.BreakpointColor; break;
|
||||
case DebugEventType::Irq: color = options.IrqColor; break;
|
||||
case DebugEventType::Nmi: color = options.NmiColor; break;
|
||||
case DebugEventType::Register:
|
||||
uint16_t reg = evt.Operation.Address & 0xFFFF;
|
||||
if(reg <= 0x213F) {
|
||||
if(isWrite) {
|
||||
if(reg >= 0x2101 && reg <= 0x2104) {
|
||||
color = options.PpuRegisterWriteOamColor;
|
||||
} else if(reg >= 0x2105 && reg <= 0x210C) {
|
||||
color = options.PpuRegisterWriteBgOptionColor;
|
||||
} else if(reg >= 0x210D && reg <= 0x2114) {
|
||||
color = options.PpuRegisterWriteBgScrollColor;
|
||||
} else if(reg >= 0x2115 && reg <= 0x2119) {
|
||||
color = options.PpuRegisterWriteVramColor;
|
||||
} else if(reg >= 0x211A && reg <= 0x2120) {
|
||||
color = options.PpuRegisterWriteMode7Color;
|
||||
} else if(reg >= 0x2121 && reg <= 0x2122) {
|
||||
color = options.PpuRegisterWriteCgramColor;
|
||||
} else if(reg >= 0x2123 && reg <= 0x212B) {
|
||||
color = options.PpuRegisterWriteWindowColor;
|
||||
} else {
|
||||
color = options.PpuRegisterWriteOtherColor;
|
||||
}
|
||||
} else {
|
||||
color = options.PpuRegisterReadColor;
|
||||
switch (evt.Type)
|
||||
{
|
||||
case DebugEventType::Breakpoint: color = options.BreakpointColor;
|
||||
break;
|
||||
case DebugEventType::Irq: color = options.IrqColor;
|
||||
break;
|
||||
case DebugEventType::Nmi: color = options.NmiColor;
|
||||
break;
|
||||
case DebugEventType::Register:
|
||||
uint16_t reg = evt.Operation.Address & 0xFFFF;
|
||||
if (reg <= 0x213F)
|
||||
{
|
||||
if (isWrite)
|
||||
{
|
||||
if (reg >= 0x2101 && reg <= 0x2104)
|
||||
{
|
||||
color = options.PpuRegisterWriteOamColor;
|
||||
}
|
||||
else if (reg >= 0x2105 && reg <= 0x210C)
|
||||
{
|
||||
color = options.PpuRegisterWriteBgOptionColor;
|
||||
}
|
||||
else if (reg >= 0x210D && reg <= 0x2114)
|
||||
{
|
||||
color = options.PpuRegisterWriteBgScrollColor;
|
||||
}
|
||||
else if (reg >= 0x2115 && reg <= 0x2119)
|
||||
{
|
||||
color = options.PpuRegisterWriteVramColor;
|
||||
}
|
||||
else if (reg >= 0x211A && reg <= 0x2120)
|
||||
{
|
||||
color = options.PpuRegisterWriteMode7Color;
|
||||
}
|
||||
else if (reg >= 0x2121 && reg <= 0x2122)
|
||||
{
|
||||
color = options.PpuRegisterWriteCgramColor;
|
||||
}
|
||||
else if (reg >= 0x2123 && reg <= 0x212B)
|
||||
{
|
||||
color = options.PpuRegisterWriteWindowColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = options.PpuRegisterWriteOtherColor;
|
||||
}
|
||||
} else if(reg <= 0x217F) {
|
||||
color = isWrite ? options.ApuRegisterWriteColor : options.ApuRegisterReadColor;
|
||||
} else if(reg <= 0x2183) {
|
||||
color = isWrite ? options.WorkRamRegisterWriteColor : options.WorkRamRegisterReadColor;
|
||||
} else if(reg >= 0x4000) {
|
||||
color = isWrite ? options.CpuRegisterWriteColor : options.CpuRegisterReadColor;
|
||||
}
|
||||
break;
|
||||
else
|
||||
{
|
||||
color = options.PpuRegisterReadColor;
|
||||
}
|
||||
}
|
||||
else if (reg <= 0x217F)
|
||||
{
|
||||
color = isWrite ? options.ApuRegisterWriteColor : options.ApuRegisterReadColor;
|
||||
}
|
||||
else if (reg <= 0x2183)
|
||||
{
|
||||
color = isWrite ? options.WorkRamRegisterWriteColor : options.WorkRamRegisterReadColor;
|
||||
}
|
||||
else if (reg >= 0x4000)
|
||||
{
|
||||
color = isWrite ? options.CpuRegisterWriteColor : options.CpuRegisterReadColor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(drawBackground){
|
||||
if (drawBackground)
|
||||
{
|
||||
color = 0xFF000000 | ((color >> 1) & 0x7F7F7F);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
color |= 0xFF000000;
|
||||
}
|
||||
|
||||
|
@ -224,10 +301,13 @@ void EventManager::DrawEvent(DebugEventInfo &evt, bool drawBackground, uint32_t
|
|||
uint32_t y = std::min<uint32_t>(evt.Scanline * 2, _scanlineCount * 2);
|
||||
uint32_t x = evt.Cycle / 2;
|
||||
|
||||
for(int i = iMin; i <= iMax; i++) {
|
||||
for(int j = jMin; j <= jMax; j++) {
|
||||
for (int i = iMin; i <= iMax; i++)
|
||||
{
|
||||
for (int j = jMin; j <= jMax; j++)
|
||||
{
|
||||
int32_t pos = (y + i) * EventManager::ScanlineWidth + x + j;
|
||||
if(pos < 0 || pos >= EventManager::ScanlineWidth * (int)_scanlineCount * 2) {
|
||||
if (pos < 0 || pos >= EventManager::ScanlineWidth * (int)_scanlineCount * 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
buffer[pos] = color;
|
||||
|
@ -247,14 +327,17 @@ uint32_t EventManager::TakeEventSnapshot(EventViewerDisplayOptions options)
|
|||
_overscanMode = _ppu->GetState().OverscanMode;
|
||||
_useHighResOutput = _ppu->IsHighResOutput();
|
||||
|
||||
if(scanline >= _ppu->GetNmiScanline() || scanline == 0) {
|
||||
memcpy(_ppuBuffer, _ppu->GetScreenBuffer(), (_useHighResOutput ? (512 * 478) : (256*239)) * sizeof(uint16_t));
|
||||
} else {
|
||||
if (scanline >= _ppu->GetNmiScanline() || scanline == 0)
|
||||
{
|
||||
memcpy(_ppuBuffer, _ppu->GetScreenBuffer(), (_useHighResOutput ? (512 * 478) : (256 * 239)) * sizeof(uint16_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t adjustedScanline = scanline + (_overscanMode ? 0 : 7);
|
||||
uint32_t size = _useHighResOutput ? (512 * 478) : (256 * 239);
|
||||
uint32_t offset = _useHighResOutput ? (512 * adjustedScanline * 2) : (256 * adjustedScanline);
|
||||
memcpy(_ppuBuffer, _ppu->GetScreenBuffer(), offset * sizeof(uint16_t));
|
||||
memcpy(_ppuBuffer+offset, _ppu->GetPreviousScreenBuffer()+offset, (size - offset) * sizeof(uint16_t));
|
||||
memcpy(_ppuBuffer + offset, _ppu->GetPreviousScreenBuffer() + offset, (size - offset) * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
_snapshot = _debugEvents;
|
||||
|
@ -264,25 +347,29 @@ uint32_t EventManager::TakeEventSnapshot(EventViewerDisplayOptions options)
|
|||
return _scanlineCount;
|
||||
}
|
||||
|
||||
void EventManager::GetDisplayBuffer(uint32_t *buffer, uint32_t bufferSize, EventViewerDisplayOptions options)
|
||||
void EventManager::GetDisplayBuffer(uint32_t* buffer, uint32_t bufferSize, EventViewerDisplayOptions options)
|
||||
{
|
||||
auto lock = _lock.AcquireSafe();
|
||||
|
||||
if(_snapshotScanline < 0 || bufferSize < _scanlineCount * 2 * EventManager::ScanlineWidth * 4) {
|
||||
if (_snapshotScanline < 0 || bufferSize < _scanlineCount * 2 * EventManager::ScanlineWidth * 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < EventManager::ScanlineWidth * (int)_scanlineCount * 2; i++) {
|
||||
for (int i = 0; i < EventManager::ScanlineWidth * (int)_scanlineCount * 2; i++)
|
||||
{
|
||||
buffer[i] = 0xFF555555;
|
||||
}
|
||||
|
||||
//Skip the first 7 blank lines in the buffer when overscan mode is off
|
||||
uint16_t *src = _ppuBuffer + (_overscanMode ? 0 : (_useHighResOutput ? (512 * 14) : (256 * 7)));
|
||||
uint16_t* src = _ppuBuffer + (_overscanMode ? 0 : (_useHighResOutput ? (512 * 14) : (256 * 7)));
|
||||
|
||||
for(uint32_t y = 0, len = _overscanMode ? 239*2 : 224*2; y < len; y++) {
|
||||
for(uint32_t x = 0; x < 512; x++) {
|
||||
for (uint32_t y = 0, len = _overscanMode ? 239 * 2 : 224 * 2; y < len; y++)
|
||||
{
|
||||
for (uint32_t x = 0; x < 512; x++)
|
||||
{
|
||||
int srcOffset = _useHighResOutput ? ((y << 9) | x) : (((y >> 1) << 8) | (x >> 1));
|
||||
buffer[(y + 2)*EventManager::ScanlineWidth + x + 22*2] = DefaultVideoFilter::ToArgb(src[srcOffset]);
|
||||
buffer[(y + 2) * EventManager::ScanlineWidth + x + 22 * 2] = DefaultVideoFilter::ToArgb(src[srcOffset]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,20 +377,24 @@ void EventManager::GetDisplayBuffer(uint32_t *buffer, uint32_t bufferSize, Event
|
|||
constexpr uint32_t currentScanlineColor = 0xFFFFFF55;
|
||||
int nmiScanline = (_overscanMode ? 240 : 225) * 2 * EventManager::ScanlineWidth;
|
||||
uint32_t scanlineOffset = _snapshotScanline * 2 * EventManager::ScanlineWidth;
|
||||
for(int i = 0; i < EventManager::ScanlineWidth; i++) {
|
||||
for (int i = 0; i < EventManager::ScanlineWidth; i++)
|
||||
{
|
||||
buffer[nmiScanline + i] = nmiColor;
|
||||
buffer[nmiScanline + EventManager::ScanlineWidth + i] = nmiColor;
|
||||
if(_snapshotScanline != 0) {
|
||||
if (_snapshotScanline != 0)
|
||||
{
|
||||
buffer[scanlineOffset + i] = currentScanlineColor;
|
||||
buffer[scanlineOffset + EventManager::ScanlineWidth + i] = currentScanlineColor;
|
||||
}
|
||||
}
|
||||
|
||||
FilterEvents(options);
|
||||
for(DebugEventInfo &evt : _sentEvents) {
|
||||
for (DebugEventInfo& evt : _sentEvents)
|
||||
{
|
||||
DrawEvent(evt, true, buffer, options);
|
||||
}
|
||||
for(DebugEventInfo &evt : _sentEvents) {
|
||||
for (DebugEventInfo& evt : _sentEvents)
|
||||
{
|
||||
DrawEvent(evt, false, buffer, options);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,15 +19,15 @@ class EventManager final : public IEventManager
|
|||
private:
|
||||
static constexpr int ScanlineWidth = 1364 / 2;
|
||||
|
||||
Cpu * _cpu;
|
||||
Ppu *_ppu;
|
||||
Cpu* _cpu;
|
||||
Ppu* _ppu;
|
||||
MemoryManager* _memoryManager;
|
||||
DmaController *_dmaController;
|
||||
Debugger *_debugger;
|
||||
DmaController* _dmaController;
|
||||
Debugger* _debugger;
|
||||
vector<DebugEventInfo> _debugEvents;
|
||||
vector<DebugEventInfo> _prevDebugEvents;
|
||||
vector<DebugEventInfo> _sentEvents;
|
||||
|
||||
|
||||
vector<DebugEventInfo> _snapshot;
|
||||
int16_t _snapshotScanline = -1;
|
||||
uint16_t _snapshotCycle = 0;
|
||||
|
@ -36,23 +36,23 @@ private:
|
|||
bool _overscanMode = false;
|
||||
bool _useHighResOutput = false;
|
||||
uint32_t _scanlineCount = 262;
|
||||
uint16_t *_ppuBuffer = nullptr;
|
||||
uint16_t* _ppuBuffer = nullptr;
|
||||
|
||||
void DrawEvent(DebugEventInfo &evt, bool drawBackground, uint32_t *buffer, EventViewerDisplayOptions &options);
|
||||
void FilterEvents(EventViewerDisplayOptions &options);
|
||||
void DrawEvent(DebugEventInfo& evt, bool drawBackground, uint32_t* buffer, EventViewerDisplayOptions& options);
|
||||
void FilterEvents(EventViewerDisplayOptions& options);
|
||||
|
||||
public:
|
||||
EventManager(Debugger *debugger, Cpu *cpu, Ppu *ppu, MemoryManager *memoryManager, DmaController *dmaController);
|
||||
EventManager(Debugger* debugger, Cpu* cpu, Ppu* ppu, MemoryManager* memoryManager, DmaController* dmaController);
|
||||
~EventManager();
|
||||
|
||||
void AddEvent(DebugEventType type, MemoryOperationInfo &operation, int32_t breakpointId = -1);
|
||||
void AddEvent(DebugEventType type, MemoryOperationInfo& operation, int32_t breakpointId = -1);
|
||||
void AddEvent(DebugEventType type);
|
||||
|
||||
void GetEvents(DebugEventInfo *eventArray, uint32_t &maxEventCount);
|
||||
|
||||
void GetEvents(DebugEventInfo* eventArray, uint32_t& maxEventCount);
|
||||
uint32_t GetEventCount(EventViewerDisplayOptions options);
|
||||
void ClearFrameEvents();
|
||||
|
||||
uint32_t TakeEventSnapshot(EventViewerDisplayOptions options);
|
||||
void GetDisplayBuffer(uint32_t *buffer, uint32_t bufferSize, EventViewerDisplayOptions options);
|
||||
DebugEventInfo GetEvent(uint16_t scanline, uint16_t cycle, EventViewerDisplayOptions &options);
|
||||
void GetDisplayBuffer(uint32_t* buffer, uint32_t bufferSize, EventViewerDisplayOptions options);
|
||||
DebugEventInfo GetEvent(uint16_t scanline, uint16_t cycle, EventViewerDisplayOptions& options);
|
||||
};
|
||||
|
|
|
@ -14,4 +14,4 @@ enum class EventType
|
|||
GbStartFrame,
|
||||
GbEndFrame,
|
||||
EventTypeSize
|
||||
};
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -39,8 +39,10 @@ enum EvalOperators : int64_t
|
|||
LogicalNot = 20000000053,
|
||||
|
||||
//Used to read ram address
|
||||
Bracket = 20000000054, //Read byte
|
||||
Braces = 20000000055, //Read word
|
||||
Bracket = 20000000054,
|
||||
//Read byte
|
||||
Braces = 20000000055,
|
||||
//Read word
|
||||
|
||||
//Special value, not used as an operator
|
||||
Parenthesis = 20000000100,
|
||||
|
@ -133,7 +135,7 @@ enum class EvalResultType : int32_t
|
|||
class StringHasher
|
||||
{
|
||||
public:
|
||||
size_t operator()(const std::string& t) const
|
||||
size_t operator()(const std::string& t) const
|
||||
{
|
||||
//Quick hash for expressions - most are likely to have different lengths, and not expecting dozens of breakpoints, either, so this should be fine.
|
||||
return t.size();
|
||||
|
@ -157,36 +159,40 @@ private:
|
|||
|
||||
std::unordered_map<string, ExpressionData, StringHasher> _cache;
|
||||
SimpleLock _cacheLock;
|
||||
|
||||
|
||||
int64_t operandStack[1000];
|
||||
Debugger* _debugger;
|
||||
LabelManager* _labelManager;
|
||||
CpuType _cpuType;
|
||||
SnesMemoryType _cpuMemory;
|
||||
|
||||
bool IsOperator(string token, int &precedence, bool unaryOperator);
|
||||
bool IsOperator(string token, int& precedence, bool unaryOperator);
|
||||
EvalOperators GetOperator(string token, bool unaryOperator);
|
||||
bool CheckSpecialTokens(string expression, size_t &pos, string &output, ExpressionData &data);
|
||||
bool CheckSpecialTokens(string expression, size_t& pos, string& output, ExpressionData& data);
|
||||
int64_t ProcessCpuSpcTokens(string token, bool spc);
|
||||
int64_t ProcessSharedTokens(string token);
|
||||
int64_t ProcessGsuTokens(string token);
|
||||
int64_t ProcessGameboyTokens(string token);
|
||||
string GetNextToken(string expression, size_t &pos, ExpressionData &data, bool &success, bool previousTokenIsOp);
|
||||
bool ProcessSpecialOperator(EvalOperators evalOp, std::stack<EvalOperators> &opStack, std::stack<int> &precedenceStack, vector<int64_t> &outputQueue);
|
||||
bool ToRpn(string expression, ExpressionData &data);
|
||||
int32_t PrivateEvaluate(string expression, DebugState &state, EvalResultType &resultType, MemoryOperationInfo &operationInfo, bool &success);
|
||||
string GetNextToken(string expression, size_t& pos, ExpressionData& data, bool& success, bool previousTokenIsOp);
|
||||
bool ProcessSpecialOperator(EvalOperators evalOp, std::stack<EvalOperators>& opStack,
|
||||
std::stack<int>& precedenceStack, vector<int64_t>& outputQueue);
|
||||
bool ToRpn(string expression, ExpressionData& data);
|
||||
int32_t PrivateEvaluate(string expression, DebugState& state, EvalResultType& resultType,
|
||||
MemoryOperationInfo& operationInfo, bool& success);
|
||||
ExpressionData* PrivateGetRpnList(string expression, bool& success);
|
||||
|
||||
public:
|
||||
ExpressionEvaluator(Debugger* debugger, CpuType cpuType);
|
||||
|
||||
int32_t Evaluate(ExpressionData &data, DebugState &state, EvalResultType &resultType, MemoryOperationInfo &operationInfo);
|
||||
int32_t Evaluate(string expression, DebugState &state, EvalResultType &resultType, MemoryOperationInfo &operationInfo);
|
||||
ExpressionData GetRpnList(string expression, bool &success);
|
||||
int32_t Evaluate(ExpressionData& data, DebugState& state, EvalResultType& resultType,
|
||||
MemoryOperationInfo& operationInfo);
|
||||
int32_t Evaluate(string expression, DebugState& state, EvalResultType& resultType,
|
||||
MemoryOperationInfo& operationInfo);
|
||||
ExpressionData GetRpnList(string expression, bool& success);
|
||||
|
||||
bool Validate(string expression);
|
||||
|
||||
#if _DEBUG
|
||||
void RunTests();
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
|
|
@ -14,20 +14,29 @@ struct MissingFirmwareMessage
|
|||
class FirmwareHelper
|
||||
{
|
||||
private:
|
||||
static bool AttemptLoadDspFirmware(string combinedFilename, string splitFilenameProgram, string splitFilenameData, vector<uint8_t> &programRom, vector<uint8_t> &dataRom, uint32_t programSize, uint32_t dataSize)
|
||||
static bool AttemptLoadDspFirmware(string combinedFilename, string splitFilenameProgram, string splitFilenameData,
|
||||
vector<uint8_t>& programRom, vector<uint8_t>& dataRom, uint32_t programSize,
|
||||
uint32_t dataSize)
|
||||
{
|
||||
VirtualFile combinedFirmware(FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), combinedFilename));
|
||||
if(combinedFirmware.GetSize() == programSize + dataSize) {
|
||||
VirtualFile combinedFirmware(
|
||||
FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), combinedFilename));
|
||||
if (combinedFirmware.GetSize() == programSize + dataSize)
|
||||
{
|
||||
vector<uint8_t> firmwareData;
|
||||
combinedFirmware.ReadFile(firmwareData);
|
||||
programRom.insert(programRom.end(), firmwareData.begin(), firmwareData.begin() + programSize);
|
||||
dataRom.insert(dataRom.end(), firmwareData.begin() + programSize, firmwareData.end());
|
||||
return true;
|
||||
} else {
|
||||
VirtualFile splitFirmwareProg(FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), splitFilenameProgram));
|
||||
VirtualFile splitFirmwareData(FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), splitFilenameData));
|
||||
}
|
||||
else
|
||||
{
|
||||
VirtualFile splitFirmwareProg(
|
||||
FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), splitFilenameProgram));
|
||||
VirtualFile splitFirmwareData(
|
||||
FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), splitFilenameData));
|
||||
|
||||
if(splitFirmwareProg.GetSize() == programSize && splitFirmwareData.GetSize() == dataSize) {
|
||||
if (splitFirmwareProg.GetSize() == programSize && splitFirmwareData.GetSize() == dataSize)
|
||||
{
|
||||
splitFirmwareProg.ReadFile(programRom);
|
||||
splitFirmwareData.ReadFile(dataRom);
|
||||
return true;
|
||||
|
@ -35,11 +44,12 @@ private:
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool AttemptLoadBsxFirmware(uint8_t** prgRom, uint32_t& prgSize)
|
||||
{
|
||||
VirtualFile firmware(FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), "BS-X.bin"));
|
||||
if(firmware.IsValid() && firmware.GetSize() >= 0x8000) {
|
||||
if (firmware.IsValid() && firmware.GetSize() >= 0x8000)
|
||||
{
|
||||
*prgRom = new uint8_t[firmware.GetSize()];
|
||||
prgSize = (uint32_t)firmware.GetSize();
|
||||
firmware.ReadFile(*prgRom, (uint32_t)firmware.GetSize());
|
||||
|
@ -53,12 +63,14 @@ private:
|
|||
{
|
||||
string path = FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), filename);
|
||||
VirtualFile firmware(path);
|
||||
if((!firmware.IsValid() || firmware.GetSize() != size) && !altFilename.empty()) {
|
||||
if ((!firmware.IsValid() || firmware.GetSize() != size) && !altFilename.empty())
|
||||
{
|
||||
string altPath = FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), altFilename);
|
||||
firmware = VirtualFile(altPath);
|
||||
}
|
||||
|
||||
if(firmware.IsValid() && firmware.GetSize() == size) {
|
||||
if (firmware.IsValid() && firmware.GetSize() == size)
|
||||
{
|
||||
*out = new uint8_t[firmware.GetSize()];
|
||||
firmware.ReadFile(*out, (uint32_t)firmware.GetSize());
|
||||
return true;
|
||||
|
@ -68,13 +80,20 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
static bool LoadDspFirmware(Console *console, FirmwareType type, string combinedFilename, string splitFilenameProgram, string splitFilenameData, vector<uint8_t> &programRom, vector<uint8_t> &dataRom, vector<uint8_t> &embeddedFirware, uint32_t programSize = 0x1800, uint32_t dataSize = 0x800)
|
||||
static bool LoadDspFirmware(Console* console, FirmwareType type, string combinedFilename,
|
||||
string splitFilenameProgram, string splitFilenameData, vector<uint8_t>& programRom,
|
||||
vector<uint8_t>& dataRom, vector<uint8_t>& embeddedFirware,
|
||||
uint32_t programSize = 0x1800, uint32_t dataSize = 0x800)
|
||||
{
|
||||
if(embeddedFirware.size() == programSize + dataSize) {
|
||||
if (embeddedFirware.size() == programSize + dataSize)
|
||||
{
|
||||
programRom.insert(programRom.end(), embeddedFirware.begin(), embeddedFirware.begin() + programSize);
|
||||
dataRom.insert(dataRom.end(), embeddedFirware.begin() + programSize, embeddedFirware.end());
|
||||
return true;
|
||||
} else if(AttemptLoadDspFirmware(combinedFilename, splitFilenameProgram, splitFilenameData, programRom, dataRom, programSize, dataSize)) {
|
||||
}
|
||||
else if (AttemptLoadDspFirmware(combinedFilename, splitFilenameProgram, splitFilenameData, programRom, dataRom,
|
||||
programSize, dataSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -85,7 +104,9 @@ public:
|
|||
console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg);
|
||||
|
||||
//Try again in case the user selected a valid firmware file
|
||||
if(AttemptLoadDspFirmware(combinedFilename, splitFilenameProgram, splitFilenameData, programRom, dataRom, programSize, dataSize)) {
|
||||
if (AttemptLoadDspFirmware(combinedFilename, splitFilenameProgram, splitFilenameData, programRom, dataRom,
|
||||
programSize, dataSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -95,17 +116,19 @@ public:
|
|||
|
||||
static bool LoadBsxFirmware(Console* console, uint8_t** prgRom, uint32_t& prgSize)
|
||||
{
|
||||
if(AttemptLoadBsxFirmware(prgRom, prgSize)) {
|
||||
if (AttemptLoadBsxFirmware(prgRom, prgSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
MissingFirmwareMessage msg;
|
||||
msg.Filename = "BS-X.bin";
|
||||
msg.Firmware = FirmwareType::Satellaview;
|
||||
msg.Size = 1024*1024;
|
||||
msg.Size = 1024 * 1024;
|
||||
console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg);
|
||||
|
||||
if(AttemptLoadBsxFirmware(prgRom, prgSize)) {
|
||||
|
||||
if (AttemptLoadBsxFirmware(prgRom, prgSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -117,7 +140,8 @@ public:
|
|||
{
|
||||
string filename = useSgb2 ? "SGB2.sfc" : "SGB1.sfc";
|
||||
prgSize = useSgb2 ? 0x80000 : 0x40000;
|
||||
if(AttemptLoadFirmware(prgRom, filename, prgSize)) {
|
||||
if (AttemptLoadFirmware(prgRom, filename, prgSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -127,7 +151,8 @@ public:
|
|||
msg.Size = prgSize;
|
||||
console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg);
|
||||
|
||||
if(AttemptLoadFirmware(prgRom, filename, prgSize)) {
|
||||
if (AttemptLoadFirmware(prgRom, filename, prgSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -139,16 +164,26 @@ public:
|
|||
{
|
||||
string filename;
|
||||
string altFilename;
|
||||
switch(type) {
|
||||
default:
|
||||
case FirmwareType::Gameboy: filename = "dmg_boot.bin"; altFilename = "gb_bios.bin"; break;
|
||||
case FirmwareType::GameboyColor: filename = "cgb_boot.bin"; altFilename = "gbc_bios.bin"; break;
|
||||
case FirmwareType::Sgb1GameboyCpu: filename = "sgb_boot.bin"; altFilename = "sgb_bios.bin"; break;
|
||||
case FirmwareType::Sgb2GameboyCpu: filename = "sgb2_boot.bin"; altFilename = "sgb_bios.bin"; break;
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case FirmwareType::Gameboy: filename = "dmg_boot.bin";
|
||||
altFilename = "gb_bios.bin";
|
||||
break;
|
||||
case FirmwareType::GameboyColor: filename = "cgb_boot.bin";
|
||||
altFilename = "gbc_bios.bin";
|
||||
break;
|
||||
case FirmwareType::Sgb1GameboyCpu: filename = "sgb_boot.bin";
|
||||
altFilename = "sgb_bios.bin";
|
||||
break;
|
||||
case FirmwareType::Sgb2GameboyCpu: filename = "sgb2_boot.bin";
|
||||
altFilename = "sgb_bios.bin";
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t size = type == FirmwareType::GameboyColor ? 2304 : 256;
|
||||
if(AttemptLoadFirmware(bootRom, filename, size, altFilename)) {
|
||||
if (AttemptLoadFirmware(bootRom, filename, size, altFilename))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -165,4 +200,4 @@ public:
|
|||
MessageManager::DisplayMessage("Error", "Could not find boot rom: " + filename);*/
|
||||
return false;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -11,13 +11,15 @@ private:
|
|||
string _disconnectMessage;
|
||||
|
||||
protected:
|
||||
void Serialize(Serializer &s) override
|
||||
void Serialize(Serializer& s) override
|
||||
{
|
||||
s.Stream(_disconnectMessage);
|
||||
}
|
||||
|
||||
public:
|
||||
ForceDisconnectMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
|
||||
ForceDisconnectMessage(void* buffer, uint32_t length) : NetMessage(buffer, length)
|
||||
{
|
||||
}
|
||||
|
||||
ForceDisconnectMessage(string message) : NetMessage(MessageType::ForceDisconnect)
|
||||
{
|
||||
|
@ -28,4 +30,4 @@ public:
|
|||
{
|
||||
return _disconnectMessage;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -25,7 +25,8 @@ public:
|
|||
|
||||
void ProcessFrame()
|
||||
{
|
||||
if(_resetRunTimers || (_clockTimer.GetElapsedMS() - _targetTime) > 300) {
|
||||
if (_resetRunTimers || (_clockTimer.GetElapsedMS() - _targetTime) > 300)
|
||||
{
|
||||
//Reset the timers, this can happen in 3 scenarios:
|
||||
//1) Target frame rate changed
|
||||
//2) The console was reset/power cycled or the emulation was paused (with or without the debugger)
|
||||
|
@ -41,7 +42,8 @@ public:
|
|||
|
||||
bool WaitForNextFrame()
|
||||
{
|
||||
if(_targetTime - _clockTimer.GetElapsedMS() > 50) {
|
||||
if (_targetTime - _clockTimer.GetElapsedMS() > 50)
|
||||
{
|
||||
//When sleeping for a long time (e.g <= 25% speed), sleep in small chunks and check to see if we need to stop sleeping between each sleep call
|
||||
_clockTimer.WaitUntil(_clockTimer.GetElapsedMS() + 40);
|
||||
return true;
|
||||
|
@ -50,4 +52,4 @@ public:
|
|||
_clockTimer.WaitUntil(_targetTime);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -21,7 +21,8 @@ GameClient::GameClient(shared_ptr<Console> console)
|
|||
GameClient::~GameClient()
|
||||
{
|
||||
_stop = true;
|
||||
if(_clientThread) {
|
||||
if (_clientThread)
|
||||
{
|
||||
_clientThread->join();
|
||||
}
|
||||
}
|
||||
|
@ -32,13 +33,14 @@ bool GameClient::Connected()
|
|||
return instance ? instance->_connected : false;
|
||||
}
|
||||
|
||||
void GameClient::Connect(shared_ptr<Console> console, ClientConnectionData &connectionData)
|
||||
void GameClient::Connect(shared_ptr<Console> console, ClientConnectionData& connectionData)
|
||||
{
|
||||
_instance.reset(new GameClient(console));
|
||||
console->GetNotificationManager()->RegisterNotificationListener(_instance);
|
||||
|
||||
|
||||
shared_ptr<GameClient> instance = _instance;
|
||||
if(instance) {
|
||||
if (instance)
|
||||
{
|
||||
instance->PrivateConnect(connectionData);
|
||||
instance->_clientThread.reset(new thread(&GameClient::Exec, instance.get()));
|
||||
}
|
||||
|
@ -55,15 +57,18 @@ shared_ptr<GameClientConnection> GameClient::GetConnection()
|
|||
return instance ? instance->_connection : nullptr;
|
||||
}
|
||||
|
||||
void GameClient::PrivateConnect(ClientConnectionData &connectionData)
|
||||
void GameClient::PrivateConnect(ClientConnectionData& connectionData)
|
||||
{
|
||||
_stop = false;
|
||||
shared_ptr<Socket> socket(new Socket());
|
||||
if(socket->Connect(connectionData.Host.c_str(), connectionData.Port)) {
|
||||
if (socket->Connect(connectionData.Host.c_str(), connectionData.Port))
|
||||
{
|
||||
_connection.reset(new GameClientConnection(_console, socket, connectionData));
|
||||
_console->GetNotificationManager()->RegisterNotificationListener(_connection);
|
||||
_connected = true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageManager::DisplayMessage("NetPlay", "CouldNotConnect");
|
||||
_connected = false;
|
||||
}
|
||||
|
@ -71,12 +76,17 @@ void GameClient::PrivateConnect(ClientConnectionData &connectionData)
|
|||
|
||||
void GameClient::Exec()
|
||||
{
|
||||
if(_connected) {
|
||||
while(!_stop) {
|
||||
if(!_connection->ConnectionError()) {
|
||||
if (_connected)
|
||||
{
|
||||
while (!_stop)
|
||||
{
|
||||
if (!_connection->ConnectionError())
|
||||
{
|
||||
_connection->ProcessMessages();
|
||||
_connection->SendInput();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_connected = false;
|
||||
_connection->Shutdown();
|
||||
_connection.reset();
|
||||
|
@ -89,10 +99,11 @@ void GameClient::Exec()
|
|||
|
||||
void GameClient::ProcessNotification(ConsoleNotificationType type, void* parameter)
|
||||
{
|
||||
if(type == ConsoleNotificationType::GameLoaded &&
|
||||
std::this_thread::get_id() != _clientThread->get_id() &&
|
||||
if (type == ConsoleNotificationType::GameLoaded &&
|
||||
std::this_thread::get_id() != _clientThread->get_id() &&
|
||||
std::this_thread::get_id() != _console->GetEmulationThreadId()
|
||||
) {
|
||||
)
|
||||
{
|
||||
//Disconnect if the client tried to manually load a game
|
||||
//A deadlock occurs if this is called from the emulation thread while a network message is being processed
|
||||
GameClient::Disconnect();
|
||||
|
@ -102,7 +113,8 @@ void GameClient::ProcessNotification(ConsoleNotificationType type, void* paramet
|
|||
void GameClient::SelectController(uint8_t port)
|
||||
{
|
||||
shared_ptr<GameClientConnection> connection = GetConnection();
|
||||
if(connection) {
|
||||
if (connection)
|
||||
{
|
||||
connection->SelectController(port);
|
||||
}
|
||||
}
|
||||
|
@ -117,4 +129,4 @@ uint8_t GameClient::GetControllerPort()
|
|||
{
|
||||
shared_ptr<GameClientConnection> connection = GetConnection();
|
||||
return connection ? connection->GetControllerPort() : GameConnection::SpectatorPort;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ private:
|
|||
|
||||
static shared_ptr<GameClientConnection> GetConnection();
|
||||
|
||||
void PrivateConnect(ClientConnectionData &connectionData);
|
||||
void PrivateConnect(ClientConnectionData& connectionData);
|
||||
void Exec();
|
||||
|
||||
public:
|
||||
|
@ -31,7 +31,7 @@ public:
|
|||
virtual ~GameClient();
|
||||
|
||||
static bool Connected();
|
||||
static void Connect(shared_ptr<Console> console, ClientConnectionData &connectionData);
|
||||
static void Connect(shared_ptr<Console> console, ClientConnectionData& connectionData);
|
||||
static void Disconnect();
|
||||
|
||||
static void SelectController(uint8_t port);
|
||||
|
@ -39,4 +39,4 @@ public:
|
|||
static uint8_t GetAvailableControllers();
|
||||
|
||||
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
#include "NotificationManager.h"
|
||||
#include "RomFinder.h"
|
||||
|
||||
GameClientConnection::GameClientConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, ClientConnectionData &connectionData) : GameConnection(console, socket)
|
||||
GameClientConnection::GameClientConnection(shared_ptr<Console> console, shared_ptr<Socket> socket,
|
||||
ClientConnectionData& connectionData) : GameConnection(console, socket)
|
||||
{
|
||||
_connectionData = connectionData;
|
||||
_shutdown = false;
|
||||
|
@ -34,12 +35,14 @@ GameClientConnection::~GameClientConnection()
|
|||
|
||||
void GameClientConnection::Shutdown()
|
||||
{
|
||||
if(!_shutdown) {
|
||||
if (!_shutdown)
|
||||
{
|
||||
_shutdown = true;
|
||||
DisableControllers();
|
||||
|
||||
shared_ptr<ControlManager> controlManager = _console->GetControlManager();
|
||||
if(controlManager) {
|
||||
if (controlManager)
|
||||
{
|
||||
controlManager->UnregisterInputProvider(this);
|
||||
}
|
||||
|
||||
|
@ -50,7 +53,9 @@ void GameClientConnection::Shutdown()
|
|||
|
||||
void GameClientConnection::SendHandshake()
|
||||
{
|
||||
HandShakeMessage message(_connectionData.PlayerName, HandShakeMessage::GetPasswordHash(_connectionData.Password, _serverSalt), _connectionData.Spectator, _console->GetSettings()->GetVersion());
|
||||
HandShakeMessage message(_connectionData.PlayerName,
|
||||
HandShakeMessage::GetPasswordHash(_connectionData.Password, _serverSalt),
|
||||
_connectionData.Spectator, _console->GetSettings()->GetVersion());
|
||||
SendNetMessage(message);
|
||||
}
|
||||
|
||||
|
@ -63,7 +68,8 @@ void GameClientConnection::SendControllerSelection(uint8_t port)
|
|||
void GameClientConnection::ClearInputData()
|
||||
{
|
||||
LockHandler lock = _writeLock.AcquireSafe();
|
||||
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
|
||||
for (int i = 0; i < BaseControlDevice::PortCount; i++)
|
||||
{
|
||||
_inputSize[i] = 0;
|
||||
_inputData[i].clear();
|
||||
}
|
||||
|
@ -73,80 +79,98 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
|
|||
{
|
||||
GameInformationMessage* gameInfo;
|
||||
|
||||
switch(message->GetType()) {
|
||||
case MessageType::ServerInformation:
|
||||
_serverSalt = ((ServerInformationMessage*)message)->GetHashSalt();
|
||||
SendHandshake();
|
||||
break;
|
||||
switch (message->GetType())
|
||||
{
|
||||
case MessageType::ServerInformation:
|
||||
_serverSalt = ((ServerInformationMessage*)message)->GetHashSalt();
|
||||
SendHandshake();
|
||||
break;
|
||||
|
||||
case MessageType::SaveState:
|
||||
if(_gameLoaded) {
|
||||
DisableControllers();
|
||||
_console->Lock();
|
||||
ClearInputData();
|
||||
((SaveStateMessage*)message)->LoadState(_console);
|
||||
_enableControllers = true;
|
||||
InitControlDevice();
|
||||
_console->Unlock();
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageType::MovieData:
|
||||
if(_gameLoaded) {
|
||||
PushControllerState(((MovieDataMessage*)message)->GetPortNumber(), ((MovieDataMessage*)message)->GetInputState());
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageType::ForceDisconnect:
|
||||
MessageManager::DisplayMessage("NetPlay", ((ForceDisconnectMessage*)message)->GetMessage());
|
||||
break;
|
||||
|
||||
case MessageType::PlayerList:
|
||||
_playerList = ((PlayerListMessage*)message)->GetPlayerList();
|
||||
break;
|
||||
|
||||
case MessageType::GameInformation:
|
||||
case MessageType::SaveState:
|
||||
if (_gameLoaded)
|
||||
{
|
||||
DisableControllers();
|
||||
_console->Lock();
|
||||
gameInfo = (GameInformationMessage*)message;
|
||||
if(gameInfo->GetPort() != _controllerPort) {
|
||||
_controllerPort = gameInfo->GetPort();
|
||||
|
||||
if(_controllerPort == GameConnection::SpectatorPort) {
|
||||
MessageManager::DisplayMessage("NetPlay", "ConnectedAsSpectator");
|
||||
} else {
|
||||
MessageManager::DisplayMessage("NetPlay", "ConnectedAsPlayer", std::to_string(_controllerPort + 1));
|
||||
}
|
||||
}
|
||||
|
||||
ClearInputData();
|
||||
((SaveStateMessage*)message)->LoadState(_console);
|
||||
_enableControllers = true;
|
||||
InitControlDevice();
|
||||
_console->Unlock();
|
||||
}
|
||||
break;
|
||||
|
||||
_gameLoaded = AttemptLoadGame(gameInfo->GetRomFilename(), gameInfo->GetSha1Hash());
|
||||
if(!_gameLoaded) {
|
||||
_console->Stop(true);
|
||||
} else {
|
||||
_console->GetControlManager()->UnregisterInputProvider(this);
|
||||
_console->GetControlManager()->RegisterInputProvider(this);
|
||||
if(gameInfo->IsPaused()) {
|
||||
_console->Pause();
|
||||
} else {
|
||||
_console->Resume();
|
||||
}
|
||||
case MessageType::MovieData:
|
||||
if (_gameLoaded)
|
||||
{
|
||||
PushControllerState(((MovieDataMessage*)message)->GetPortNumber(),
|
||||
((MovieDataMessage*)message)->GetInputState());
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageType::ForceDisconnect:
|
||||
MessageManager::DisplayMessage("NetPlay", ((ForceDisconnectMessage*)message)->GetMessage());
|
||||
break;
|
||||
|
||||
case MessageType::PlayerList:
|
||||
_playerList = ((PlayerListMessage*)message)->GetPlayerList();
|
||||
break;
|
||||
|
||||
case MessageType::GameInformation:
|
||||
DisableControllers();
|
||||
_console->Lock();
|
||||
gameInfo = (GameInformationMessage*)message;
|
||||
if (gameInfo->GetPort() != _controllerPort)
|
||||
{
|
||||
_controllerPort = gameInfo->GetPort();
|
||||
|
||||
if (_controllerPort == GameConnection::SpectatorPort)
|
||||
{
|
||||
MessageManager::DisplayMessage("NetPlay", "ConnectedAsSpectator");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
else
|
||||
{
|
||||
MessageManager::DisplayMessage("NetPlay", "ConnectedAsPlayer", std::to_string(_controllerPort + 1));
|
||||
}
|
||||
}
|
||||
|
||||
ClearInputData();
|
||||
_console->Unlock();
|
||||
|
||||
_gameLoaded = AttemptLoadGame(gameInfo->GetRomFilename(), gameInfo->GetSha1Hash());
|
||||
if (!_gameLoaded)
|
||||
{
|
||||
_console->Stop(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_console->GetControlManager()->UnregisterInputProvider(this);
|
||||
_console->GetControlManager()->RegisterInputProvider(this);
|
||||
if (gameInfo->IsPaused())
|
||||
{
|
||||
_console->Pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
_console->Resume();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool GameClientConnection::AttemptLoadGame(string filename, string sha1Hash)
|
||||
{
|
||||
if(filename.size() > 0) {
|
||||
if(!RomFinder::LoadMatchingRom(_console.get(), filename, sha1Hash)) {
|
||||
if (filename.size() > 0)
|
||||
{
|
||||
if (!RomFinder::LoadMatchingRom(_console.get(), filename, sha1Hash))
|
||||
{
|
||||
MessageManager::DisplayMessage("NetPlay", "CouldNotFindRom", filename);
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +183,8 @@ void GameClientConnection::PushControllerState(uint8_t port, ControlDeviceState
|
|||
_inputData[port].push_back(state);
|
||||
_inputSize[port]++;
|
||||
|
||||
if(_inputData[port].size() >= _minimumQueueSize) {
|
||||
if (_inputData[port].size() >= _minimumQueueSize)
|
||||
{
|
||||
_waitForInput[port].Signal();
|
||||
}
|
||||
}
|
||||
|
@ -169,30 +194,36 @@ void GameClientConnection::DisableControllers()
|
|||
//Used to prevent deadlocks when client is trying to fill its buffer while the host changes the current game/settings/etc. (i.e situations where we need to call Console::Pause())
|
||||
_enableControllers = false;
|
||||
ClearInputData();
|
||||
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
|
||||
for (int i = 0; i < BaseControlDevice::PortCount; i++)
|
||||
{
|
||||
_waitForInput[i].Signal();
|
||||
}
|
||||
}
|
||||
|
||||
bool GameClientConnection::SetInput(BaseControlDevice *device)
|
||||
bool GameClientConnection::SetInput(BaseControlDevice* device)
|
||||
{
|
||||
if(_enableControllers) {
|
||||
if (_enableControllers)
|
||||
{
|
||||
uint8_t port = device->GetPort();
|
||||
while(_inputSize[port] == 0) {
|
||||
while (_inputSize[port] == 0)
|
||||
{
|
||||
_waitForInput[port].Wait();
|
||||
|
||||
if(port == 0 && _minimumQueueSize < 10) {
|
||||
if (port == 0 && _minimumQueueSize < 10)
|
||||
{
|
||||
//Increase buffer size - reduces freezes at the cost of additional lag
|
||||
_minimumQueueSize++;
|
||||
}
|
||||
|
||||
if(_shutdown || !_enableControllers) {
|
||||
if (_shutdown || !_enableControllers)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LockHandler lock = _writeLock.AcquireSafe();
|
||||
if(_shutdown || !_enableControllers || _inputSize[port] == 0) {
|
||||
if (_shutdown || !_enableControllers || _inputSize[port] == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -200,10 +231,13 @@ bool GameClientConnection::SetInput(BaseControlDevice *device)
|
|||
_inputData[port].pop_front();
|
||||
_inputSize[port]--;
|
||||
|
||||
if(_inputData[port].size() > _minimumQueueSize) {
|
||||
if (_inputData[port].size() > _minimumQueueSize)
|
||||
{
|
||||
//Too much data, catch up
|
||||
_console->GetSettings()->SetFlag(EmulationFlags::MaximumSpeed);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_console->GetSettings()->ClearFlag(EmulationFlags::MaximumSpeed);
|
||||
}
|
||||
|
||||
|
@ -215,33 +249,41 @@ bool GameClientConnection::SetInput(BaseControlDevice *device)
|
|||
void GameClientConnection::InitControlDevice()
|
||||
{
|
||||
//Pretend we are using port 0 (to use player 1's keybindings during netplay)
|
||||
_newControlDevice = ControlManager::CreateControllerDevice(_console->GetSettings()->GetInputConfig().Controllers[_controllerPort].Type, 0, _console.get());
|
||||
_newControlDevice = ControlManager::CreateControllerDevice(
|
||||
_console->GetSettings()->GetInputConfig().Controllers[_controllerPort].Type, 0, _console.get());
|
||||
}
|
||||
|
||||
void GameClientConnection::ProcessNotification(ConsoleNotificationType type, void* parameter)
|
||||
{
|
||||
if(type == ConsoleNotificationType::ConfigChanged) {
|
||||
if (type == ConsoleNotificationType::ConfigChanged)
|
||||
{
|
||||
InitControlDevice();
|
||||
} else if(type == ConsoleNotificationType::GameLoaded) {
|
||||
}
|
||||
else if (type == ConsoleNotificationType::GameLoaded)
|
||||
{
|
||||
_console->GetControlManager()->RegisterInputProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
void GameClientConnection::SendInput()
|
||||
{
|
||||
if(_gameLoaded) {
|
||||
if(_newControlDevice) {
|
||||
if (_gameLoaded)
|
||||
{
|
||||
if (_newControlDevice)
|
||||
{
|
||||
_controlDevice = _newControlDevice;
|
||||
_newControlDevice.reset();
|
||||
}
|
||||
|
||||
ControlDeviceState inputState;
|
||||
if(_controlDevice) {
|
||||
if (_controlDevice)
|
||||
{
|
||||
_controlDevice->SetStateFromInput();
|
||||
inputState = _controlDevice->GetRawState();
|
||||
}
|
||||
|
||||
if(_lastInputSent != inputState) {
|
||||
|
||||
if (_lastInputSent != inputState)
|
||||
{
|
||||
InputDataMessage message(inputState);
|
||||
SendNetMessage(message);
|
||||
_lastInputSent = inputState;
|
||||
|
@ -257,8 +299,10 @@ void GameClientConnection::SelectController(uint8_t port)
|
|||
uint8_t GameClientConnection::GetAvailableControllers()
|
||||
{
|
||||
uint8_t availablePorts = (1 << BaseControlDevice::PortCount) - 1;
|
||||
for(PlayerInfo &playerInfo : _playerList) {
|
||||
if(playerInfo.ControllerPort < BaseControlDevice::PortCount) {
|
||||
for (PlayerInfo& playerInfo : _playerList)
|
||||
{
|
||||
if (playerInfo.ControllerPort < BaseControlDevice::PortCount)
|
||||
{
|
||||
availablePorts &= ~(1 << playerInfo.ControllerPort);
|
||||
}
|
||||
}
|
||||
|
@ -268,4 +312,4 @@ uint8_t GameClientConnection::GetAvailableControllers()
|
|||
uint8_t GameClientConnection::GetControllerPort()
|
||||
{
|
||||
return _controllerPort;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,18 +45,18 @@ protected:
|
|||
void ProcessMessage(NetMessage* message) override;
|
||||
|
||||
public:
|
||||
GameClientConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, ClientConnectionData &connectionData);
|
||||
GameClientConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, ClientConnectionData& connectionData);
|
||||
virtual ~GameClientConnection();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
|
||||
|
||||
bool SetInput(BaseControlDevice *device) override;
|
||||
bool SetInput(BaseControlDevice* device) override;
|
||||
void InitControlDevice();
|
||||
void SendInput();
|
||||
|
||||
void SelectController(uint8_t port);
|
||||
uint8_t GetAvailableControllers();
|
||||
uint8_t GetControllerPort();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -20,17 +20,20 @@ GameConnection::GameConnection(shared_ptr<Console> console, shared_ptr<Socket> s
|
|||
void GameConnection::ReadSocket()
|
||||
{
|
||||
auto lock = _socketLock.AcquireSafe();
|
||||
int bytesReceived = _socket->Recv((char*)_readBuffer + _readPosition, GameConnection::MaxMsgLength - _readPosition, 0);
|
||||
if(bytesReceived > 0) {
|
||||
int bytesReceived = _socket->Recv((char*)_readBuffer + _readPosition, GameConnection::MaxMsgLength - _readPosition,
|
||||
0);
|
||||
if (bytesReceived > 0)
|
||||
{
|
||||
_readPosition += bytesReceived;
|
||||
}
|
||||
}
|
||||
|
||||
bool GameConnection::ExtractMessage(void *buffer, uint32_t &messageLength)
|
||||
bool GameConnection::ExtractMessage(void* buffer, uint32_t& messageLength)
|
||||
{
|
||||
messageLength = _readBuffer[0] | (_readBuffer[1] << 8) | (_readBuffer[2] << 16) | (_readBuffer[3] << 24);
|
||||
|
||||
if(messageLength > GameConnection::MaxMsgLength) {
|
||||
if (messageLength > GameConnection::MaxMsgLength)
|
||||
{
|
||||
MessageManager::Log("[Netplay] Invalid data received, closing connection.");
|
||||
Disconnect();
|
||||
return false;
|
||||
|
@ -38,8 +41,9 @@ bool GameConnection::ExtractMessage(void *buffer, uint32_t &messageLength)
|
|||
|
||||
int packetLength = messageLength + sizeof(messageLength);
|
||||
|
||||
if(_readPosition >= packetLength) {
|
||||
memcpy(buffer, _readBuffer+sizeof(messageLength), messageLength);
|
||||
if (_readPosition >= packetLength)
|
||||
{
|
||||
memcpy(buffer, _readBuffer + sizeof(messageLength), messageLength);
|
||||
memmove(_readBuffer, _readBuffer + packetLength, _readPosition - packetLength);
|
||||
_readPosition -= packetLength;
|
||||
return true;
|
||||
|
@ -51,26 +55,29 @@ NetMessage* GameConnection::ReadMessage()
|
|||
{
|
||||
ReadSocket();
|
||||
|
||||
if(_readPosition > 4) {
|
||||
if (_readPosition > 4)
|
||||
{
|
||||
uint32_t messageLength;
|
||||
if(ExtractMessage(_messageBuffer, messageLength)) {
|
||||
switch((MessageType)_messageBuffer[0]) {
|
||||
case MessageType::HandShake: return new HandShakeMessage(_messageBuffer, messageLength);
|
||||
case MessageType::SaveState: return new SaveStateMessage(_messageBuffer, messageLength);
|
||||
case MessageType::InputData: return new InputDataMessage(_messageBuffer, messageLength);
|
||||
case MessageType::MovieData: return new MovieDataMessage(_messageBuffer, messageLength);
|
||||
case MessageType::GameInformation: return new GameInformationMessage(_messageBuffer, messageLength);
|
||||
case MessageType::PlayerList: return new PlayerListMessage(_messageBuffer, messageLength);
|
||||
case MessageType::SelectController: return new SelectControllerMessage(_messageBuffer, messageLength);
|
||||
case MessageType::ForceDisconnect: return new ForceDisconnectMessage(_messageBuffer, messageLength);
|
||||
case MessageType::ServerInformation: return new ServerInformationMessage(_messageBuffer, messageLength);
|
||||
if (ExtractMessage(_messageBuffer, messageLength))
|
||||
{
|
||||
switch ((MessageType)_messageBuffer[0])
|
||||
{
|
||||
case MessageType::HandShake: return new HandShakeMessage(_messageBuffer, messageLength);
|
||||
case MessageType::SaveState: return new SaveStateMessage(_messageBuffer, messageLength);
|
||||
case MessageType::InputData: return new InputDataMessage(_messageBuffer, messageLength);
|
||||
case MessageType::MovieData: return new MovieDataMessage(_messageBuffer, messageLength);
|
||||
case MessageType::GameInformation: return new GameInformationMessage(_messageBuffer, messageLength);
|
||||
case MessageType::PlayerList: return new PlayerListMessage(_messageBuffer, messageLength);
|
||||
case MessageType::SelectController: return new SelectControllerMessage(_messageBuffer, messageLength);
|
||||
case MessageType::ForceDisconnect: return new ForceDisconnectMessage(_messageBuffer, messageLength);
|
||||
case MessageType::ServerInformation: return new ServerInformationMessage(_messageBuffer, messageLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GameConnection::SendNetMessage(NetMessage &message)
|
||||
void GameConnection::SendNetMessage(NetMessage& message)
|
||||
{
|
||||
auto lock = _socketLock.AcquireSafe();
|
||||
message.Send(*_socket.get());
|
||||
|
@ -90,10 +97,11 @@ bool GameConnection::ConnectionError()
|
|||
void GameConnection::ProcessMessages()
|
||||
{
|
||||
NetMessage* message;
|
||||
while((message = ReadMessage()) != nullptr) {
|
||||
while ((message = ReadMessage()) != nullptr)
|
||||
{
|
||||
//Loop until all messages have been processed
|
||||
message->Initialize();
|
||||
ProcessMessage(message);
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ private:
|
|||
|
||||
void ReadSocket();
|
||||
|
||||
bool ExtractMessage(void *buffer, uint32_t &messageLength);
|
||||
bool ExtractMessage(void* buffer, uint32_t& messageLength);
|
||||
NetMessage* ReadMessage();
|
||||
|
||||
virtual void ProcessMessage(NetMessage* message) = 0;
|
||||
|
@ -44,5 +44,5 @@ public:
|
|||
|
||||
bool ConnectionError();
|
||||
void ProcessMessages();
|
||||
void SendNetMessage(NetMessage &message);
|
||||
};
|
||||
void SendNetMessage(NetMessage& message);
|
||||
};
|
||||
|
|
|
@ -13,22 +13,25 @@ private:
|
|||
bool _paused = false;
|
||||
|
||||
protected:
|
||||
void Serialize(Serializer &s) override
|
||||
void Serialize(Serializer& s) override
|
||||
{
|
||||
s.Stream(_romFilename, _sha1Hash, _controllerPort, _paused);
|
||||
}
|
||||
|
||||
public:
|
||||
GameInformationMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
|
||||
GameInformationMessage(void* buffer, uint32_t length) : NetMessage(buffer, length)
|
||||
{
|
||||
}
|
||||
|
||||
GameInformationMessage(string filepath, string sha1Hash, uint8_t port, bool paused) : NetMessage(MessageType::GameInformation)
|
||||
GameInformationMessage(string filepath, string sha1Hash, uint8_t port, bool paused) : NetMessage(
|
||||
MessageType::GameInformation)
|
||||
{
|
||||
_romFilename = FolderUtilities::GetFilename(filepath, true);
|
||||
_sha1Hash = sha1Hash;
|
||||
_controllerPort = port;
|
||||
_paused = paused;
|
||||
}
|
||||
|
||||
|
||||
uint8_t GetPort()
|
||||
{
|
||||
return _controllerPort;
|
||||
|
@ -48,4 +51,4 @@ public:
|
|||
{
|
||||
return _paused;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue