Merge branch 'reformat_code'

This commit is contained in:
Vladimir Kononovich 2020-12-19 23:33:29 +03:00
commit daf3b57e89
573 changed files with 82336 additions and 65195 deletions

View file

@ -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,12 +46,14 @@ 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;
}
@ -60,7 +68,8 @@ uint8_t AluMulDiv::Read(uint16_t addr)
{
Run(true);
switch(addr) {
switch (addr)
{
case 0x4214: return (uint8_t)_state.DivResult;
case 0x4215: return (uint8_t)(_state.DivResult >> 8);
@ -75,11 +84,14 @@ void AluMulDiv::Write(uint16_t addr, uint8_t value)
{
Run(false);
switch(addr) {
case 0x4202: _state.MultOperand1 = value; break;
switch (addr)
{
case 0x4202: _state.MultOperand1 = value;
break;
case 0x4203:
_state.MultOrRemainderResult = 0;
if(!_divCounter && !_multCounter) {
if (!_divCounter && !_multCounter)
{
_multCounter = 8;
_state.MultOperand2 = value;
@ -88,13 +100,16 @@ void AluMulDiv::Write(uint16_t addr, uint8_t value)
}
break;
case 0x4204: _state.Dividend = (_state.Dividend & 0xFF00) | value; break;
case 0x4205: _state.Dividend = (_state.Dividend & 0xFF) | (value << 8); 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;
if(!_divCounter && !_multCounter) {
if (!_divCounter && !_multCounter)
{
_divCounter = 16;
_state.Divisor = value;
_shift = (value << 16);
@ -113,7 +128,8 @@ AluState AluMulDiv::GetState()
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
);
}

View file

@ -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.OperandSuffix == "],Y") {
if(opSize == 1) {
else if (lineData.HasOpeningBracket)
{
if (lineData.OperandSuffix == "]")
{
switch (opSize)
{
case 1: lineData.Mode = AddrMode::DirIndLng;
break;
case 2: lineData.Mode = AddrMode::AbsIndLng;
break;
default: invalid = true;
break;
}
}
else if (lineData.OperandSuffix == "],Y")
{
if (opSize == 1)
{
lineData.Mode = AddrMode::DirIndLngIdxY;
} else {
}
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);
}
}

View file

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

View file

@ -40,11 +40,14 @@ BaseCartridge::~BaseCartridge()
shared_ptr<BaseCartridge> BaseCartridge::CreateCartridge(Console* console, VirtualFile& romFile, VirtualFile& patchFile)
{
if(romFile.IsValid()) {
if (romFile.IsValid())
{
shared_ptr<BaseCartridge> cart(new BaseCartridge());
if(patchFile.IsValid()) {
if (patchFile.IsValid())
{
cart->_patchPath = patchFile;
if(romFile.ApplyPatch(patchFile)) {
if (romFile.ApplyPatch(patchFile))
{
MessageManager::DisplayMessage("Patch", "ApplyingPatch", patchFile.GetFileName());
}
}
@ -52,7 +55,8 @@ shared_ptr<BaseCartridge> BaseCartridge::CreateCartridge(Console* console, Virtu
vector<uint8_t> romData;
romFile.ReadFile(romData);
if(romData.size() < 0x4000) {
if (romData.size() < 0x4000)
{
return nullptr;
}
@ -60,24 +64,35 @@ shared_ptr<BaseCartridge> BaseCartridge::CreateCartridge(Console* console, Virtu
cart->_romPath = romFile;
string fileExt = FolderUtilities::GetExtension(romFile.GetFileName());
if(fileExt == ".bs") {
if (fileExt == ".bs")
{
cart->_bsxMemPack.reset(new BsxMemoryPack(console, romData, false));
if(!FirmwareHelper::LoadBsxFirmware(console, &cart->_prgRom, cart->_prgRomSize)) {
if (!FirmwareHelper::LoadBsxFirmware(console, &cart->_prgRom, cart->_prgRomSize))
{
return nullptr;
}
} else if(fileExt == ".gb" || fileExt == ".gbc") {
if(cart->LoadGameboy(romFile, true)) {
}
else if (fileExt == ".gb" || fileExt == ".gbc")
{
if (cart->LoadGameboy(romFile, true))
{
return cart;
} else {
}
else
{
return nullptr;
}
} else {
if(romData.size() < 0x8000) {
}
else
{
if (romData.size() < 0x8000)
{
return nullptr;
}
cart->_prgRomSize = (uint32_t)romData.size();
if((cart->_prgRomSize & 0xFFF) != 0) {
if ((cart->_prgRomSize & 0xFFF) != 0)
{
//Round up to the next 4kb size, to ensure we have access to all the rom's data
cart->_prgRomSize = (cart->_prgRomSize & ~0xFFF) + 0x1000;
}
@ -86,19 +101,27 @@ shared_ptr<BaseCartridge> BaseCartridge::CreateCartridge(Console* console, Virtu
memcpy(cart->_prgRom, romData.data(), romData.size());
}
if(memcmp(cart->_prgRom, "SNES-SPC700 Sound File Data", 27) == 0) {
if(cart->_prgRomSize >= 0x10200) {
if (memcmp(cart->_prgRom, "SNES-SPC700 Sound File Data", 27) == 0)
{
if (cart->_prgRomSize >= 0x10200)
{
//SPC files must be 0x10200 bytes long at minimum
cart->LoadSpc();
} else {
}
else
{
return nullptr;
}
} else {
}
else
{
cart->LoadRom();
}
return cart;
} else {
}
else
{
return nullptr;
}
}
@ -106,7 +129,8 @@ shared_ptr<BaseCartridge> BaseCartridge::CreateCartridge(Console* console, Virtu
int32_t BaseCartridge::GetHeaderScore(uint32_t addr)
{
//Try to figure out where the header is by using a scoring system
if(_prgRomSize < addr + 0x7FFF) {
if (_prgRomSize < addr + 0x7FFF)
{
return -1;
}
@ -115,42 +139,55 @@ int32_t BaseCartridge::GetHeaderScore(uint32_t addr)
uint32_t score = 0;
uint8_t mode = (cartInfo.MapMode & ~0x10);
if((mode == 0x20 || mode == 0x22) && addr < 0x8000) {
if ((mode == 0x20 || mode == 0x22) && addr < 0x8000)
{
score++;
} else if((mode == 0x21 || mode == 0x25) && addr >= 0x8000) {
}
else if ((mode == 0x21 || mode == 0x25) && addr >= 0x8000)
{
score++;
}
if(cartInfo.RomType < 0x08) {
if (cartInfo.RomType < 0x08)
{
score++;
}
if(cartInfo.RomSize < 0x10) {
if (cartInfo.RomSize < 0x10)
{
score++;
}
if(cartInfo.SramSize < 0x08) {
if (cartInfo.SramSize < 0x08)
{
score++;
}
uint16_t checksum = cartInfo.Checksum[0] | (cartInfo.Checksum[1] << 8);
uint16_t complement = cartInfo.ChecksumComplement[0] | (cartInfo.ChecksumComplement[1] << 8);
if(checksum + complement == 0xFFFF && checksum != 0 && complement != 0) {
if (checksum + complement == 0xFFFF && checksum != 0 && complement != 0)
{
score += 8;
}
uint32_t resetVectorAddr = addr + 0x7FFC;
uint32_t resetVector = _prgRom[resetVectorAddr] | (_prgRom[resetVectorAddr + 1] << 8);
if(resetVector < 0x8000) {
if (resetVector < 0x8000)
{
return -1;
}
uint8_t op = _prgRom[addr + (resetVector & 0x7FFF)];
if(op == 0x18 || op == 0x78 || op == 0x4C || op == 0x5C || op == 0x20 || op == 0x22 || op == 0x9C) {
if (op == 0x18 || op == 0x78 || op == 0x4C || op == 0x5C || op == 0x20 || op == 0x22 || op == 0x9C)
{
//CLI, SEI, JMP, JML, JSR, JSl, STZ
score += 8;
} else if(op == 0xC2 || op == 0xE2 || op == 0xA9 || op == 0xA2 || op == 0xA0) {
}
else if (op == 0xC2 || op == 0xE2 || op == 0xA9 || op == 0xA2 || op == 0xA0)
{
//REP, SEP, LDA, LDX, LDY
score += 4;
} else if(op == 0x00 || op == 0xFF || op == 0xCC) {
}
else if (op == 0x00 || op == 0xFF || op == 0xCC)
{
//BRK, SBC, CPY
score -= 8;
}
@ -166,9 +203,11 @@ void BaseCartridge::LoadRom()
bool hasHeader = false;
bool isLoRom = true;
bool isExRom = true;
for(uint32_t baseAddress : baseAddresses) {
for (uint32_t baseAddress : baseAddresses)
{
int32_t score = GetHeaderScore(baseAddress);
if(score >= 0 && score >= bestScore) {
if (score >= 0 && score >= bestScore)
{
bestScore = score;
isLoRom = (baseAddress & 0x8000) == 0;
isExRom = (baseAddress & 0x400000) != 0;
@ -180,44 +219,57 @@ void BaseCartridge::LoadRom()
}
uint32_t flags = 0;
if(isLoRom) {
if(hasHeader) {
if (isLoRom)
{
if (hasHeader)
{
flags |= CartFlags::CopierHeader;
}
flags |= CartFlags::LoRom;
} else {
if(hasHeader) {
}
else
{
if (hasHeader)
{
flags |= CartFlags::CopierHeader;
}
flags |= isExRom ? CartFlags::ExHiRom : CartFlags::HiRom;
}
if(flags & CartFlags::CopierHeader) {
if (flags & CartFlags::CopierHeader)
{
//Remove the copier header
memmove(_prgRom, _prgRom + 512, _prgRomSize - 512);
_prgRomSize -= 512;
_headerOffset -= 512;
}
if((flags & CartFlags::HiRom) && (_cartInfo.MapMode & 0x27) == 0x25) {
if ((flags & CartFlags::HiRom) && (_cartInfo.MapMode & 0x27) == 0x25)
{
flags |= CartFlags::ExHiRom;
} else if((flags & CartFlags::LoRom) && (_cartInfo.MapMode & 0x27) == 0x22) {
}
else if ((flags & CartFlags::LoRom) && (_cartInfo.MapMode & 0x27) == 0x22)
{
flags |= CartFlags::ExLoRom;
}
if(_cartInfo.MapMode & 0x10) {
if (_cartInfo.MapMode & 0x10)
{
flags |= CartFlags::FastRom;
}
_flags = (CartFlags::CartFlags)flags;
_hasBattery = (_cartInfo.RomType & 0x0F) == 0x02 || (_cartInfo.RomType & 0x0F) == 0x05 || (_cartInfo.RomType & 0x0F) == 0x06 || (_cartInfo.RomType & 0x0F) == 0x09 || (_cartInfo.RomType & 0x0F) == 0x0A;
_hasBattery = (_cartInfo.RomType & 0x0F) == 0x02 || (_cartInfo.RomType & 0x0F) == 0x05 || (_cartInfo.RomType & 0x0F)
== 0x06 || (_cartInfo.RomType & 0x0F) == 0x09 || (_cartInfo.RomType & 0x0F) == 0x0A;
_coprocessorType = GetCoprocessorType();
if(_coprocessorType != CoprocessorType::None && _cartInfo.ExpansionRamSize > 0 && _cartInfo.ExpansionRamSize <= 7) {
if (_coprocessorType != CoprocessorType::None && _cartInfo.ExpansionRamSize > 0 && _cartInfo.ExpansionRamSize <= 7)
{
_coprocessorRamSize = _cartInfo.ExpansionRamSize > 0 ? 1024 * (1 << _cartInfo.ExpansionRamSize) : 0;
}
if(_coprocessorType == CoprocessorType::GSU && _coprocessorRamSize == 0) {
if (_coprocessorType == CoprocessorType::GSU && _coprocessorRamSize == 0)
{
//Use a min of 64kb by default for GSU games
_coprocessorRamSize = 0x10000;
}
@ -236,8 +288,10 @@ void BaseCartridge::LoadRom()
CoprocessorType BaseCartridge::GetCoprocessorType()
{
if((_cartInfo.RomType & 0x0F) >= 0x03) {
switch((_cartInfo.RomType & 0xF0) >> 4) {
if ((_cartInfo.RomType & 0x0F) >= 0x03)
{
switch ((_cartInfo.RomType & 0xF0) >> 4)
{
case 0x00: return GetDspVersion();
case 0x01: return CoprocessorType::GSU;
case 0x02: return CoprocessorType::OBC1;
@ -245,7 +299,8 @@ CoprocessorType BaseCartridge::GetCoprocessorType()
case 0x04: return CoprocessorType::SDD1;
case 0x05: return CoprocessorType::RTC;
case 0x0E:
switch(_cartInfo.RomType) {
switch (_cartInfo.RomType)
{
case 0xE3: return CoprocessorType::SGB;
case 0xE5: return CoprocessorType::Satellaview;
default: return CoprocessorType::None;
@ -253,7 +308,8 @@ CoprocessorType BaseCartridge::GetCoprocessorType()
break;
case 0x0F:
switch(_cartInfo.CartridgeType) {
switch (_cartInfo.CartridgeType)
{
case 0x00:
_hasBattery = true;
_hasRtc = (_cartInfo.RomType & 0x0F) == 0x09;
@ -271,7 +327,9 @@ CoprocessorType BaseCartridge::GetCoprocessorType()
}
break;
}
} else if(GetGameCode() == "042J") {
}
else if (GetGameCode() == "042J")
{
return CoprocessorType::SGB;
}
@ -281,7 +339,8 @@ CoprocessorType BaseCartridge::GetCoprocessorType()
CoprocessorType BaseCartridge::GetSt01xVersion()
{
string cartName = GetCartName();
if(cartName == "2DAN MORITA SHOUGI") {
if (cartName == "2DAN MORITA SHOUGI")
{
return CoprocessorType::ST011;
}
@ -291,14 +350,21 @@ CoprocessorType BaseCartridge::GetSt01xVersion()
CoprocessorType BaseCartridge::GetDspVersion()
{
string cartName = GetCartName();
if(cartName == "DUNGEON MASTER") {
if (cartName == "DUNGEON MASTER")
{
return CoprocessorType::DSP2;
} if(cartName == "PILOTWINGS") {
}
if (cartName == "PILOTWINGS")
{
return CoprocessorType::DSP1;
} else if(cartName == "SD\xB6\xDE\xDD\xC0\xDE\xD1GX") {
}
else if (cartName == "SD\xB6\xDE\xDD\xC0\xDE\xD1GX")
{
//SD Gundam GX
return CoprocessorType::DSP3;
} else if(cartName == "PLANETS CHAMP TG3000" || cartName == "TOP GEAR 3000") {
}
else if (cartName == "PLANETS CHAMP TG3000" || cartName == "TOP GEAR 3000")
{
return CoprocessorType::DSP4;
}
@ -308,10 +374,12 @@ CoprocessorType BaseCartridge::GetDspVersion()
void BaseCartridge::Reset()
{
if(_coprocessor) {
if (_coprocessor)
{
_coprocessor->Reset();
}
if(_bsxMemPack) {
if (_bsxMemPack)
{
_bsxMemPack->Reset();
}
}
@ -330,30 +398,43 @@ RomInfo BaseCartridge::GetRomInfo()
vector<uint8_t> BaseCartridge::GetOriginalPrgRom()
{
RomInfo romInfo = GetRomInfo();
shared_ptr<BaseCartridge> originalCart = BaseCartridge::CreateCartridge(_console, romInfo.RomFile, romInfo.PatchFile);
if(originalCart->_gameboy) {
shared_ptr<BaseCartridge> originalCart =
BaseCartridge::CreateCartridge(_console, romInfo.RomFile, romInfo.PatchFile);
if (originalCart->_gameboy)
{
uint8_t* orgPrgRom = originalCart->_gameboy->DebugGetMemory(SnesMemoryType::GbPrgRom);
uint32_t orgRomSize = originalCart->_gameboy->DebugGetMemorySize(SnesMemoryType::GbPrgRom);
return vector<uint8_t>(orgPrgRom, orgPrgRom + orgRomSize);
} else {
return vector<uint8_t>(originalCart->DebugGetPrgRom(), originalCart->DebugGetPrgRom() + originalCart->DebugGetPrgRomSize());
}
else
{
return vector<uint8_t>(originalCart->DebugGetPrgRom(),
originalCart->DebugGetPrgRom() + originalCart->DebugGetPrgRomSize());
}
}
uint32_t BaseCartridge::GetCrc32()
{
if(_gameboy) {
return CRC32::GetCRC(_gameboy->DebugGetMemory(SnesMemoryType::GbPrgRom), _gameboy->DebugGetMemorySize(SnesMemoryType::GbPrgRom));
} else {
if (_gameboy)
{
return CRC32::GetCRC(_gameboy->DebugGetMemory(SnesMemoryType::GbPrgRom),
_gameboy->DebugGetMemorySize(SnesMemoryType::GbPrgRom));
}
else
{
return CRC32::GetCRC(_prgRom, _prgRomSize);
}
}
string BaseCartridge::GetSha1Hash()
{
if(_gameboy) {
return SHA1::GetHash(_gameboy->DebugGetMemory(SnesMemoryType::GbPrgRom), _gameboy->DebugGetMemorySize(SnesMemoryType::GbPrgRom));
} else {
if (_gameboy)
{
return SHA1::GetHash(_gameboy->DebugGetMemory(SnesMemoryType::GbPrgRom),
_gameboy->DebugGetMemorySize(SnesMemoryType::GbPrgRom));
}
else
{
return SHA1::GetHash(_prgRom, _prgRomSize);
}
}
@ -365,34 +446,41 @@ CartFlags::CartFlags BaseCartridge::GetCartFlags()
void BaseCartridge::LoadBattery()
{
if(_saveRamSize > 0) {
if (_saveRamSize > 0)
{
_console->GetBatteryManager()->LoadBattery(".srm", _saveRam, _saveRamSize);
}
if(_coprocessor && _hasBattery) {
if (_coprocessor && _hasBattery)
{
_coprocessor->LoadBattery();
}
if(_gameboy) {
if (_gameboy)
{
_gameboy->LoadBattery();
}
}
void BaseCartridge::SaveBattery()
{
if(_saveRamSize > 0) {
if (_saveRamSize > 0)
{
_console->GetBatteryManager()->SaveBattery(".srm", _saveRam, _saveRamSize);
}
if(_coprocessor && _hasBattery) {
if (_coprocessor && _hasBattery)
{
_coprocessor->SaveBattery();
}
if(_bsxMemPack) {
if (_bsxMemPack)
{
_bsxMemPack->SaveBattery();
}
if(_gameboy) {
if (_gameboy)
{
_gameboy->SaveBattery();
}
}
@ -402,31 +490,40 @@ void BaseCartridge::Init(MemoryMappings &mm)
_prgRomHandlers.clear();
_saveRamHandlers.clear();
for(uint32_t i = 0; i < _prgRomSize; i += 0x1000) {
_prgRomHandlers.push_back(unique_ptr<RomHandler>(new RomHandler(_prgRom, i, _prgRomSize, SnesMemoryType::PrgRom)));
for (uint32_t i = 0; i < _prgRomSize; i += 0x1000)
{
_prgRomHandlers.
push_back(unique_ptr<RomHandler>(new RomHandler(_prgRom, i, _prgRomSize, SnesMemoryType::PrgRom)));
}
uint32_t power = (uint32_t)std::log2(_prgRomSize);
if(_prgRomSize >(1u << power)) {
if (_prgRomSize > (1u << power))
{
//If size isn't a power of 2, mirror the part above the nearest (lower) power of 2 until the size reaches the next power of 2.
uint32_t halfSize = 1 << power;
uint32_t fullSize = 1 << (power + 1);
uint32_t extraHandlers = std::max<uint32_t>((_prgRomSize - halfSize) / 0x1000, 1);
while(_prgRomHandlers.size() < fullSize / 0x1000) {
for(uint32_t i = 0; i < extraHandlers; i += 0x1000) {
_prgRomHandlers.push_back(unique_ptr<RomHandler>(new RomHandler(_prgRom, halfSize + i, _prgRomSize, SnesMemoryType::PrgRom)));
while (_prgRomHandlers.size() < fullSize / 0x1000)
{
for (uint32_t i = 0; i < extraHandlers; i += 0x1000)
{
_prgRomHandlers.push_back(
unique_ptr<RomHandler>(new RomHandler(_prgRom, halfSize + i, _prgRomSize, SnesMemoryType::PrgRom)));
}
}
}
for(uint32_t i = 0; i < _saveRamSize; i += 0x1000) {
_saveRamHandlers.push_back(unique_ptr<RamHandler>(new RamHandler(_saveRam, i, _saveRamSize, SnesMemoryType::SaveRam)));
for (uint32_t i = 0; i < _saveRamSize; i += 0x1000)
{
_saveRamHandlers.push_back(
unique_ptr<RamHandler>(new RamHandler(_saveRam, i, _saveRamSize, SnesMemoryType::SaveRam)));
}
RegisterHandlers(mm);
if(_coprocessorType != CoprocessorType::Gameboy) {
if (_coprocessorType != CoprocessorType::Gameboy)
{
InitCoprocessor();
}
@ -435,27 +532,36 @@ void BaseCartridge::Init(MemoryMappings &mm)
void BaseCartridge::RegisterHandlers(MemoryMappings& mm)
{
if(MapSpecificCarts(mm) || _coprocessorType == CoprocessorType::GSU || _coprocessorType == CoprocessorType::SDD1 || _coprocessorType == CoprocessorType::SPC7110 || _coprocessorType == CoprocessorType::CX4) {
if (MapSpecificCarts(mm) || _coprocessorType == CoprocessorType::GSU || _coprocessorType == CoprocessorType::SDD1 ||
_coprocessorType == CoprocessorType::SPC7110 || _coprocessorType == CoprocessorType::CX4)
{
MapBsxMemoryPack(mm);
return;
}
if(_flags & CartFlags::LoRom) {
if (_flags & CartFlags::LoRom)
{
mm.RegisterHandler(0x00, 0x7D, 0x8000, 0xFFFF, _prgRomHandlers);
mm.RegisterHandler(0x80, 0xFF, 0x8000, 0xFFFF, _prgRomHandlers);
if(_saveRamSize > 0) {
if(_prgRomSize >= 1024 * 1024 * 2) {
if (_saveRamSize > 0)
{
if (_prgRomSize >= 1024 * 1024 * 2)
{
//For games >= 2mb in size, put ROM at 70-7D/F0-FF:0000-7FFF (e.g: Fire Emblem: Thracia 776)
mm.RegisterHandler(0x70, 0x7D, 0x0000, 0x7FFF, _saveRamHandlers);
mm.RegisterHandler(0xF0, 0xFF, 0x0000, 0x7FFF, _saveRamHandlers);
} else {
}
else
{
//For games < 2mb in size, put save RAM at 70-7D/F0-FF:0000-FFFF (e.g: Wanderers from Ys)
mm.RegisterHandler(0x70, 0x7D, 0x0000, 0xFFFF, _saveRamHandlers);
mm.RegisterHandler(0xF0, 0xFF, 0x0000, 0xFFFF, _saveRamHandlers);
}
}
} else if(_flags & CartFlags::HiRom) {
}
else if (_flags & CartFlags::HiRom)
{
mm.RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, _prgRomHandlers, 8);
mm.RegisterHandler(0x40, 0x7D, 0x0000, 0xFFFF, _prgRomHandlers, 0);
mm.RegisterHandler(0x80, 0xBF, 0x8000, 0xFFFF, _prgRomHandlers, 8);
@ -463,7 +569,9 @@ void BaseCartridge::RegisterHandlers(MemoryMappings &mm)
mm.RegisterHandler(0x20, 0x3F, 0x6000, 0x7FFF, _saveRamHandlers);
mm.RegisterHandler(0xA0, 0xBF, 0x6000, 0x7FFF, _saveRamHandlers);
} else if(_flags & CartFlags::ExHiRom) {
}
else if (_flags & CartFlags::ExHiRom)
{
//First half is at the end
mm.RegisterHandler(0xC0, 0xFF, 0x0000, 0xFFFF, _prgRomHandlers, 0);
mm.RegisterHandler(0x80, 0xBF, 0x8000, 0xFFFF, _prgRomHandlers, 8); //mirror
@ -484,11 +592,16 @@ void BaseCartridge::RegisterHandlers(MemoryMappings &mm)
void BaseCartridge::LoadEmbeddedFirmware()
{
//Attempt to detect/load the firmware from the end of the rom file, if it exists
if((_coprocessorType >= CoprocessorType::DSP1 && _coprocessorType <= CoprocessorType::DSP4) || (_coprocessorType >= CoprocessorType::ST010 && _coprocessorType <= CoprocessorType::ST011)) {
if ((_coprocessorType >= CoprocessorType::DSP1 && _coprocessorType <= CoprocessorType::DSP4) || (_coprocessorType >=
CoprocessorType::ST010 && _coprocessorType <= CoprocessorType::ST011))
{
uint32_t firmwareSize = 0;
if((_prgRomSize & 0x7FFF) == 0x2000) {
if ((_prgRomSize & 0x7FFF) == 0x2000)
{
firmwareSize = 0x2000;
} else if((_prgRomSize & 0xFFFF) == 0xD000) {
}
else if ((_prgRomSize & 0xFFFF) == 0xD000)
{
firmwareSize = 0xD000;
}
@ -503,23 +616,33 @@ void BaseCartridge::InitCoprocessor()
_coprocessor.reset(NecDsp::InitCoprocessor(_coprocessorType, _console, _embeddedFirmware));
_necDsp = dynamic_cast<NecDsp*>(_coprocessor.get());
if(_coprocessorType == CoprocessorType::SA1) {
if (_coprocessorType == CoprocessorType::SA1)
{
_coprocessor.reset(new Sa1(_console));
_sa1 = dynamic_cast<Sa1*>(_coprocessor.get());
_needCoprocSync = true;
} else if(_coprocessorType == CoprocessorType::GSU) {
}
else if (_coprocessorType == CoprocessorType::GSU)
{
_coprocessor.reset(new Gsu(_console, _coprocessorRamSize));
_gsu = dynamic_cast<Gsu*>(_coprocessor.get());
_needCoprocSync = true;
} else if(_coprocessorType == CoprocessorType::SDD1) {
}
else if (_coprocessorType == CoprocessorType::SDD1)
{
_coprocessor.reset(new Sdd1(_console));
} else if(_coprocessorType == CoprocessorType::SPC7110) {
}
else if (_coprocessorType == CoprocessorType::SPC7110)
{
_coprocessor.reset(new Spc7110(_console, _hasRtc));
} else if(_coprocessorType == CoprocessorType::Satellaview) {
}
else if (_coprocessorType == CoprocessorType::Satellaview)
{
//Share save file across all .bs files that use the BS-X bios
_console->GetBatteryManager()->Initialize("BsxBios");
if(!_bsxMemPack) {
if (!_bsxMemPack)
{
//Create an empty memory pack if the BIOS was loaded directly (instead of a .bs file)
vector<uint8_t> emptyMemPack;
_bsxMemPack.reset(new BsxMemoryPack(_console, emptyMemPack, false));
@ -527,13 +650,19 @@ void BaseCartridge::InitCoprocessor()
_coprocessor.reset(new BsxCart(_console, _bsxMemPack.get()));
_bsx = dynamic_cast<BsxCart*>(_coprocessor.get());
} else if(_coprocessorType == CoprocessorType::CX4) {
}
else if (_coprocessorType == CoprocessorType::CX4)
{
_coprocessor.reset(new Cx4(_console));
_cx4 = dynamic_cast<Cx4*>(_coprocessor.get());
_needCoprocSync = true;
} else if(_coprocessorType == CoprocessorType::OBC1 && _saveRamSize > 0) {
}
else if (_coprocessorType == CoprocessorType::OBC1 && _saveRamSize > 0)
{
_coprocessor.reset(new Obc1(_console, _saveRam, _saveRamSize));
} else if(_coprocessorType == CoprocessorType::SGB) {
}
else if (_coprocessorType == CoprocessorType::SGB)
{
_coprocessor.reset(new SuperGameboy(_console));
_sgb = dynamic_cast<SuperGameboy*>(_coprocessor.get());
_needCoprocSync = true;
@ -544,7 +673,8 @@ bool BaseCartridge::MapSpecificCarts(MemoryMappings &mm)
{
string name = GetCartName();
string code = GetGameCode();
if(GetCartName() == "DEZAEMON") {
if (GetCartName() == "DEZAEMON")
{
//LOROM with mirrored SRAM?
mm.RegisterHandler(0x00, 0x7D, 0x8000, 0xFFFF, _prgRomHandlers);
mm.RegisterHandler(0x80, 0xFF, 0x8000, 0xFFFF, _prgRomHandlers);
@ -557,13 +687,16 @@ bool BaseCartridge::MapSpecificCarts(MemoryMappings &mm)
mm.RegisterHandler(0xF0, 0xFF, 0x0000, 0x7FFF, _saveRamHandlers);
return true;
} else if(code == "ZDBJ" || code == "ZR2J" || code == "ZSNJ") {
}
else if (code == "ZDBJ" || code == "ZR2J" || code == "ZSNJ")
{
//BSC-1A5M-02, BSC-1A7M-01
//Games: Sound Novel Tsukuuru, RPG Tsukuuru, Derby Stallion 96
mm.RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, _prgRomHandlers);
mm.RegisterHandler(0x80, 0x9F, 0x8000, 0xFFFF, _prgRomHandlers, 0, 0x200);
mm.RegisterHandler(0xA0, 0xBF, 0x8000, 0xFFFF, _prgRomHandlers, 0, 0x100);
if(_saveRamSize > 0) {
if (_saveRamSize > 0)
{
mm.RegisterHandler(0x70, 0x7D, 0x0000, 0x7FFF, _saveRamHandlers);
mm.RegisterHandler(0xF0, 0xFF, 0x0000, 0x7FFF, _saveRamHandlers);
}
@ -575,19 +708,24 @@ bool BaseCartridge::MapSpecificCarts(MemoryMappings &mm)
void BaseCartridge::MapBsxMemoryPack(MemoryMappings& mm)
{
string code = GetGameCode();
if(!_bsxMemPack && code.size() == 4 && code[0] == 'Z' && _cartInfo.DeveloperId == 0x33) {
if (!_bsxMemPack && code.size() == 4 && code[0] == 'Z' && _cartInfo.DeveloperId == 0x33)
{
//Game with data pack slot (e.g Sound Novel Tsukuuru, etc.)
vector<uint8_t> saveData = _console->GetBatteryManager()->LoadBattery(".bs");
if(saveData.empty()) {
if (saveData.empty())
{
//Make a 1 megabyte flash cartridge by default (use $FF for all bytes)
saveData.resize(0x100000, 0xFF);
}
_bsxMemPack.reset(new BsxMemoryPack(_console, saveData, true));
if(_flags & CartFlags::LoRom) {
if (_flags & CartFlags::LoRom)
{
mm.RegisterHandler(0xC0, 0xEF, 0x0000, 0x7FFF, _bsxMemPack->GetMemoryHandlers());
mm.RegisterHandler(0xC0, 0xEF, 0x8000, 0xFFFF, _bsxMemPack->GetMemoryHandlers());
} else {
}
else
{
mm.RegisterHandler(0x20, 0x3F, 0x8000, 0xFFFF, _bsxMemPack->GetMemoryHandlers(), 8);
mm.RegisterHandler(0x60, 0x7D, 0x0000, 0xFFFF, _bsxMemPack->GetMemoryHandlers());
mm.RegisterHandler(0xA0, 0xBF, 0x8000, 0xFFFF, _bsxMemPack->GetMemoryHandlers(), 8);
@ -601,12 +739,15 @@ void BaseCartridge::MapBsxMemoryPack(MemoryMappings& mm)
void BaseCartridge::ApplyConfigOverrides()
{
string name = GetCartName();
if(name == "POWERDRIVE" || name == "DEATH BRADE" || name == "RPG SAILORMOON") {
if (name == "POWERDRIVE" || name == "DEATH BRADE" || name == "RPG SAILORMOON")
{
//These games work better when ram is initialized to $FF
EmulationConfig cfg = _console->GetSettings()->GetEmulationConfig();
cfg.RamPowerOnState = RamState::AllOnes;
_console->GetSettings()->SetEmulationConfig(cfg);
} else if(name == "SUPER KEIBA 2") {
}
else if (name == "SUPER KEIBA 2")
{
//Super Keiba 2 behaves incorrectly if save ram is filled with 0s
EmulationConfig cfg = _console->GetSettings()->GetEmulationConfig();
cfg.RamPowerOnState = RamState::Random;
@ -623,26 +764,34 @@ void BaseCartridge::LoadSpc()
bool BaseCartridge::LoadGameboy(VirtualFile& romFile, bool sgbEnabled)
{
_gameboy.reset(Gameboy::Create(_console, romFile, sgbEnabled));
if(!_gameboy) {
if (!_gameboy)
{
return false;
}
_cartInfo = {};
_headerOffset = Gameboy::HeaderOffset;
if(_gameboy->IsSgb()) {
if (_gameboy->IsSgb())
{
GameboyConfig cfg = _console->GetSettings()->GetGameboyConfig();
if(FirmwareHelper::LoadSgbFirmware(_console, &_prgRom, _prgRomSize, cfg.UseSgb2)) {
if (FirmwareHelper::LoadSgbFirmware(_console, &_prgRom, _prgRomSize, cfg.UseSgb2))
{
LoadRom();
if(_coprocessorType != CoprocessorType::SGB) {
if (_coprocessorType != CoprocessorType::SGB)
{
//SGB bios file isn't a recognized SGB bios, try again without SGB mode
return LoadGameboy(romFile, false);
}
} else {
}
else
{
//Couldn't load the SGB bios, try again with in GB/GBC mode
return LoadGameboy(romFile, false);
}
} else {
}
else
{
_coprocessorType = CoprocessorType::Gameboy;
SetupCpuHalt();
}
@ -673,13 +822,16 @@ void BaseCartridge::SetupCpuHalt()
void BaseCartridge::Serialize(Serializer& s)
{
s.StreamArray(_saveRam, _saveRamSize);
if(_coprocessor) {
if (_coprocessor)
{
s.Stream(_coprocessor.get());
}
if(_bsxMemPack) {
if (_bsxMemPack)
{
s.Stream(_bsxMemPack.get());
}
if(_gameboy) {
if (_gameboy)
{
s.Stream(_gameboy.get());
}
}
@ -687,16 +839,20 @@ void BaseCartridge::Serialize(Serializer &s)
string BaseCartridge::GetGameCode()
{
string code;
if(_cartInfo.GameCode[0] > ' ') {
if (_cartInfo.GameCode[0] > ' ')
{
code += _cartInfo.GameCode[0];
}
if(_cartInfo.GameCode[1] > ' ') {
if (_cartInfo.GameCode[1] > ' ')
{
code += _cartInfo.GameCode[1];
}
if(_cartInfo.GameCode[2] > ' ') {
if (_cartInfo.GameCode[2] > ' ')
{
code += _cartInfo.GameCode[2];
}
if(_cartInfo.GameCode[3] > ' ') {
if (_cartInfo.GameCode[3] > ' ')
{
code += _cartInfo.GameCode[3];
}
return code;
@ -705,8 +861,10 @@ string BaseCartridge::GetGameCode()
string BaseCartridge::GetCartName()
{
int nameLength = 21;
for(int i = 0; i < 21; i++) {
if(_cartInfo.CartName[i] == 0) {
for (int i = 0; i < 21; i++)
{
if (_cartInfo.CartName[i] == 0)
{
nameLength = i;
break;
}
@ -714,9 +872,12 @@ string BaseCartridge::GetCartName()
string name = string(_cartInfo.CartName, nameLength);
size_t lastNonSpace = name.find_last_not_of(' ');
if(lastNonSpace != string::npos) {
if (lastNonSpace != string::npos)
{
return name.substr(0, lastNonSpace + 1);
} else {
}
else
{
return name;
}
}
@ -724,7 +885,8 @@ string BaseCartridge::GetCartName()
ConsoleRegion BaseCartridge::GetRegion()
{
uint8_t destCode = _cartInfo.DestinationCode;
if((destCode >= 0x02 && destCode <= 0x0C) || destCode == 0x11 || destCode == 0x12) {
if ((destCode >= 0x02 && destCode <= 0x0C) || destCode == 0x11 || destCode == 0x12)
{
return ConsoleRegion::Pal;
}
return ConsoleRegion::Ntsc;
@ -736,50 +898,81 @@ void BaseCartridge::DisplayCartInfo()
MessageManager::Log("File: " + VirtualFile(_romPath).GetFileName());
MessageManager::Log("Game: " + GetCartName());
string gameCode = GetGameCode();
if(!gameCode.empty()) {
if (!gameCode.empty())
{
MessageManager::Log("Game code: " + gameCode);
}
if(_flags & CartFlags::ExHiRom) {
if (_flags & CartFlags::ExHiRom)
{
MessageManager::Log("Type: ExHiROM");
} else if(_flags & CartFlags::ExLoRom) {
}
else if (_flags & CartFlags::ExLoRom)
{
MessageManager::Log("Type: ExLoROM");
} else if(_flags & CartFlags::HiRom) {
}
else if (_flags & CartFlags::HiRom)
{
MessageManager::Log("Type: HiROM");
} else if(_flags & CartFlags::LoRom) {
}
else if (_flags & CartFlags::LoRom)
{
MessageManager::Log("Type: LoROM");
}
if(_coprocessorType != CoprocessorType::None) {
if (_coprocessorType != CoprocessorType::None)
{
string coProcMessage = "Coprocessor: ";
switch(_coprocessorType) {
case CoprocessorType::None: coProcMessage += "<none>"; break;
case CoprocessorType::CX4: coProcMessage += "CX4"; break;
case CoprocessorType::SDD1: coProcMessage += "S-DD1"; break;
case CoprocessorType::DSP1: coProcMessage += "DSP1"; break;
case CoprocessorType::DSP1B: coProcMessage += "DSP1B"; break;
case CoprocessorType::DSP2: coProcMessage += "DSP2"; break;
case CoprocessorType::DSP3: coProcMessage += "DSP3"; break;
case CoprocessorType::DSP4: coProcMessage += "DSP4"; break;
case CoprocessorType::GSU: coProcMessage += "Super FX (GSU1/2)"; break;
case CoprocessorType::OBC1: coProcMessage += "OBC1"; break;
case CoprocessorType::RTC: coProcMessage += "RTC"; break;
case CoprocessorType::SA1: coProcMessage += "SA1"; break;
case CoprocessorType::Satellaview: coProcMessage += "Satellaview"; break;
case CoprocessorType::SPC7110: coProcMessage += "SPC7110"; break;
case CoprocessorType::ST010: coProcMessage += "ST010"; break;
case CoprocessorType::ST011: coProcMessage += "ST011"; break;
case CoprocessorType::ST018: coProcMessage += "ST018"; break;
case CoprocessorType::Gameboy: coProcMessage += "Game Boy"; break;
case CoprocessorType::SGB: coProcMessage += "Super Game Boy"; break;
switch (_coprocessorType)
{
case CoprocessorType::None: coProcMessage += "<none>";
break;
case CoprocessorType::CX4: coProcMessage += "CX4";
break;
case CoprocessorType::SDD1: coProcMessage += "S-DD1";
break;
case CoprocessorType::DSP1: coProcMessage += "DSP1";
break;
case CoprocessorType::DSP1B: coProcMessage += "DSP1B";
break;
case CoprocessorType::DSP2: coProcMessage += "DSP2";
break;
case CoprocessorType::DSP3: coProcMessage += "DSP3";
break;
case CoprocessorType::DSP4: coProcMessage += "DSP4";
break;
case CoprocessorType::GSU: coProcMessage += "Super FX (GSU1/2)";
break;
case CoprocessorType::OBC1: coProcMessage += "OBC1";
break;
case CoprocessorType::RTC: coProcMessage += "RTC";
break;
case CoprocessorType::SA1: coProcMessage += "SA1";
break;
case CoprocessorType::Satellaview: coProcMessage += "Satellaview";
break;
case CoprocessorType::SPC7110: coProcMessage += "SPC7110";
break;
case CoprocessorType::ST010: coProcMessage += "ST010";
break;
case CoprocessorType::ST011: coProcMessage += "ST011";
break;
case CoprocessorType::ST018: coProcMessage += "ST018";
break;
case CoprocessorType::Gameboy: coProcMessage += "Game Boy";
break;
case CoprocessorType::SGB: coProcMessage += "Super Game Boy";
break;
}
MessageManager::Log(coProcMessage);
}
if(_flags & CartFlags::FastRom) {
if (_flags & CartFlags::FastRom)
{
MessageManager::Log("FastROM");
}
if(_flags & CartFlags::CopierHeader) {
if (_flags & CartFlags::CopierHeader)
{
MessageManager::Log("Copier header found.");
}
@ -788,13 +981,17 @@ void BaseCartridge::DisplayCartInfo()
MessageManager::Log("File size: " + std::to_string(_prgRomSize / 1024) + " KB");
MessageManager::Log("ROM size: " + std::to_string((0x400 << _cartInfo.RomSize) / 1024) + " KB");
if(_saveRamSize > 0) {
MessageManager::Log("SRAM size: " + std::to_string(_saveRamSize / 1024) + " KB" + (_hasBattery ? " (with battery)" : ""));
if (_saveRamSize > 0)
{
MessageManager::Log(
"SRAM size: " + std::to_string(_saveRamSize / 1024) + " KB" + (_hasBattery ? " (with battery)" : ""));
}
if(_coprocessorRamSize > 0) {
if (_coprocessorRamSize > 0)
{
MessageManager::Log("Coprocessor RAM size: " + std::to_string(_coprocessorRamSize / 1024) + " KB");
}
if(_hasBattery) {
if (_hasBattery)
{
MessageManager::Log("Battery: yes");
}
MessageManager::Log("-----------------------------");
@ -843,7 +1040,8 @@ Gameboy* BaseCartridge::GetGameboy()
void BaseCartridge::RunCoprocessors()
{
//These coprocessors are run at the end of the frame, or as needed
if(_necDsp) {
if (_necDsp)
{
_necDsp->Run();
}
}

View file

@ -120,7 +120,8 @@ public:
__forceinline void SyncCoprocessors()
{
if(_needCoprocSync) {
if (_needCoprocSync)
{
_coprocessor->Run();
}
}

View file

@ -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);
}
}
@ -262,7 +292,8 @@ MouseMovement BaseControlDevice::GetMovement()
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,10 +301,12 @@ 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);
}
}

View file

@ -20,7 +20,9 @@ protected:
uint8_t _port;
SimpleLock _stateLock;
virtual void RefreshStateBuffer() { }
virtual void RefreshStateBuffer()
{
}
void EnsureCapacity(int32_t minBitCount);
uint32_t GetByteIndex(uint8_t bit);
@ -69,7 +71,10 @@ public:
string GetTextState();
void SetStateFromInput();
virtual void OnAfterSetState() { }
virtual void OnAfterSetState()
{
}
void SetRawState(ControlDeviceState state);
ControlDeviceState GetRawState();
@ -78,7 +83,8 @@ public:
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 static SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2,
uint8_t button2);
void Serialize(Serializer& s) override;
};

View file

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

View file

@ -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,10 +38,14 @@ 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++;
@ -54,17 +59,23 @@ std::wstring BaseRenderer::WrapText(string utf8Text, float maxLineWidth, uint32_
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++;
@ -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++);
}
}

View file

@ -38,7 +38,8 @@ protected:
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;
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);

View file

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

View file

@ -37,7 +37,8 @@ FrameInfo BaseVideoFilter::GetFrameInfo()
void BaseVideoFilter::UpdateBufferSize()
{
uint32_t newBufferSize = GetFrameInfo().Width * GetFrameInfo().Height;
if(_bufferSize != newBufferSize) {
if (_bufferSize != newBufferSize)
{
_frameLock.Acquire();
delete[] _outputBuffer;
_bufferSize = newBufferSize;
@ -98,7 +99,8 @@ void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename
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));
}

View file

@ -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);
}
}
@ -51,19 +53,25 @@ 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);
}

View file

@ -5,20 +5,33 @@
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,7 +41,8 @@ bool Breakpoint::Matches(uint32_t memoryAddr, AddressInfo &info)
bool Breakpoint::HasBreakpointType(BreakpointType bpType)
{
switch(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;

View file

@ -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,24 +27,31 @@ void BreakpointManager::SetBreakpoints(Breakpoint breakpoints[], uint32_t count)
_bpExpEval.reset(new ExpressionEvaluator(_debugger, _cpuType));
for(uint32_t j = 0; j < count; j++) {
for (uint32_t j = 0; j < count; j++)
{
Breakpoint& bp = breakpoints[j];
for(int i = 0; i < BreakpointManager::BreakpointTypeCount; i++) {
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()));
}
@ -60,27 +68,35 @@ 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) {
switch (type)
{
default:
case MemoryOperationType::ExecOperand:
case MemoryOperationType::ExecOpCode:
@ -100,7 +116,8 @@ int BreakpointManager::InternalCheckBreakpoint(MemoryOperationInfo operationInfo
{
BreakpointType type = GetBreakpointType(operationInfo.Type);
if(!_hasBreakpointType[(int)type]) {
if (!_hasBreakpointType[(int)type])
{
return -1;
}
@ -108,13 +125,19 @@ int BreakpointManager::InternalCheckBreakpoint(MemoryOperationInfo operationInfo
_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()) {
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;
}
}

View file

@ -39,7 +39,8 @@ public:
__forceinline int BreakpointManager::CheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo& address)
{
if(!_hasBreakpoint) {
if (!_hasBreakpoint)
{
return -1;
}
return InternalCheckBreakpoint(operationInfo, address);

View file

@ -28,8 +28,10 @@ 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();
@ -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,26 +114,32 @@ 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
@ -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;
}
@ -190,7 +218,8 @@ void BsxCart::Serialize(Serializer& s)
s.Stream(psRam, regs, dirtyRegs, _dirty);
s.Stream(_satellaview.get());
if(!s.IsSaving()) {
if (!s.IsSaving())
{
UpdateMemoryMappings();
}
}

View file

@ -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);
}
}
@ -36,19 +38,23 @@ 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};
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};
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,7 +66,8 @@ void BsxMemoryPack::ProcessCommand(uint8_t value, uint32_t page)
{
_command = (_command << 8) | value;
switch(value) {
switch (value)
{
case 0x00:
case 0xFF:
_enableCsr = false;
@ -73,14 +80,20 @@ void BsxMemoryPack::ProcessCommand(uint8_t value, uint32_t page)
_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,21 +130,26 @@ BsxMemoryPack::BsxMemoryPackHandler::BsxMemoryPackHandler(BsxMemoryPack* memPack
uint8_t BsxMemoryPack::BsxMemoryPackHandler::Read(uint32_t addr)
{
if(_offset == 0 && _memPack->_enableEsr) {
switch(addr & 0xFFF) {
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) {
switch (addr & 0xFF)
{
case 0x00: return 0x4d;
case 0x01: return 0x00;
case 0x02: return 0x50;
@ -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);
}
}

View file

@ -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,11 +38,13 @@ 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) {
switch (addr)
{
case 0x2188: return _stream[0].GetChannel() & 0xFF;
case 0x2189: return (_stream[0].GetChannel()) >> 8;
case 0x218A: return _stream[0].GetPrefixCount();
@ -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();
}
}

View file

@ -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,11 +257,13 @@ void BsxStream::InitTimeStruct()
uint8_t BsxStream::GetTime()
{
if(_fileOffset == 0) {
if (_fileOffset == 0)
{
InitTimeStruct();
}
switch(_fileOffset) {
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)
@ -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);
}

View file

@ -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,9 +68,11 @@ 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);
}
}
}
@ -72,7 +81,8 @@ void CallstackManager::GetCallstack(StackFrameInfo* callstackArray, uint32_t &ca
{
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;

View file

@ -16,7 +16,8 @@ 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);

View file

@ -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
@ -91,13 +101,17 @@ void CheatManager::AddStringCheat(string code)
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;
}

View file

@ -36,9 +36,11 @@ public:
__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;
}
}

View file

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

View file

@ -30,23 +30,31 @@ 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);
}
@ -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,11 +169,16 @@ 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;
}
@ -160,7 +187,8 @@ CpuType CodeDataLogger::GetCpuType(uint32_t absoluteAddr)
void CodeDataLogger::SetCdlData(uint8_t* cdlData, uint32_t length)
{
if(length <= _prgSize) {
if (length <= _prgSize)
{
memcpy(_cdlData, cdlData, length);
}
}
@ -177,22 +205,30 @@ 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) {
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) {
}
else if (flag == CdlStripOption::StripUsed)
{
for (uint32_t i = 0; i < _prgSize; i++)
{
if (_cdlData[i] != 0)
{
romBuffer[i] = 0;
}
}

View file

@ -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,7 +116,8 @@ void Console::RunFrame()
void Console::Run()
{
if(!_cpu) {
if (!_cpu)
{
return;
}
@ -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();
@ -206,7 +225,8 @@ void Console::RunFrameWithRunAhead()
_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,14 +379,18 @@ 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);
}
@ -366,9 +402,11 @@ void Console::Reset()
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,17 +424,21 @@ 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);
}
@ -406,7 +448,8 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
_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();
}
@ -463,19 +513,24 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
_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) {
if (stopRom)
{
#ifndef LIBRETRO
_emuThread.reset(new thread(&Console::Run, this));
#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;
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) {
}
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;
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;
@ -714,7 +824,8 @@ 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,10 +834,13 @@ 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());
}
@ -738,7 +852,8 @@ void Console::Deserialize(istream &in, uint32_t fileFormatVersion, bool compress
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,10 +1028,13 @@ 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;
}
@ -920,25 +1043,29 @@ uint32_t Console::GetFrameCount()
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);
}
}

View file

@ -181,63 +181,75 @@ public:
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);
};

View file

@ -59,11 +59,15 @@ 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) {
if (device)
{
states.push_back({device->GetControllerType(), device->GetRawState()});
} else {
}
else
{
states.push_back({ControllerType::None, ControlDeviceState()});
}
}
@ -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,18 +110,26 @@ 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) {
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;
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;
@ -121,15 +138,18 @@ shared_ptr<BaseControlDevice> ControlManager::CreateControllerDevice(ControllerT
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,7 +223,8 @@ 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);
}
}
@ -204,13 +232,16 @@ void ControlManager::Write(uint16_t addr, uint8_t value)
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());
}
}

View file

@ -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;
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,7 +43,8 @@ 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;
@ -46,24 +55,31 @@ void Cpu::Add16(uint16_t value)
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;
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,7 +140,8 @@ 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;
@ -124,24 +152,31 @@ void Cpu::Sub16(uint16_t value)
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;
@ -330,12 +371,15 @@ void Cpu::IncDecReg(uint16_t &reg, int8_t offset)
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,9 +960,12 @@ 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);
}
}
@ -854,9 +973,12 @@ void Cpu::PushRegister(uint16_t reg, 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);
}
}
@ -866,18 +988,24 @@ Store/load operations
**********************/
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();
}
@ -1233,7 +1387,8 @@ 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();
}
}

File diff suppressed because it is too large Load diff

View file

@ -26,8 +26,10 @@ void Cpu::Exec()
{
_immediateMode = false;
switch(_state.StopState) {
case CpuStopState::Running: RunOp(); break;
switch (_state.StopState)
{
case CpuStopState::Running: RunOp();
break;
case CpuStopState::Stopped:
//STP was executed, CPU no longer executes any code
#ifndef DUMMYCPU
@ -38,7 +40,8 @@ void Cpu::Exec()
case CpuStopState::WaitingForIrq:
//WAI
Idle();
if(_state.IrqSource || _state.NeedNmi) {
if (_state.IrqSource || _state.NeedNmi)
{
Idle();
Idle();
_state.StopState = CpuStopState::Running;
@ -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);
}

View file

@ -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();
@ -229,7 +233,8 @@ private:
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();

View file

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

View file

@ -37,13 +37,15 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
_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;
}
@ -63,22 +65,30 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
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)) {
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;
}
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) {
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 {
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();
}
}

View file

@ -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,55 +24,93 @@ 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) {
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;
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::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:
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::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::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::StkRel: str.WriteAll(operand, ",S");
break;
case AddrMode::StkRelIndIdxY: str.WriteAll('(', operand, ",S),Y");
break;
default: throw std::runtime_error("invalid address mode");
}
@ -84,19 +123,28 @@ uint32_t CpuDisUtils::GetOperandAddress(DisassemblyInfo &info, uint32_t memoryAd
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);
}
}
@ -106,7 +154,8 @@ uint32_t CpuDisUtils::GetOperandAddress(DisassemblyInfo &info, uint32_t memoryAd
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,7 +168,8 @@ int32_t CpuDisUtils::GetEffectiveAddress(DisassemblyInfo &info, Console *console
bool CpuDisUtils::HasEffectiveAddress(AddrMode addrMode)
{
switch(addrMode) {
switch (addrMode)
{
case AddrMode::Acc:
case AddrMode::Imp:
case AddrMode::Stk:
@ -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;
}
@ -205,20 +258,36 @@ string CpuDisUtils::OpName[256] = {
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
};

View file

@ -22,7 +22,8 @@ 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);
};
@ -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,

View file

@ -88,4 +88,3 @@ enum class IrqSource
Ppu = 1,
Coprocessor = 2
};

View file

@ -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,7 +166,8 @@ void Cx4::Exec(uint16_t opCode)
uint32_t Cx4::GetSourceValue(uint8_t src)
{
switch(src & 0x7F) {
switch (src & 0x7F)
{
case 0x00: return _state.A;
case 0x01: return (_state.Mult >> 24) & 0xFFFFFF;
case 0x02: return _state.Mult & 0xFFFFFF;
@ -144,22 +210,38 @@ uint32_t Cx4::GetSourceValue(uint8_t src)
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;
@ -168,21 +250,30 @@ uint32_t Cx4::GetSourceValue(uint8_t src)
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;
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 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;
@ -198,22 +289,54 @@ void Cx4::WriteRegister(uint8_t reg, uint32_t value)
_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 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) {
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;
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,9 +770,12 @@ 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;
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,9 +789,12 @@ 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;
switch (src)
{
case 0: WriteRegister(dst, _state.A);
break;
case 1: WriteRegister(dst, _state.MemoryDataReg);
break;
default: break; //nop
}
}
@ -632,7 +802,8 @@ void Cx4::Store(uint8_t src, uint8_t dst)
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
};

View file

@ -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,13 +154,15 @@ 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;
}
@ -142,7 +170,8 @@ void Cx4::SwitchCachePage()
_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,18 +192,21 @@ 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;
@ -182,7 +216,8 @@ bool Cx4::ProcessCache(uint64_t targetCycle)
}
//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) {
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,23 +324,33 @@ 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& reg = _state.Regs[addr / 3];
switch(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) {
switch (addr)
{
case 0x7F40: return _state.Dma.Source;
case 0x7F41: return _state.Dma.Source >> 8;
case 0x7F42: return _state.Dma.Source >> 16;
@ -322,62 +379,90 @@ 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& 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;
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;
}
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) {
if (_state.Stopped)
{
_state.Dma.Enabled = true;
}
break;
case 0x7F48:
_state.Cache.Page = value & 0x01;
if(_state.Stopped) {
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 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) {
if (_state.Stopped)
{
_state.Stopped = false;
_state.PB = _state.Cache.ProgramBank;
_state.PC = _state.Cache.ProgramCounter;
@ -391,20 +476,23 @@ void Cx4::Write(uint32_t addr, uint8_t value)
case 0x7F51:
_state.IrqDisabled = value & 0x01;
if(_state.IrqDisabled) {
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 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
@ -429,10 +517,14 @@ 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
);
@ -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;
}
break;
case Cx4Register::Cx4RegPC:
{
_state.PC = value & 0xFF;
} break;
}
break;
case Cx4Register::Cx4RegA:
{
_state.A = value & 0xFFFFFF; // 24-bit
} break;
}
break;
case Cx4Register::Cx4RegP:
{
_state.P = value & 0xFFFF;
} break;
}
break;
case Cx4Register::Cx4RegSP:
{
_state.SP = value & 0xFF;
} break;
}
break;
}
}

View file

@ -44,17 +44,21 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
AddressInfo addressInfo = _cx4->GetMemoryMappings()->GetAbsoluteAddress(addr);
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());
@ -98,8 +106,10 @@ 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:

View file

@ -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;
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;
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 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;
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');
break;
case 0x28: str.Write("JSR "); writeBranchTarget(); break;
case 0x2C: str.Write("JEQ "); writeBranchTarget(); 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 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 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 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) {
if (param1 == 1)
{
str.Write("SXB");
} else if(param1 == 2) {
}
else if (param1 == 2)
{
str.Write("SXW");
} else {
}
else
{
str.Write("???");
}
break;
case 0x5C: 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 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 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 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) {
if (param1 <= 1)
{
str.WriteAll("LD P", param1 ? "H" : "L", ",#$", HexUtilities::ToHex(param2));
} else {
}
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 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 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 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 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 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 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 {
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 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;
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();

View file

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

View file

@ -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,7 +33,8 @@ public:
~DebugBreakHelper()
{
if(!_isEmulationThread) {
if (!_isEmulationThread)
{
_debugger->BreakRequest(true);
}
}

View file

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

View file

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

View file

@ -7,7 +7,8 @@ class DebugUtilities
public:
static SnesMemoryType GetCpuMemoryType(CpuType type)
{
switch(type) {
switch (type)
{
case CpuType::Cpu: return SnesMemoryType::CpuMemory;
case CpuType::Spc: return SnesMemoryType::SpcMemory;
case CpuType::NecDsp: return SnesMemoryType::NecDspMemory;
@ -22,7 +23,8 @@ public:
static CpuType ToCpuType(SnesMemoryType type)
{
switch(type) {
switch (type)
{
case SnesMemoryType::SpcMemory:
case SnesMemoryType::SpcRam:
case SnesMemoryType::SpcRom:
@ -69,7 +71,8 @@ public:
static bool IsPpuMemory(SnesMemoryType memType)
{
switch(memType) {
switch (memType)
{
case SnesMemoryType::VideoRam:
case SnesMemoryType::SpriteRam:
case SnesMemoryType::CGRam:
@ -84,7 +87,8 @@ public:
static bool IsRomMemory(SnesMemoryType memType)
{
switch(memType) {
switch (memType)
{
case SnesMemoryType::PrgRom:
case SnesMemoryType::GbPrgRom:
case SnesMemoryType::GbBootRom:

View file

@ -80,18 +80,26 @@ Debugger::Debugger(shared_ptr<Console> console)
_ppuTools.reset(new PpuTools(_console.get(), _ppu.get()));
_scriptManager.reset(new ScriptManager(this));
if(_gameboy) {
if (_gameboy)
{
_gbDebugger.reset(new GbDebugger(this));
}
_cpuDebugger.reset(new CpuDebugger(this, CpuType::Cpu));
_spcDebugger.reset(new SpcDebugger(this));
if(_cart->GetSa1()) {
if (_cart->GetSa1())
{
_sa1Debugger.reset(new CpuDebugger(this, CpuType::Sa1));
} else if(_cart->GetGsu()) {
}
else if (_cart->GetGsu())
{
_gsuDebugger.reset(new GsuDebugger(this));
} else if(_cart->GetDsp()) {
}
else if (_cart->GetDsp())
{
_necDspDebugger.reset(new NecDspDebugger(this));
} else if(_cart->GetCx4()) {
}
else if (_cart->GetCx4())
{
_cx4Debugger.reset(new Cx4Debugger(this));
}
@ -100,12 +108,16 @@ Debugger::Debugger(shared_ptr<Console> console)
_suspendRequestCount = 0;
CpuType cpuType = _gbDebugger ? CpuType::Gameboy : CpuType::Cpu;
string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_cart->GetRomInfo().RomFile.GetFileName(), false) + ".cdl");
GetCodeDataLogger(cpuType)->LoadCdlFile(cdlFile, _settings->CheckDebuggerFlag(DebuggerFlags::AutoResetCdl), _cart->GetCrc32());
string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(),
FolderUtilities::GetFilename(
_cart->GetRomInfo().RomFile.GetFileName(), false) + ".cdl");
GetCodeDataLogger(cpuType)->LoadCdlFile(cdlFile, _settings->CheckDebuggerFlag(DebuggerFlags::AutoResetCdl),
_cart->GetCrc32());
RefreshCodeCache();
if(_console->IsPaused()) {
if (_console->IsPaused())
{
Step(CpuType::Cpu, 1, StepType::Step);
}
_executionStopped = false;
@ -119,10 +131,13 @@ Debugger::~Debugger()
void Debugger::Release()
{
CpuType cpuType = _gbDebugger ? CpuType::Gameboy : CpuType::Cpu;
string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_cart->GetRomInfo().RomFile.GetFileName(), false) + ".cdl");
string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(),
FolderUtilities::GetFilename(
_cart->GetRomInfo().RomFile.GetFileName(), false) + ".cdl");
GetCodeDataLogger(cpuType)->SaveCdlFile(cdlFile, _cart->GetCrc32());
while(_executionStopped) {
while (_executionStopped)
{
Run();
}
}
@ -132,7 +147,8 @@ void Debugger::Reset()
_memoryAccessCounter->ResetCounts();
_cpuDebugger->Reset();
_spcDebugger->Reset();
if(_sa1Debugger) {
if (_sa1Debugger)
{
_sa1Debugger->Reset();
}
}
@ -140,17 +156,26 @@ void Debugger::Reset()
template <CpuType type>
void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType)
{
switch(type) {
case CpuType::Cpu: _cpuDebugger->ProcessRead(addr, value, opType); break;
case CpuType::Spc: _spcDebugger->ProcessRead(addr, value, opType); break;
case CpuType::NecDsp: _necDspDebugger->ProcessRead(addr, value, opType); break;
case CpuType::Sa1: _sa1Debugger->ProcessRead(addr, value, opType); break;
case CpuType::Gsu: _gsuDebugger->ProcessRead(addr, value, opType); break;
case CpuType::Cx4: _cx4Debugger->ProcessRead(addr, value, opType); break;
case CpuType::Gameboy: _gbDebugger->ProcessRead(addr, value, opType); break;
switch (type)
{
case CpuType::Cpu: _cpuDebugger->ProcessRead(addr, value, opType);
break;
case CpuType::Spc: _spcDebugger->ProcessRead(addr, value, opType);
break;
case CpuType::NecDsp: _necDspDebugger->ProcessRead(addr, value, opType);
break;
case CpuType::Sa1: _sa1Debugger->ProcessRead(addr, value, opType);
break;
case CpuType::Gsu: _gsuDebugger->ProcessRead(addr, value, opType);
break;
case CpuType::Cx4: _cx4Debugger->ProcessRead(addr, value, opType);
break;
case CpuType::Gameboy: _gbDebugger->ProcessRead(addr, value, opType);
break;
}
if(_scriptManager->HasScript()) {
if (_scriptManager->HasScript())
{
_scriptManager->ProcessMemoryOperation(addr, value, opType, type);
}
}
@ -158,17 +183,26 @@ void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationTy
template <CpuType type>
void Debugger::ProcessMemoryWrite(uint32_t addr, uint8_t value, MemoryOperationType opType)
{
switch(type) {
case CpuType::Cpu: _cpuDebugger->ProcessWrite(addr, value, opType); break;
case CpuType::Spc: _spcDebugger->ProcessWrite(addr, value, opType); break;
case CpuType::NecDsp: _necDspDebugger->ProcessWrite(addr, value, opType); break;
case CpuType::Sa1: _sa1Debugger->ProcessWrite(addr, value, opType); break;
case CpuType::Gsu: _gsuDebugger->ProcessWrite(addr, value, opType); break;
case CpuType::Cx4: _cx4Debugger->ProcessWrite(addr, value, opType); break;
case CpuType::Gameboy: _gbDebugger->ProcessWrite(addr, value, opType); break;
switch (type)
{
case CpuType::Cpu: _cpuDebugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Spc: _spcDebugger->ProcessWrite(addr, value, opType);
break;
case CpuType::NecDsp: _necDspDebugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Sa1: _sa1Debugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Gsu: _gsuDebugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Cx4: _cx4Debugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Gameboy: _gbDebugger->ProcessWrite(addr, value, opType);
break;
}
if(_scriptManager->HasScript()) {
if (_scriptManager->HasScript())
{
_scriptManager->ProcessMemoryOperation(addr, value, opType, type);
}
}
@ -196,7 +230,9 @@ void Debugger::ProcessPpuRead(uint16_t addr, uint8_t value, SnesMemoryType memor
AddressInfo addressInfo{addr, memoryType};
MemoryOperationInfo operation{addr, value, MemoryOperationType::Read};
BreakpointManager* bpManager = DebugUtilities::ToCpuType(memoryType) == CpuType::Gameboy ? _gbDebugger->GetBreakpointManager() : _cpuDebugger->GetBreakpointManager();
BreakpointManager* bpManager = DebugUtilities::ToCpuType(memoryType) == CpuType::Gameboy
? _gbDebugger->GetBreakpointManager()
: _cpuDebugger->GetBreakpointManager();
ProcessBreakConditions(false, bpManager, operation, addressInfo);
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _console->GetMasterClock());
@ -207,7 +243,9 @@ void Debugger::ProcessPpuWrite(uint16_t addr, uint8_t value, SnesMemoryType memo
AddressInfo addressInfo{addr, memoryType};
MemoryOperationInfo operation{addr, value, MemoryOperationType::Write};
BreakpointManager* bpManager = DebugUtilities::ToCpuType(memoryType) == CpuType::Gameboy ? _gbDebugger->GetBreakpointManager() : _cpuDebugger->GetBreakpointManager();
BreakpointManager* bpManager = DebugUtilities::ToCpuType(memoryType) == CpuType::Gameboy
? _gbDebugger->GetBreakpointManager()
: _cpuDebugger->GetBreakpointManager();
ProcessBreakConditions(false, bpManager, operation, addressInfo);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _console->GetMasterClock());
@ -218,22 +256,29 @@ void Debugger::ProcessPpuCycle()
{
uint16_t scanline;
uint16_t cycle;
if(cpuType == CpuType::Gameboy) {
if (cpuType == CpuType::Gameboy)
{
scanline = _gameboy->GetPpu()->GetScanline();
cycle = _gameboy->GetPpu()->GetCycle();
} else {
}
else
{
scanline = _ppu->GetScanline();
cycle = _memoryManager->GetHClock();
}
_ppuTools->UpdateViewers(scanline, cycle, cpuType);
switch(cpuType) {
switch (cpuType)
{
case CpuType::Cpu:
//Catch up SPC/DSP as needed (if we're tracing or debugging those particular CPUs)
if(_traceLogger->IsCpuLogged(CpuType::Spc) || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled)) {
if (_traceLogger->IsCpuLogged(CpuType::Spc) || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled))
{
_spc->Run();
} else if(_traceLogger->IsCpuLogged(CpuType::NecDsp)) {
}
else if (_traceLogger->IsCpuLogged(CpuType::NecDsp))
{
_cart->RunCoprocessors();
}
@ -245,60 +290,79 @@ void Debugger::ProcessPpuCycle()
break;
}
if(_breakRequestCount > 0) {
if (_breakRequestCount > 0)
{
SleepUntilResume(BreakSource::Unspecified);
}
}
void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo* operation, int breakpointId)
{
if(_suspendRequestCount) {
if (_suspendRequestCount)
{
return;
}
_console->GetSoundMixer()->StopAudio();
_disassembler->Disassemble(CpuType::Cpu);
_disassembler->Disassemble(CpuType::Spc);
if(_cart->GetSa1()) {
if (_cart->GetSa1())
{
_disassembler->Disassemble(CpuType::Sa1);
} else if(_cart->GetGsu()) {
}
else if (_cart->GetGsu())
{
_disassembler->Disassemble(CpuType::Gsu);
} else if(_cart->GetDsp()) {
}
else if (_cart->GetDsp())
{
_disassembler->Disassemble(CpuType::NecDsp);
} else if(_cart->GetCx4()) {
}
else if (_cart->GetCx4())
{
_disassembler->Disassemble(CpuType::Cx4);
} else if(_cart->GetGameboy()) {
}
else if (_cart->GetGameboy())
{
_disassembler->RefreshDisassembly(CpuType::Gameboy);
}
_executionStopped = true;
if(source != BreakSource::Unspecified || _breakRequestCount == 0) {
if (source != BreakSource::Unspecified || _breakRequestCount == 0)
{
//Only trigger code break event if the pause was caused by user action
BreakEvent evt = {};
evt.BreakpointId = breakpointId;
evt.Source = source;
if(operation) {
if (operation)
{
evt.Operation = *operation;
}
_waitForBreakResume = true;
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak, &evt);
}
while((_waitForBreakResume && !_suspendRequestCount) || _breakRequestCount) {
while ((_waitForBreakResume && !_suspendRequestCount) || _breakRequestCount)
{
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(10));
}
_executionStopped = false;
}
void Debugger::ProcessBreakConditions(bool needBreak, BreakpointManager* bpManager, MemoryOperationInfo &operation, AddressInfo &addressInfo, BreakSource source)
void Debugger::ProcessBreakConditions(bool needBreak, BreakpointManager* bpManager, MemoryOperationInfo& operation,
AddressInfo& addressInfo, BreakSource source)
{
if (needBreak || _breakRequestCount || _waitForBreakResume)
{
if(needBreak || _breakRequestCount || _waitForBreakResume) {
SleepUntilResume(source);
} else {
}
else
{
int breakpointId = bpManager->CheckBreakpoint(operation, addressInfo);
if(breakpointId >= 0) {
if (breakpointId >= 0)
{
SleepUntilResume(BreakSource::Breakpoint, &operation, breakpointId);
}
}
@ -307,10 +371,14 @@ void Debugger::ProcessBreakConditions(bool needBreak, BreakpointManager* bpManag
template <CpuType type>
void Debugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
{
switch(type) {
case CpuType::Cpu: _cpuDebugger->ProcessInterrupt(originalPc, currentPc, forNmi); break;
case CpuType::Sa1: _sa1Debugger->ProcessInterrupt(originalPc, currentPc, forNmi); break;
case CpuType::Gameboy: _gbDebugger->ProcessInterrupt(originalPc, currentPc); break;
switch (type)
{
case CpuType::Cpu: _cpuDebugger->ProcessInterrupt(originalPc, currentPc, forNmi);
break;
case CpuType::Sa1: _sa1Debugger->ProcessInterrupt(originalPc, currentPc, forNmi);
break;
case CpuType::Gameboy: _gbDebugger->ProcessInterrupt(originalPc, currentPc);
break;
}
ProcessEvent(forNmi ? EventType::Nmi : EventType::Irq);
@ -320,24 +388,29 @@ void Debugger::ProcessEvent(EventType type)
{
_scriptManager->ProcessEvent(type);
switch(type) {
switch (type)
{
default: break;
case EventType::StartFrame:
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh, (void*)CpuType::Cpu);
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh,
(void*)CpuType::Cpu);
GetEventManager(CpuType::Cpu)->ClearFrameEvents();
break;
case EventType::GbStartFrame:
if(_settings->CheckFlag(EmulationFlags::GameboyMode)) {
if (_settings->CheckFlag(EmulationFlags::GameboyMode))
{
_scriptManager->ProcessEvent(EventType::StartFrame);
}
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh, (void*)CpuType::Gameboy);
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh,
(void*)CpuType::Gameboy);
GetEventManager(CpuType::Gameboy)->ClearFrameEvents();
break;
case EventType::GbEndFrame:
if(_settings->CheckFlag(EmulationFlags::GameboyMode)) {
if (_settings->CheckFlag(EmulationFlags::GameboyMode))
{
_scriptManager->ProcessEvent(EventType::EndFrame);
}
break;
@ -357,9 +430,12 @@ int32_t Debugger::EvaluateExpression(string expression, CpuType cpuType, EvalRes
MemoryOperationInfo operationInfo{0, 0, MemoryOperationType::Read};
DebugState state;
GetState(state, false);
if(useCache) {
if (useCache)
{
return _watchExpEval[(int)cpuType]->Evaluate(expression, state, resultType, operationInfo);
} else {
}
else
{
ExpressionEvaluator expEval(this, cpuType);
return expEval.Evaluate(expression, state, resultType, operationInfo);
}
@ -369,19 +445,24 @@ void Debugger::Run()
{
_cpuDebugger->Run();
_spcDebugger->Run();
if(_sa1Debugger) {
if (_sa1Debugger)
{
_sa1Debugger->Run();
}
if(_gsuDebugger) {
if (_gsuDebugger)
{
_gsuDebugger->Run();
}
if(_necDspDebugger) {
if (_necDspDebugger)
{
_necDspDebugger->Run();
}
if(_cx4Debugger) {
if (_cx4Debugger)
{
_cx4Debugger->Run();
}
if(_gbDebugger) {
if (_gbDebugger)
{
_gbDebugger->Run();
}
_waitForBreakResume = false;
@ -393,39 +474,55 @@ void Debugger::Step(CpuType cpuType, int32_t stepCount, StepType type)
StepRequest step;
IDebugger* debugger = nullptr;
switch(cpuType) {
case CpuType::Cpu: debugger = _cpuDebugger.get(); break;
case CpuType::Spc: debugger = _spcDebugger.get(); break;
case CpuType::NecDsp: debugger = _necDspDebugger.get(); break;
case CpuType::Sa1: debugger = _sa1Debugger.get(); break;
case CpuType::Gsu: debugger = _gsuDebugger.get(); break;
case CpuType::Cx4: debugger = _cx4Debugger.get(); break;
case CpuType::Gameboy: debugger = _gbDebugger.get(); break;
switch (cpuType)
{
case CpuType::Cpu: debugger = _cpuDebugger.get();
break;
case CpuType::Spc: debugger = _spcDebugger.get();
break;
case CpuType::NecDsp: debugger = _necDspDebugger.get();
break;
case CpuType::Sa1: debugger = _sa1Debugger.get();
break;
case CpuType::Gsu: debugger = _gsuDebugger.get();
break;
case CpuType::Cx4: debugger = _cx4Debugger.get();
break;
case CpuType::Gameboy: debugger = _gbDebugger.get();
break;
}
if(debugger) {
if (debugger)
{
debugger->Step(stepCount, type);
}
if(debugger != _cpuDebugger.get()) {
if (debugger != _cpuDebugger.get())
{
_cpuDebugger->Run();
}
if(debugger != _spcDebugger.get()) {
if (debugger != _spcDebugger.get())
{
_spcDebugger->Run();
}
if(_sa1Debugger && debugger != _sa1Debugger.get()) {
if (_sa1Debugger && debugger != _sa1Debugger.get())
{
_sa1Debugger->Run();
}
if(_gsuDebugger && debugger != _gsuDebugger.get()) {
if (_gsuDebugger && debugger != _gsuDebugger.get())
{
_gsuDebugger->Run();
}
if(_necDspDebugger && debugger != _necDspDebugger.get()) {
if (_necDspDebugger && debugger != _necDspDebugger.get())
{
_necDspDebugger->Run();
}
if(_cx4Debugger && debugger != _cx4Debugger.get()) {
if (_cx4Debugger && debugger != _cx4Debugger.get())
{
_cx4Debugger->Run();
}
if(_gbDebugger && debugger != _gbDebugger.get()) {
if (_gbDebugger && debugger != _gbDebugger.get())
{
_gbDebugger->Run();
}
_waitForBreakResume = false;
@ -443,24 +540,33 @@ bool Debugger::HasBreakRequest()
void Debugger::BreakRequest(bool release)
{
if(release) {
if (release)
{
_breakRequestCount--;
} else {
}
else
{
_breakRequestCount++;
}
}
void Debugger::SuspendDebugger(bool release)
{
if(release) {
if(_suspendRequestCount > 0) {
if (release)
{
if (_suspendRequestCount > 0)
{
_suspendRequestCount--;
} else {
}
else
{
#ifdef _DEBUG
//throw std::runtime_error("unexpected debugger suspend::release call");
#endif
}
} else {
}
else
{
_suspendRequestCount++;
}
}
@ -468,11 +574,19 @@ void Debugger::SuspendDebugger(bool release)
void Debugger::BreakImmediately(BreakSource source)
{
bool gbDebugger = _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled);
if(source == BreakSource::GbDisableLcdOutsideVblank && (!gbDebugger || !_settings->CheckDebuggerFlag(DebuggerFlags::GbBreakOnDisableLcdOutsideVblank))) {
if (source == BreakSource::GbDisableLcdOutsideVblank && (!gbDebugger || !_settings->CheckDebuggerFlag(
DebuggerFlags::GbBreakOnDisableLcdOutsideVblank)))
{
return;
} else if(source == BreakSource::GbInvalidVramAccess && (!gbDebugger || !_settings->CheckDebuggerFlag(DebuggerFlags::GbBreakOnInvalidVramAccess))) {
}
else if (source == BreakSource::GbInvalidVramAccess && (!gbDebugger || !_settings->CheckDebuggerFlag(
DebuggerFlags::GbBreakOnInvalidVramAccess)))
{
return;
} else if(source == BreakSource::GbInvalidOamAccess && (!gbDebugger || !_settings->CheckDebuggerFlag(DebuggerFlags::GbBreakOnInvalidOamAccess))) {
}
else if (source == BreakSource::GbInvalidOamAccess && (!gbDebugger || !_settings->CheckDebuggerFlag(
DebuggerFlags::GbBreakOnInvalidOamAccess)))
{
return;
}
SleepUntilResume(source);
@ -486,27 +600,34 @@ void Debugger::GetState(DebugState &state, bool partialPpuState)
state.Spc = _spc->GetState();
state.Dsp = _spc->GetDspState();
if(!partialPpuState) {
for(int i = 0; i < 8; i++) {
if (!partialPpuState)
{
for (int i = 0; i < 8; i++)
{
state.DmaChannels[i] = _dmaController->GetChannelConfig(i);
}
state.InternalRegs = _internalRegs->GetState();
state.Alu = _internalRegs->GetAluState();
}
if(_cart->GetDsp()) {
if (_cart->GetDsp())
{
state.NecDsp = _cart->GetDsp()->GetState();
}
if(_cart->GetSa1()) {
if (_cart->GetSa1())
{
state.Sa1 = _cart->GetSa1()->GetState();
}
if(_cart->GetGsu()) {
if (_cart->GetGsu())
{
state.Gsu = _cart->GetGsu()->GetState();
}
if(_cart->GetCx4()) {
if (_cart->GetCx4())
{
state.Cx4 = _cart->GetCx4()->GetState();
}
if(_cart->GetGameboy()) {
if (_cart->GetGameboy())
{
state.Gameboy = _cart->GetGameboy()->GetState();
}
}
@ -558,23 +679,39 @@ void Debugger::SetSpcRegister(SpcRegister reg, uint16_t value)
AddressInfo Debugger::GetAbsoluteAddress(AddressInfo relAddress)
{
if(relAddress.Type == SnesMemoryType::CpuMemory) {
if(_memoryManager->IsRegister(relAddress.Address)) {
if (relAddress.Type == SnesMemoryType::CpuMemory)
{
if (_memoryManager->IsRegister(relAddress.Address))
{
return {relAddress.Address & 0xFFFF, SnesMemoryType::Register};
} else {
}
else
{
return _memoryManager->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
}
} else if(relAddress.Type == SnesMemoryType::SpcMemory) {
}
else if (relAddress.Type == SnesMemoryType::SpcMemory)
{
return _spc->GetAbsoluteAddress(relAddress.Address);
} else if(relAddress.Type == SnesMemoryType::Sa1Memory) {
}
else if (relAddress.Type == SnesMemoryType::Sa1Memory)
{
return _cart->GetSa1()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
} else if(relAddress.Type == SnesMemoryType::GsuMemory) {
}
else if (relAddress.Type == SnesMemoryType::GsuMemory)
{
return _cart->GetGsu()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
} else if(relAddress.Type == SnesMemoryType::Cx4Memory) {
}
else if (relAddress.Type == SnesMemoryType::Cx4Memory)
{
return _cart->GetCx4()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
} else if(relAddress.Type == SnesMemoryType::NecDspMemory) {
}
else if (relAddress.Type == SnesMemoryType::NecDspMemory)
{
return {relAddress.Address, SnesMemoryType::DspProgramRom};
} else if(relAddress.Type == SnesMemoryType::GameboyMemory) {
}
else if (relAddress.Type == SnesMemoryType::GameboyMemory)
{
return _cart->GetGameboy()->GetAbsoluteAddress(relAddress.Address);
}
@ -584,35 +721,51 @@ AddressInfo Debugger::GetAbsoluteAddress(AddressInfo relAddress)
AddressInfo Debugger::GetRelativeAddress(AddressInfo absAddress, CpuType cpuType)
{
MemoryMappings* mappings = nullptr;
switch(cpuType) {
case CpuType::Cpu: mappings = _memoryManager->GetMemoryMappings(); break;
switch (cpuType)
{
case CpuType::Cpu: mappings = _memoryManager->GetMemoryMappings();
break;
case CpuType::Spc: break;
case CpuType::NecDsp: break;
case CpuType::Sa1: mappings = _cart->GetSa1()->GetMemoryMappings(); break;
case CpuType::Gsu: mappings = _cart->GetGsu()->GetMemoryMappings(); break;
case CpuType::Cx4: mappings = _cart->GetCx4()->GetMemoryMappings(); break;
case CpuType::Sa1: mappings = _cart->GetSa1()->GetMemoryMappings();
break;
case CpuType::Gsu: mappings = _cart->GetGsu()->GetMemoryMappings();
break;
case CpuType::Cx4: mappings = _cart->GetCx4()->GetMemoryMappings();
break;
case CpuType::Gameboy: break;
}
switch(absAddress.Type) {
switch (absAddress.Type)
{
case SnesMemoryType::PrgRom:
case SnesMemoryType::WorkRam:
case SnesMemoryType::SaveRam: {
if(!mappings) {
case SnesMemoryType::SaveRam:
{
if (!mappings)
{
throw std::runtime_error("Unsupported cpu type");
}
uint8_t startBank = 0;
//Try to find a mirror close to where the PC is
if(cpuType == CpuType::Cpu) {
if(absAddress.Type == SnesMemoryType::WorkRam) {
if (cpuType == CpuType::Cpu)
{
if (absAddress.Type == SnesMemoryType::WorkRam)
{
startBank = 0x7E;
} else {
}
else
{
startBank = _cpu->GetState().K & 0xC0;
}
} else if(cpuType == CpuType::Sa1) {
}
else if (cpuType == CpuType::Sa1)
{
startBank = (_cart->GetSa1()->GetCpuState().K & 0xC0);
} else if(cpuType == CpuType::Gsu) {
}
else if (cpuType == CpuType::Gsu)
{
startBank = (_cart->GetGsu()->GetState().ProgramBank & 0xC0);
}
@ -665,7 +818,8 @@ void Debugger::RefreshCodeCache()
void Debugger::RebuildPrgCache(CpuType cpuType)
{
shared_ptr<CodeDataLogger> cdl = GetCodeDataLogger(cpuType);
if(!cdl) {
if (!cdl)
{
return;
}
@ -673,8 +827,10 @@ void Debugger::RebuildPrgCache(CpuType cpuType)
AddressInfo addrInfo;
addrInfo.Type = cpuType == CpuType::Gameboy ? SnesMemoryType::GbPrgRom : SnesMemoryType::PrgRom;
for(uint32_t i = 0; i < prgRomSize; i++) {
if(cdl->IsCode(i)) {
for (uint32_t i = 0; i < prgRomSize; i++)
{
if (cdl->IsCode(i))
{
addrInfo.Address = (int32_t)i;
i += _disassembler->BuildCache(addrInfo, cdl->GetCpuFlags(i), cdl->GetCpuType(i)) - 1;
}
@ -685,14 +841,18 @@ void Debugger::GetCdlData(uint32_t offset, uint32_t length, SnesMemoryType memor
{
CpuType cpuType = DebugUtilities::ToCpuType(memoryType);
shared_ptr<CodeDataLogger> cdl = GetCodeDataLogger(cpuType);
if(memoryType == SnesMemoryType::PrgRom || memoryType == SnesMemoryType::GbPrgRom) {
if (memoryType == SnesMemoryType::PrgRom || memoryType == SnesMemoryType::GbPrgRom)
{
cdl->GetCdlData(offset, length, cdlData);
} else {
}
else
{
SnesMemoryType prgType = _gbDebugger ? SnesMemoryType::GbPrgRom : SnesMemoryType::PrgRom;
AddressInfo relAddress;
relAddress.Type = memoryType;
for(uint32_t i = 0; i < length; i++) {
for (uint32_t i = 0; i < length; i++)
{
relAddress.Address = offset + i;
AddressInfo info = GetAbsoluteAddress(relAddress);
cdlData[i] = info.Type == prgType ? cdl->GetFlags(info.Address) : 0;
@ -704,60 +864,82 @@ void Debugger::SetBreakpoints(Breakpoint breakpoints[], uint32_t length)
{
_cpuDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
_spcDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
if(_gsuDebugger) {
if (_gsuDebugger)
{
_gsuDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
if(_sa1Debugger) {
if (_sa1Debugger)
{
_sa1Debugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
if(_necDspDebugger) {
if (_necDspDebugger)
{
_necDspDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
if(_cx4Debugger) {
if (_cx4Debugger)
{
_cx4Debugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
if(_gbDebugger) {
if (_gbDebugger)
{
_gbDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
}
void Debugger::GetBreakpoints(CpuType cpuType, Breakpoint* breakpoints, int& execs, int& reads, int& writes)
{
switch (cpuType) {
switch (cpuType)
{
case CpuType::Cpu: return _cpuDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
case CpuType::Spc: return _spcDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
case CpuType::Gsu: {
if (_gsuDebugger) {
case CpuType::Gsu:
{
if (_gsuDebugger)
{
return _gsuDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
} break;
case CpuType::Sa1: {
if (_sa1Debugger) {
}
break;
case CpuType::Sa1:
{
if (_sa1Debugger)
{
return _sa1Debugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
} break;
case CpuType::NecDsp: {
if (_necDspDebugger) {
}
break;
case CpuType::NecDsp:
{
if (_necDspDebugger)
{
return _necDspDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
} break;
case CpuType::Cx4: {
if (_cx4Debugger) {
}
break;
case CpuType::Cx4:
{
if (_cx4Debugger)
{
return _cx4Debugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
} break;
case CpuType::Gameboy: {
if (_gbDebugger) {
}
break;
case CpuType::Gameboy:
{
if (_gbDebugger)
{
return _gbDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
} break;
}
break;
}
}
void Debugger::Log(string message)
{
auto lock = _logLock.AcquireSafe();
if(_debuggerLog.size() >= 1000) {
if (_debuggerLog.size() >= 1000)
{
_debuggerLog.pop_front();
}
_debuggerLog.push_back(message);
@ -767,7 +949,8 @@ string Debugger::GetLog()
{
auto lock = _logLock.AcquireSafe();
stringstream ss;
for(string& msg : _debuggerLog) {
for (string& msg : _debuggerLog)
{
ss << msg << "\n";
}
return ss.str();
@ -780,25 +963,35 @@ void Debugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption str
Gameboy* gb = _cart->GetGameboy();
vector<uint8_t> rom;
if(gb) {
if (gb)
{
uint8_t* prgRom = gb->DebugGetMemory(SnesMemoryType::GbPrgRom);
uint32_t prgRomSize = gb->DebugGetMemorySize(SnesMemoryType::GbPrgRom);
rom = vector<uint8_t>(prgRom, prgRom + prgRomSize);
} else {
}
else
{
rom = vector<uint8_t>(_cart->DebugGetPrgRom(), _cart->DebugGetPrgRom() + _cart->DebugGetPrgRomSize());
}
if(saveAsIps) {
if (saveAsIps)
{
output = IpsPatcher::CreatePatch(_cart->GetOriginalPrgRom(), rom);
} else {
if(stripOption != CdlStripOption::StripNone) {
}
else
{
if (stripOption != CdlStripOption::StripNone)
{
GetCodeDataLogger(gb ? CpuType::Gameboy : CpuType::Cpu)->StripData(rom.data(), stripOption);
//Preserve rom header regardless of CDL file contents
if(gb) {
if (gb)
{
GameboyHeader header = gb->GetHeader();
memcpy(rom.data() + romInfo.HeaderOffset, &header, sizeof(GameboyHeader));
} else {
}
else
{
memcpy(rom.data() + romInfo.HeaderOffset, &romInfo.Header, sizeof(SnesCartInformation));
}
}
@ -806,7 +999,8 @@ void Debugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption str
}
ofstream file(filename, ios::out | ios::binary);
if(file) {
if (file)
{
file.write((char*)output.data(), output.size());
file.close();
}
@ -829,9 +1023,12 @@ shared_ptr<MemoryAccessCounter> Debugger::GetMemoryAccessCounter()
shared_ptr<CodeDataLogger> Debugger::GetCodeDataLogger(CpuType cpuType)
{
if(cpuType == CpuType::Gameboy) {
if (cpuType == CpuType::Gameboy)
{
return _gbDebugger ? _gbDebugger->GetCodeDataLogger() : nullptr;
} else {
}
else
{
return _codeDataLogger;
}
}
@ -848,9 +1045,12 @@ shared_ptr<PpuTools> Debugger::GetPpuTools()
shared_ptr<IEventManager> Debugger::GetEventManager(CpuType cpuType)
{
if(cpuType == CpuType::Gameboy) {
if (cpuType == CpuType::Gameboy)
{
return std::dynamic_pointer_cast<IEventManager>(_gbDebugger->GetEventManager());
} else {
}
else
{
return std::dynamic_pointer_cast<IEventManager>(_cpuDebugger->GetEventManager());
}
}
@ -867,7 +1067,8 @@ shared_ptr<ScriptManager> Debugger::GetScriptManager()
shared_ptr<CallstackManager> Debugger::GetCallstackManager(CpuType cpuType)
{
switch(cpuType) {
switch (cpuType)
{
case CpuType::Cpu: return _cpuDebugger->GetCallstackManager();
case CpuType::Spc: return _spcDebugger->GetCallstackManager();
case CpuType::Sa1: return _sa1Debugger->GetCallstackManager();
@ -888,9 +1089,12 @@ shared_ptr<Console> Debugger::GetConsole()
shared_ptr<IAssembler> Debugger::GetAssembler(CpuType cpuType)
{
if(cpuType == CpuType::Gameboy) {
if (cpuType == CpuType::Gameboy)
{
return std::dynamic_pointer_cast<IAssembler>(_gbDebugger->GetAssembler());
} else {
}
else
{
return std::dynamic_pointer_cast<IAssembler>(_cpuDebugger->GetAssembler());
}
}

View file

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

View file

@ -27,7 +27,8 @@ void DefaultVideoFilter::InitConversionMatrix(double hueShift, double saturation
double* output = _yiqToRgbMatrix;
double* input = baseValues;
for(int n = 0; n < 3; n++) {
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;
}
@ -123,32 +134,47 @@ void DefaultVideoFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
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++) {
}
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) {
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];
@ -158,16 +184,20 @@ void DefaultVideoFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
}
}
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]];
}
}

View file

@ -46,16 +46,19 @@ 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};
BuildCache(dspStart, 0, CpuType::NecDsp);
@ -72,7 +75,8 @@ void Disassembler::InitSource(SnesMemoryType type)
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");
}
@ -86,23 +90,29 @@ uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuTy
bool needDisassemble = false;
int returnSize = 0;
int32_t address = addrInfo.Address;
while(address >= 0 && address < (int32_t)src.Cache->size()) {
while (address >= 0 && address < (int32_t)src.Cache->size())
{
DisassemblyInfo& disInfo = (*src.Cache)[address];
if(!disInfo.IsInitialized() || !disInfo.IsValid(cpuFlags)) {
if (!disInfo.IsInitialized() || !disInfo.IsValid(cpuFlags))
{
disInfo.Initialize(src.Data + address, cpuFlags, type);
for(int i = 1; i < disInfo.GetOpSize(); i++) {
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,10 +160,14 @@ 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;
}
@ -157,14 +175,16 @@ void Disassembler::InvalidateCache(AddressInfo addrInfo, CpuType type)
}
}
if(needDisassemble) {
if (needDisassemble)
{
SetDisassembleFlag(type);
}
}
void Disassembler::Disassemble(CpuType cpuType)
{
if(!_needDisassemble[(int)cpuType]) {
if (!_needDisassemble[(int)cpuType])
{
return;
}
@ -174,27 +194,31 @@ void Disassembler::Disassemble(CpuType cpuType)
MemoryMappings* mappings = nullptr;
int32_t maxAddr = 0xFFFFFF;
switch(cpuType) {
switch (cpuType)
{
case CpuType::Cpu:
mappings = _memoryManager->GetMemoryMappings();
break;
case CpuType::Sa1:
if(!_sa1) {
if (!_sa1)
{
return;
}
mappings = _sa1->GetMemoryMappings();
break;
case CpuType::Gsu:
if(!_gsu) {
if (!_gsu)
{
return;
}
mappings = _gsu->GetMemoryMappings();
break;
case CpuType::NecDsp:
if(!_console->GetCartridge()->GetDsp()) {
if (!_console->GetCartridge()->GetDsp())
{
return;
}
mappings = nullptr;
@ -207,7 +231,8 @@ void Disassembler::Disassemble(CpuType cpuType)
break;
case CpuType::Gameboy:
if(!_gameboy) {
if (!_gameboy)
{
return;
}
mappings = nullptr;
@ -215,7 +240,8 @@ void Disassembler::Disassemble(CpuType cpuType)
break;
case CpuType::Cx4:
if(!_console->GetCartridge()->GetCx4()) {
if (!_console->GetCartridge()->GetCx4())
{
return;
}
mappings = _console->GetCartridge()->GetCx4()->GetMemoryMappings();
@ -238,16 +264,23 @@ 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;
}
@ -261,31 +294,43 @@ void Disassembler::Disassemble(CpuType cpuType)
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,8 +447,10 @@ 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));
}
}
@ -375,7 +458,8 @@ void Disassembler::Disassemble(CpuType cpuType)
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;
}
@ -423,46 +513,61 @@ bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &d
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) {
switch (result.Address.Type)
{
default: break;
case SnesMemoryType::GbPrgRom:
case SnesMemoryType::PrgRom: data.Flags |= (uint8_t)LineFlags::PrgRom; break;
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::WorkRam: data.Flags |= (uint8_t)LineFlags::WorkRam;
break;
case SnesMemoryType::GbCartRam:
case SnesMemoryType::SaveRam: data.Flags |= (uint8_t)LineFlags::SaveRam; break;
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++) {
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,65 +575,89 @@ bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &d
data.Address = result.CpuAddress;
data.AbsoluteAddress = result.Address.Address;
switch(lineCpuType) {
switch (lineCpuType)
{
case CpuType::Cpu:
case CpuType::Sa1: {
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;
@ -536,9 +665,12 @@ bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &d
case CpuType::NecDsp:
case CpuType::Cx4:
if(!disInfo.IsInitialized()) {
if (!disInfo.IsInitialized())
{
disInfo = DisassemblyInfo(src.Data + result.Address.Address, 0, type);
} else {
}
else
{
data.Flags |= LineFlags::VerifiedCode;
}
@ -547,11 +679,15 @@ bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &d
data.ValueSize = 0;
break;
case CpuType::Gameboy: {
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);
}
}

View file

@ -72,5 +72,6 @@ public:
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);
int32_t SearchDisassembly(CpuType type, const char* searchString, int32_t startPosition, int32_t endPosition,
bool searchBackwards);
};

View file

@ -43,7 +43,8 @@ void DisassemblyInfo::Initialize(uint32_t cpuAddress, uint8_t cpuFlags, CpuType
_opSize = GetOpSize(_byteCode[0], _flags, _cpuType);
for(int i = 1; i < _opSize; i++) {
for (int i = 1; i < _opSize; i++)
{
_byteCode[i] = memoryDumper->GetMemoryValue(cpuMemType, cpuAddress + i);
}
@ -65,25 +66,33 @@ 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)
{
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)
{
switch(_cpuType) {
switch (_cpuType)
{
case CpuType::Sa1:
case CpuType::Cpu:
return CpuDisUtils::GetEffectiveAddress(*this, console, *(CpuState*)cpuState, cpuType);
@ -133,9 +142,11 @@ void DisassemblyInfo::GetByteCode(uint8_t copyBuffer[4])
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,7 +155,8 @@ void DisassemblyInfo::GetByteCode(string &out)
uint8_t DisassemblyInfo::GetOpSize(uint8_t opCode, uint8_t flags, CpuType type)
{
switch(type) {
switch (type)
{
case CpuType::Sa1:
case CpuType::Cpu:
return CpuDisUtils::GetOpSize(opCode, flags);
@ -152,11 +164,16 @@ uint8_t DisassemblyInfo::GetOpSize(uint8_t opCode, uint8_t flags, CpuType type)
case CpuType::Spc: return SpcDisUtils::GetOpSize(opCode);
case CpuType::Gsu:
if(opCode >= 0x05 && opCode <= 0x0F) {
if (opCode >= 0x05 && opCode <= 0x0F)
{
return 2;
} else if(opCode >= 0xA0 && opCode <= 0xAF) {
}
else if (opCode >= 0xA0 && opCode <= 0xAF)
{
return 2;
} else if(opCode >= 0xF0 && opCode <= 0xFF) {
}
else if (opCode >= 0xF0 && opCode <= 0xFF)
{
return 3;
}
return 1;
@ -172,7 +189,8 @@ 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) {
switch (type)
{
case CpuType::Sa1:
case CpuType::Cpu:
return opCode == 0x20 || opCode == 0x22 || opCode == 0xFC; //JSR, JSL
@ -192,7 +210,8 @@ bool DisassemblyInfo::IsJumpToSub(uint8_t opCode, CpuType type)
bool DisassemblyInfo::IsReturnInstruction(uint8_t opCode, CpuType type)
{
//RTS/RTI
switch(type) {
switch (type)
{
case CpuType::Sa1:
case CpuType::Cpu:
return opCode == 0x60 || opCode == 0x6B || opCode == 0x40;
@ -213,20 +232,29 @@ bool DisassemblyInfo::IsReturnInstruction(uint8_t opCode, CpuType type)
bool DisassemblyInfo::IsUnconditionalJump()
{
uint8_t opCode = GetOpCode();
switch(_cpuType) {
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) {
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) {
}
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) {
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;
@ -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);
}

View file

@ -47,6 +47,6 @@ public:
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);
uint16_t GetMemoryValue(uint32_t effectiveAddress, MemoryDumper* memoryDumper, SnesMemoryType memType,
uint8_t& valueSize);
};

View file

@ -16,8 +16,10 @@ 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,27 +35,37 @@ 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();
}
@ -62,7 +74,8 @@ void DmaController::CopyDmaByte(uint32_t addressBusA, uint16_t addressBusB, bool
void DmaController::RunDma(DmaChannelConfig& channel)
{
if(!channel.DmaActive) {
if (!channel.DmaActive)
{
return;
}
@ -73,7 +86,8 @@ void DmaController::RunDma(DmaChannelConfig &channel)
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++) {
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();
}
@ -161,8 +186,10 @@ void DmaController::RunHdmaTransfer(DmaChannelConfig &channel)
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++) {
for (int i = 0; i < 8; i++)
{
DmaChannelConfig& ch = _channel[i];
if((_hdmaChannels & (1 << i)) == 0) {
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++) {
for (int i = 0; i < 8; i++)
{
DmaChannelConfig& ch = _channel[i];
if((_hdmaChannels & (1 << i)) == 0 || ch.HdmaFinished) {
if ((_hdmaChannels & (1 << i)) == 0 || ch.HdmaFinished)
{
continue;
}
@ -265,19 +308,24 @@ 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();
@ -290,7 +338,8 @@ bool DmaController::ProcessHdmaChannels()
}
//"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,28 +395,37 @@ 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]);
}
@ -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();
@ -402,7 +469,14 @@ void DmaController::Write(uint16_t addr, uint8_t value)
_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];
@ -415,7 +489,14 @@ 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];
@ -423,28 +504,56 @@ void DmaController::Write(uint16_t addr, uint8_t 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];
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];
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];
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];
@ -452,7 +561,14 @@ void DmaController::Write(uint16_t addr, uint8_t 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];
@ -460,7 +576,14 @@ void DmaController::Write(uint16_t addr, uint8_t value)
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];
@ -468,7 +591,14 @@ void DmaController::Write(uint16_t addr, uint8_t 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];
@ -476,7 +606,14 @@ void DmaController::Write(uint16_t addr, uint8_t 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];
@ -484,7 +621,14 @@ void DmaController::Write(uint16_t addr, uint8_t value)
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];
@ -492,21 +636,42 @@ void DmaController::Write(uint16_t addr, uint8_t 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];
@ -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];
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];
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];
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];
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];
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];
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];
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];
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];
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];
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();
}
@ -612,7 +860,8 @@ DmaChannelConfig DmaController::GetChannelConfig(uint8_t channel)
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,

View file

@ -18,33 +18,49 @@ 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 {
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;
}
} else {
}
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 {
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;
}
}
@ -77,7 +93,8 @@ public:
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;

View file

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

View file

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

View file

@ -10,8 +10,10 @@ private:
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]);
}
}

View file

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

View file

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

View file

@ -37,9 +37,11 @@ DummySpc::~DummySpc()
void DummySpc::Step()
{
do {
do
{
ProcessCycle();
} while(_opStep != SpcOpStep::ReadOpCode);
}
while (_opStep != SpcOpStep::ReadOpCode);
}
uint32_t DummySpc::GetWriteCount()

View file

@ -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)
{
//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,20 +267,28 @@ 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) {
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;
@ -267,23 +302,28 @@ double EmuSettings::GetAspectRatio(ConsoleRegion region)
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,13 +358,17 @@ bool EmuSettings::CheckDebuggerFlag(DebuggerFlags flag)
void EmuSettings::InitializeRam(void* data, uint32_t length)
{
switch(_emulation.RamPowerOnState) {
switch (_emulation.RamPowerOnState)
{
default:
case RamState::AllZeros: memset(data, 0, length); break;
case RamState::AllOnes: memset(data, 0xFF, length); break;
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++) {
for (uint32_t i = 0; i < length; i++)
{
((uint8_t*)data)[i] = dist(_mt);
}
break;
@ -344,7 +393,8 @@ bool EmuSettings::IsInputEnabled()
double EmuSettings::GetControllerDeadzoneRatio()
{
switch(_input.ControllerDeadzoneSize) {
switch (_input.ControllerDeadzoneSize)
{
case 0: return 0.5;
case 1: return 0.75;
case 2: return 1;

View file

@ -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;
@ -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;
}
@ -76,8 +80,10 @@ DebugEventInfo EventManager::GetEvent(uint16_t scanline, uint16_t cycle, EventVi
{
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;
}
}
@ -106,114 +112,185 @@ void EventManager::FilterEvents(EventViewerDisplayOptions &options)
_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;
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]) {
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) {
if (reg <= 0x213F)
{
if (isWrite)
{
if (reg >= 0x2101 && reg <= 0x2104)
{
showEvent = options.ShowPpuRegisterOamWrites;
} else if(reg >= 0x2105 && reg <= 0x210C) {
}
else if (reg >= 0x2105 && reg <= 0x210C)
{
showEvent = options.ShowPpuRegisterBgOptionWrites;
} else if(reg >= 0x210D && reg <= 0x2114) {
}
else if (reg >= 0x210D && reg <= 0x2114)
{
showEvent = options.ShowPpuRegisterBgScrollWrites;
} else if(reg >= 0x2115 && reg <= 0x2119) {
}
else if (reg >= 0x2115 && reg <= 0x2119)
{
showEvent = options.ShowPpuRegisterVramWrites;
} else if(reg >= 0x211A && reg <= 0x2120) {
}
else if (reg >= 0x211A && reg <= 0x2120)
{
showEvent = options.ShowPpuRegisterMode7Writes;
} else if(reg >= 0x2121 && reg <= 0x2122) {
}
else if (reg >= 0x2121 && reg <= 0x2122)
{
showEvent = options.ShowPpuRegisterCgramWrites;
} else if(reg >= 0x2123 && reg <= 0x212B) {
}
else if (reg >= 0x2123 && reg <= 0x212B)
{
showEvent = options.ShowPpuRegisterWindowWrites;
} else {
}
else
{
showEvent = options.ShowPpuRegisterOtherWrites;
}
} else {
}
else
{
showEvent = options.ShowPpuRegisterReads;
}
} else if(reg <= 0x217F) {
}
else if (reg <= 0x217F)
{
showEvent = isWrite ? options.ShowApuRegisterWrites : options.ShowApuRegisterReads;
} else if(reg <= 0x2183) {
}
else if (reg <= 0x2183)
{
showEvent = isWrite ? options.ShowWorkRamRegisterWrites : options.ShowWorkRamRegisterReads;
} else if(reg >= 0x4000) {
}
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;
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) {
if (reg <= 0x213F)
{
if (isWrite)
{
if (reg >= 0x2101 && reg <= 0x2104)
{
color = options.PpuRegisterWriteOamColor;
} else if(reg >= 0x2105 && reg <= 0x210C) {
}
else if (reg >= 0x2105 && reg <= 0x210C)
{
color = options.PpuRegisterWriteBgOptionColor;
} else if(reg >= 0x210D && reg <= 0x2114) {
}
else if (reg >= 0x210D && reg <= 0x2114)
{
color = options.PpuRegisterWriteBgScrollColor;
} else if(reg >= 0x2115 && reg <= 0x2119) {
}
else if (reg >= 0x2115 && reg <= 0x2119)
{
color = options.PpuRegisterWriteVramColor;
} else if(reg >= 0x211A && reg <= 0x2120) {
}
else if (reg >= 0x211A && reg <= 0x2120)
{
color = options.PpuRegisterWriteMode7Color;
} else if(reg >= 0x2121 && reg <= 0x2122) {
}
else if (reg >= 0x2121 && reg <= 0x2122)
{
color = options.PpuRegisterWriteCgramColor;
} else if(reg >= 0x2123 && reg <= 0x212B) {
}
else if (reg >= 0x2123 && reg <= 0x212B)
{
color = options.PpuRegisterWriteWindowColor;
} else {
}
else
{
color = options.PpuRegisterWriteOtherColor;
}
} else {
}
else
{
color = options.PpuRegisterReadColor;
}
} else if(reg <= 0x217F) {
}
else if (reg <= 0x217F)
{
color = isWrite ? options.ApuRegisterWriteColor : options.ApuRegisterReadColor;
} else if(reg <= 0x2183) {
}
else if (reg <= 0x2183)
{
color = isWrite ? options.WorkRamRegisterWriteColor : options.WorkRamRegisterReadColor;
} else if(reg >= 0x4000) {
}
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,9 +327,12 @@ uint32_t EventManager::TakeEventSnapshot(EventViewerDisplayOptions options)
_overscanMode = _ppu->GetState().OverscanMode;
_useHighResOutput = _ppu->IsHighResOutput();
if(scanline >= _ppu->GetNmiScanline() || scanline == 0) {
if (scanline >= _ppu->GetNmiScanline() || scanline == 0)
{
memcpy(_ppuBuffer, _ppu->GetScreenBuffer(), (_useHighResOutput ? (512 * 478) : (256 * 239)) * sizeof(uint16_t));
} else {
}
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);
@ -268,19 +351,23 @@ void EventManager::GetDisplayBuffer(uint32_t *buffer, uint32_t bufferSize, Event
{
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)));
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]);
}
@ -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);
}
}

File diff suppressed because it is too large Load diff

View file

@ -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,
@ -172,16 +174,20 @@ private:
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 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);
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);
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);

View file

@ -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;
@ -39,7 +48,8 @@ private:
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,7 +116,8 @@ public:
static bool LoadBsxFirmware(Console* console, uint8_t** prgRom, uint32_t& prgSize)
{
if(AttemptLoadBsxFirmware(prgRom, prgSize)) {
if (AttemptLoadBsxFirmware(prgRom, prgSize))
{
return true;
}
@ -105,7 +127,8 @@ public:
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) {
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;
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;
}

View file

@ -17,7 +17,9 @@ protected:
}
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)
{

View file

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

View file

@ -21,7 +21,8 @@ GameClient::GameClient(shared_ptr<Console> console)
GameClient::~GameClient()
{
_stop = true;
if(_clientThread) {
if (_clientThread)
{
_clientThread->join();
}
}
@ -38,7 +39,8 @@ void GameClient::Connect(shared_ptr<Console> console, ClientConnectionData &conn
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()));
}
@ -59,11 +61,14 @@ 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();
@ -92,7 +102,8 @@ void GameClient::ProcessNotification(ConsoleNotificationType type, void* paramet
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);
}
}

View file

@ -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,14 +79,16 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
{
GameInformationMessage* gameInfo;
switch(message->GetType()) {
switch (message->GetType())
{
case MessageType::ServerInformation:
_serverSalt = ((ServerInformationMessage*)message)->GetHashSalt();
SendHandshake();
break;
case MessageType::SaveState:
if(_gameLoaded) {
if (_gameLoaded)
{
DisableControllers();
_console->Lock();
ClearInputData();
@ -92,8 +100,10 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
break;
case MessageType::MovieData:
if(_gameLoaded) {
PushControllerState(((MovieDataMessage*)message)->GetPortNumber(), ((MovieDataMessage*)message)->GetInputState());
if (_gameLoaded)
{
PushControllerState(((MovieDataMessage*)message)->GetPortNumber(),
((MovieDataMessage*)message)->GetInputState());
}
break;
@ -109,12 +119,16 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
DisableControllers();
_console->Lock();
gameInfo = (GameInformationMessage*)message;
if(gameInfo->GetPort() != _controllerPort) {
if (gameInfo->GetPort() != _controllerPort)
{
_controllerPort = gameInfo->GetPort();
if(_controllerPort == GameConnection::SpectatorPort) {
if (_controllerPort == GameConnection::SpectatorPort)
{
MessageManager::DisplayMessage("NetPlay", "ConnectedAsSpectator");
} else {
}
else
{
MessageManager::DisplayMessage("NetPlay", "ConnectedAsPlayer", std::to_string(_controllerPort + 1));
}
}
@ -123,14 +137,20 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
_console->Unlock();
_gameLoaded = AttemptLoadGame(gameInfo->GetRomFilename(), gameInfo->GetSha1Hash());
if(!_gameLoaded) {
if (!_gameLoaded)
{
_console->Stop(true);
} else {
}
else
{
_console->GetControlManager()->UnregisterInputProvider(this);
_console->GetControlManager()->RegisterInputProvider(this);
if(gameInfo->IsPaused()) {
if (gameInfo->IsPaused())
{
_console->Pause();
} else {
}
else
{
_console->Resume();
}
}
@ -142,11 +162,15 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
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)
{
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);
}
}

View file

@ -20,8 +20,10 @@ 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;
}
}
@ -30,7 +32,8 @@ 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,7 +41,8 @@ bool GameConnection::ExtractMessage(void *buffer, uint32_t &messageLength)
int packetLength = messageLength + sizeof(messageLength);
if(_readPosition >= packetLength) {
if (_readPosition >= packetLength)
{
memcpy(buffer, _readBuffer + sizeof(messageLength), messageLength);
memmove(_readBuffer, _readBuffer + packetLength, _readPosition - packetLength);
_readPosition -= packetLength;
@ -51,10 +55,13 @@ NetMessage* GameConnection::ReadMessage()
{
ReadSocket();
if(_readPosition > 4) {
if (_readPosition > 4)
{
uint32_t messageLength;
if(ExtractMessage(_messageBuffer, messageLength)) {
switch((MessageType)_messageBuffer[0]) {
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);
@ -90,7 +97,8 @@ 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);

View file

@ -19,9 +19,12 @@ protected:
}
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;

View file

@ -34,7 +34,8 @@ GameServer::~GameServer()
Stop();
shared_ptr<ControlManager> controlManager = _console->GetControlManager();
if(controlManager) {
if (controlManager)
{
controlManager->UnregisterInputRecorder(this);
controlManager->UnregisterInputProvider(this);
}
@ -43,7 +44,8 @@ GameServer::~GameServer()
void GameServer::RegisterServerInput()
{
shared_ptr<ControlManager> controlManager = _console->GetControlManager();
if(controlManager) {
if (controlManager)
{
controlManager->RegisterInputRecorder(this);
controlManager->RegisterInputProvider(this);
}
@ -51,13 +53,17 @@ void GameServer::RegisterServerInput()
void GameServer::AcceptConnections()
{
while(true) {
while (true)
{
shared_ptr<Socket> socket = _listener->Accept();
if(!socket->ConnectionError()) {
if (!socket->ConnectionError())
{
auto connection = shared_ptr<GameServerConnection>(new GameServerConnection(_console, socket, _password));
_console->GetNotificationManager()->RegisterNotificationListener(connection);
_openConnections.push_back(connection);
} else {
}
else
{
break;
}
}
@ -67,24 +73,32 @@ void GameServer::AcceptConnections()
void GameServer::UpdateConnections()
{
vector<shared_ptr<GameServerConnection>> connectionsToRemove;
for(shared_ptr<GameServerConnection> connection : _openConnections) {
if(connection->ConnectionError()) {
for (shared_ptr<GameServerConnection> connection : _openConnections)
{
if (connection->ConnectionError())
{
connectionsToRemove.push_back(connection);
} else {
}
else
{
connection->ProcessMessages();
}
}
for(shared_ptr<GameServerConnection> gameConnection : connectionsToRemove) {
for (shared_ptr<GameServerConnection> gameConnection : connectionsToRemove)
{
_openConnections.remove(gameConnection);
}
}
list<shared_ptr<GameServerConnection>> GameServer::GetConnectionList()
{
if(GameServer::Started()) {
if (GameServer::Started())
{
return Instance->_openConnections;
} else {
}
else
{
return list<shared_ptr<GameServerConnection>>();
}
}
@ -93,22 +107,29 @@ bool GameServer::SetInput(BaseControlDevice *device)
{
uint8_t port = device->GetPort();
if(device->GetControllerType() == ControllerType::Multitap) {
if (device->GetControllerType() == ControllerType::Multitap)
{
//Need special handling for the multitap, merge data from P3/4/5 with P1 (or P2, depending which port the multitap is plugged into)
GameServerConnection* connection = GameServerConnection::GetNetPlayDevice(port);
if(connection) {
if (connection)
{
((Multitap*)device)->SetControllerState(0, connection->GetState());
}
for(int i = 2; i < 5; i++) {
for (int i = 2; i < 5; i++)
{
GameServerConnection* connection = GameServerConnection::GetNetPlayDevice(i);
if(connection) {
if (connection)
{
((Multitap*)device)->SetControllerState(i - 1, connection->GetState());
}
}
} else {
}
else
{
GameServerConnection* connection = GameServerConnection::GetNetPlayDevice(port);
if(connection) {
if (connection)
{
//Device is controlled by a client
device->SetRawState(connection->GetState());
return true;
@ -121,9 +142,12 @@ bool GameServer::SetInput(BaseControlDevice *device)
void GameServer::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
{
for(shared_ptr<BaseControlDevice> &device : devices) {
for(shared_ptr<GameServerConnection> connection : _openConnections) {
if(!connection->ConnectionError()) {
for (shared_ptr<BaseControlDevice>& device : devices)
{
for (shared_ptr<GameServerConnection> connection : _openConnections)
{
if (!connection->ConnectionError())
{
//Send movie stream
connection->SendMovieData(device->GetPort(), device->GetRawState());
}
@ -133,7 +157,8 @@ void GameServer::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
void GameServer::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
if (type == ConsoleNotificationType::GameLoaded)
{
//Register the server as an input provider/recorder
RegisterServerInput();
}
@ -148,7 +173,8 @@ void GameServer::Exec()
_initialized = true;
MessageManager::DisplayMessage("NetPlay", "ServerStarted", std::to_string(_port));
while(!_stop) {
while (!_stop)
{
AcceptConnections();
UpdateConnections();
@ -172,23 +198,28 @@ void GameServer::StartServer(shared_ptr<Console> console, uint16_t port, string
void GameServer::StopServer()
{
if(Instance) {
if (Instance)
{
Instance.reset();
}
}
bool GameServer::Started()
{
if(Instance) {
if (Instance)
{
return Instance->_initialized;
} else {
}
else
{
return false;
}
}
string GameServer::GetHostPlayerName()
{
if(GameServer::Started()) {
if (GameServer::Started())
{
return Instance->_hostPlayerName;
}
return "";
@ -196,7 +227,8 @@ string GameServer::GetHostPlayerName()
uint8_t GameServer::GetHostControllerPort()
{
if(GameServer::Started()) {
if (GameServer::Started())
{
return Instance->_hostControllerPort;
}
return GameConnection::SpectatorPort;
@ -204,9 +236,11 @@ uint8_t GameServer::GetHostControllerPort()
void GameServer::SetHostControllerPort(uint8_t port)
{
if(GameServer::Started()) {
if (GameServer::Started())
{
Instance->_console->Lock();
if(port == GameConnection::SpectatorPort || GetAvailableControllers() & (1 << port)) {
if (port == GameConnection::SpectatorPort || GetAvailableControllers() & (1 << port))
{
//Port is available
Instance->_hostControllerPort = port;
SendPlayerList();
@ -218,8 +252,10 @@ void GameServer::SetHostControllerPort(uint8_t port)
uint8_t GameServer::GetAvailableControllers()
{
uint8_t availablePorts = (1 << BaseControlDevice::PortCount) - 1;
for(PlayerInfo &playerInfo : GetPlayerList()) {
if(playerInfo.ControllerPort < BaseControlDevice::PortCount) {
for (PlayerInfo& playerInfo : GetPlayerList())
{
if (playerInfo.ControllerPort < BaseControlDevice::PortCount)
{
availablePorts &= ~(1 << playerInfo.ControllerPort);
}
}
@ -236,7 +272,8 @@ vector<PlayerInfo> GameServer::GetPlayerList()
playerInfo.IsHost = true;
playerList.push_back(playerInfo);
for(shared_ptr<GameServerConnection> &connection : GetConnectionList()) {
for (shared_ptr<GameServerConnection>& connection : GetConnectionList())
{
playerInfo.Name = connection->GetPlayerName();
playerInfo.ControllerPort = connection->GetControllerPort();
playerInfo.IsHost = false;
@ -250,7 +287,8 @@ void GameServer::SendPlayerList()
{
vector<PlayerInfo> playerList = GetPlayerList();
for(shared_ptr<GameServerConnection> &connection : GetConnectionList()) {
for (shared_ptr<GameServerConnection>& connection : GetConnectionList())
{
//Send player list update to all connections
PlayerListMessage message(playerList);
connection->SendNetMessage(message);

View file

@ -21,7 +21,8 @@
GameServerConnection* GameServerConnection::_netPlayDevices[BaseControlDevice::PortCount] = {};
GameServerConnection::GameServerConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, string serverPassword) : GameConnection(console, socket)
GameServerConnection::GameServerConnection(shared_ptr<Console> console, shared_ptr<Socket> socket,
string serverPassword) : GameConnection(console, socket)
{
//Server-side connection
_serverPassword = serverPassword;
@ -31,8 +32,10 @@ GameServerConnection::GameServerConnection(shared_ptr<Console> console, shared_p
GameServerConnection::~GameServerConnection()
{
if(!_playerName.empty()) {
MessageManager::DisplayMessage("NetPlay", _playerName + " (Player " + std::to_string(_controllerPort + 1) + ") disconnected.");
if (!_playerName.empty())
{
MessageManager::DisplayMessage(
"NetPlay", _playerName + " (Player " + std::to_string(_controllerPort + 1) + ") disconnected.");
}
UnregisterNetPlayDevice(this);
@ -44,7 +47,8 @@ void GameServerConnection::SendServerInformation()
std::mt19937 engine(rd());
std::uniform_int_distribution<> dist((int)' ', (int)'~');
string hash(50, ' ');
for(int i = 0; i < 50; i++) {
for (int i = 0; i < 50; i++)
{
int random = dist(engine);
hash[i] = (char)random;
}
@ -59,7 +63,8 @@ void GameServerConnection::SendGameInformation()
{
_console->Lock();
RomInfo romInfo = _console->GetRomInfo();
GameInformationMessage gameInfo(romInfo.RomFile.GetFileName(), _console->GetCartridge()->GetSha1Hash(), _controllerPort, _console->IsPaused());
GameInformationMessage gameInfo(romInfo.RomFile.GetFileName(), _console->GetCartridge()->GetSha1Hash(),
_controllerPort, _console->IsPaused());
SendNetMessage(gameInfo);
SaveStateMessage saveState(_console);
SendNetMessage(saveState);
@ -68,7 +73,8 @@ void GameServerConnection::SendGameInformation()
void GameServerConnection::SendMovieData(uint8_t port, ControlDeviceState state)
{
if(_handshakeCompleted) {
if (_handshakeCompleted)
{
MovieDataMessage message(state, port);
SendNetMessage(message);
}
@ -83,7 +89,8 @@ void GameServerConnection::SendForceDisconnectMessage(string disconnectMessage)
void GameServerConnection::PushState(ControlDeviceState state)
{
if(_inputData.size() == 0 || state != _inputData.back()) {
if (_inputData.size() == 0 || state != _inputData.back())
{
_inputData.clear();
_inputData.push_back(state);
}
@ -93,9 +100,11 @@ ControlDeviceState GameServerConnection::GetState()
{
size_t inputBufferSize = _inputData.size();
ControlDeviceState stateData;
if(inputBufferSize > 0) {
if (inputBufferSize > 0)
{
stateData = _inputData.front();
if(inputBufferSize > 1) {
if (inputBufferSize > 1)
{
//Always keep the last one the client sent, it will be used until a new one is received
_inputData.pop_front();
}
@ -106,18 +115,23 @@ ControlDeviceState GameServerConnection::GetState()
void GameServerConnection::ProcessHandshakeResponse(HandShakeMessage* message)
{
//Send the game's current state to the client and register the controller
if(message->IsValid(_console->GetSettings()->GetVersion())) {
if(message->CheckPassword(_serverPassword, _connectionHash)) {
if (message->IsValid(_console->GetSettings()->GetVersion()))
{
if (message->CheckPassword(_serverPassword, _connectionHash))
{
_console->Lock();
_controllerPort = message->IsSpectator() ? GameConnection::SpectatorPort : GetFirstFreeControllerPort();
_playerName = message->GetPlayerName();
string playerPortMessage = _controllerPort == GameConnection::SpectatorPort ? "Spectator" : "Player " + std::to_string(_controllerPort + 1);
string playerPortMessage = _controllerPort == GameConnection::SpectatorPort
? "Spectator"
: "Player " + std::to_string(_controllerPort + 1);
MessageManager::DisplayMessage("NetPlay", _playerName + " (" + playerPortMessage + ") connected.");
if(_console->GetCartridge()) {
if (_console->GetCartridge())
{
SendGameInformation();
}
@ -125,24 +139,32 @@ void GameServerConnection::ProcessHandshakeResponse(HandShakeMessage* message)
RegisterNetPlayDevice(this, _controllerPort);
GameServer::SendPlayerList();
_console->Unlock();
} else {
}
else
{
SendForceDisconnectMessage("The password you provided did not match - you have been disconnected.");
}
} else {
SendForceDisconnectMessage("Server is using a different version of Mesen-S (" + _console->GetSettings()->GetVersionString() + ") - you have been disconnected.");
}
else
{
SendForceDisconnectMessage(
"Server is using a different version of Mesen-S (" + _console->GetSettings()->GetVersionString() +
") - you have been disconnected.");
MessageManager::DisplayMessage("NetPlay", + "NetplayVersionMismatch", message->GetPlayerName());
}
}
void GameServerConnection::ProcessMessage(NetMessage* message)
{
switch(message->GetType()) {
switch (message->GetType())
{
case MessageType::HandShake:
ProcessHandshakeResponse((HandShakeMessage*)message);
break;
case MessageType::InputData:
if(!_handshakeCompleted) {
if (!_handshakeCompleted)
{
SendForceDisconnectMessage("Handshake has not been completed - invalid packet");
return;
}
@ -150,7 +172,8 @@ void GameServerConnection::ProcessMessage(NetMessage* message)
break;
case MessageType::SelectController:
if(!_handshakeCompleted) {
if (!_handshakeCompleted)
{
SendForceDisconnectMessage("Handshake has not been completed - invalid packet");
return;
}
@ -165,20 +188,28 @@ void GameServerConnection::ProcessMessage(NetMessage* message)
void GameServerConnection::SelectControllerPort(uint8_t port)
{
_console->Lock();
if(port == GameConnection::SpectatorPort) {
if (port == GameConnection::SpectatorPort)
{
//Client wants to be a spectator, make sure we are not using any controller
UnregisterNetPlayDevice(this);
_controllerPort = port;
} else {
}
else
{
GameServerConnection* netPlayDevice = GetNetPlayDevice(port);
if(netPlayDevice == this) {
if (netPlayDevice == this)
{
//Nothing to do, we're already this player
} else if(netPlayDevice == nullptr) {
}
else if (netPlayDevice == nullptr)
{
//This port is free, we can switch
UnregisterNetPlayDevice(this);
RegisterNetPlayDevice(this, port);
_controllerPort = port;
} else {
}
else
{
//Another player is using this port, we can't use it
}
}
@ -189,7 +220,8 @@ void GameServerConnection::SelectControllerPort(uint8_t port)
void GameServerConnection::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
switch(type) {
switch (type)
{
case ConsoleNotificationType::GamePaused:
case ConsoleNotificationType::GameLoaded:
case ConsoleNotificationType::GameResumed:
@ -200,7 +232,8 @@ void GameServerConnection::ProcessNotification(ConsoleNotificationType type, voi
SendGameInformation();
break;
case ConsoleNotificationType::BeforeEmulationStop: {
case ConsoleNotificationType::BeforeEmulationStop:
{
//Make clients unload the current game
GameInformationMessage gameInfo("", "0000000000000000000000000000000000000000", _controllerPort, true);
SendNetMessage(gameInfo);
@ -219,9 +252,12 @@ void GameServerConnection::RegisterNetPlayDevice(GameServerConnection* device, u
void GameServerConnection::UnregisterNetPlayDevice(GameServerConnection* device)
{
if(device != nullptr) {
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
if(GameServerConnection::_netPlayDevices[i] == device) {
if (device != nullptr)
{
for (int i = 0; i < BaseControlDevice::PortCount; i++)
{
if (GameServerConnection::_netPlayDevices[i] == device)
{
GameServerConnection::_netPlayDevices[i] = nullptr;
break;
}
@ -237,8 +273,10 @@ GameServerConnection* GameServerConnection::GetNetPlayDevice(uint8_t port)
uint8_t GameServerConnection::GetFirstFreeControllerPort()
{
uint8_t hostPost = GameServer::GetHostControllerPort();
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
if(hostPost != i && GameServerConnection::_netPlayDevices[i] == nullptr) {
for (int i = 0; i < BaseControlDevice::PortCount; i++)
{
if (hostPost != i && GameServerConnection::_netPlayDevices[i] == nullptr)
{
return i;
}
}

View file

@ -31,22 +31,30 @@ Gameboy* Gameboy::Create(Console* console, VirtualFile &romFile, bool sgbEnabled
MessageManager::Log("File: " + romFile.GetFileName());
MessageManager::Log("Game: " + header.GetCartName());
MessageManager::Log("Cart Type: " + std::to_string(header.CartType));
switch(header.CgbFlag & 0xC0) {
case 0x00: MessageManager::Log("Supports: Game Boy"); break;
case 0x80: MessageManager::Log("Supports: Game Boy Color (compatible with GB)"); break;
case 0xC0: MessageManager::Log("Supports: Game Boy Color only"); break;
switch (header.CgbFlag & 0xC0)
{
case 0x00: MessageManager::Log("Supports: Game Boy");
break;
case 0x80: MessageManager::Log("Supports: Game Boy Color (compatible with GB)");
break;
case 0xC0: MessageManager::Log("Supports: Game Boy Color only");
break;
}
MessageManager::Log("File size: " + std::to_string(romData.size() / 1024) + " KB");
if(header.GetCartRamSize() > 0) {
string sizeString = header.GetCartRamSize() > 1024 ? std::to_string(header.GetCartRamSize() / 1024) + " KB" : std::to_string(header.GetCartRamSize()) + " bytes";
if (header.GetCartRamSize() > 0)
{
string sizeString = header.GetCartRamSize() > 1024
? std::to_string(header.GetCartRamSize() / 1024) + " KB"
: std::to_string(header.GetCartRamSize()) + " bytes";
MessageManager::Log("Cart RAM size: " + sizeString + (header.HasBattery() ? " (with battery)" : ""));
}
MessageManager::Log("-----------------------------");
GbCart* cart = GbCartFactory::CreateCart(header.CartType);
if(cart) {
if (cart)
{
Gameboy* gb = new Gameboy();
gb->Init(console, cart, romData, header, sgbEnabled);
return gb;
@ -55,7 +63,8 @@ Gameboy* Gameboy::Create(Console* console, VirtualFile &romFile, bool sgbEnabled
return nullptr;
}
void Gameboy::Init(Console* console, GbCart* cart, std::vector<uint8_t>& romData, GameboyHeader& header, bool sgbEnabled)
void Gameboy::Init(Console* console, GbCart* cart, std::vector<uint8_t>& romData, GameboyHeader& header,
bool sgbEnabled)
{
_console = console;
_cart.reset(cart);
@ -71,15 +80,20 @@ void Gameboy::Init(Console* console, GbCart* cart, std::vector<uint8_t>& romData
shared_ptr<EmuSettings> settings = console->GetSettings();
GameboyConfig cfg = settings->GetGameboyConfig();
GameboyModel model = cfg.Model;
if(model == GameboyModel::Auto) {
if((header.CgbFlag & 0x80) != 0) {
if (model == GameboyModel::Auto)
{
if ((header.CgbFlag & 0x80) != 0)
{
model = GameboyModel::GameboyColor;
} else {
}
else
{
model = GameboyModel::SuperGameboy;
}
}
if(!sgbEnabled && model == GameboyModel::SuperGameboy) {
if (!sgbEnabled && model == GameboyModel::SuperGameboy)
{
//SGB bios isn't available, use gameboy color mode instead
model = GameboyModel::GameboyColor;
}
@ -98,15 +112,20 @@ void Gameboy::Init(Console* console, GbCart* cart, std::vector<uint8_t>& romData
_bootRomSize = 0;
FirmwareType type = FirmwareType::Gameboy;
if(_model == GameboyModel::SuperGameboy) {
if (_model == GameboyModel::SuperGameboy)
{
type = cfg.UseSgb2 ? FirmwareType::Sgb2GameboyCpu : FirmwareType::Sgb1GameboyCpu;
} else if(_model == GameboyModel::GameboyColor) {
}
else if (_model == GameboyModel::GameboyColor)
{
type = FirmwareType::GameboyColor;
}
_bootRomSize = cgbMode ? 9 * 256 : 256;
if(!FirmwareHelper::LoadGbBootRom(console, &_bootRom, type)) {
switch(_model) {
if (!FirmwareHelper::LoadGbBootRom(console, &_bootRom, type))
{
switch (_model)
{
default:
case GameboyModel::Gameboy:
_bootRom = new uint8_t[_bootRomSize];
@ -120,9 +139,12 @@ void Gameboy::Init(Console* console, GbCart* cart, std::vector<uint8_t>& romData
case GameboyModel::SuperGameboy:
_bootRom = new uint8_t[_bootRomSize];
if(cfg.UseSgb2) {
if (cfg.UseSgb2)
{
memcpy(_bootRom, sgb2BootRom, _bootRomSize);
} else {
}
else
{
memcpy(_bootRom, sgbBootRom, _bootRomSize);
}
break;
@ -184,21 +206,24 @@ void Gameboy::Exec()
void Gameboy::Run(uint64_t runUntilClock)
{
while(_memoryManager->GetCycleCount() < runUntilClock) {
while (_memoryManager->GetCycleCount() < runUntilClock)
{
_cpu->Exec();
}
}
void Gameboy::LoadBattery()
{
if(_hasBattery) {
if (_hasBattery)
{
_console->GetBatteryManager()->LoadBattery(".srm", _cartRam, _cartRamSize);
}
}
void Gameboy::SaveBattery()
{
if(_hasBattery) {
if (_hasBattery)
{
_console->GetBatteryManager()->SaveBattery(".srm", _cartRam, _cartRamSize);
}
}
@ -219,7 +244,8 @@ GbState Gameboy::GetState()
uint32_t Gameboy::DebugGetMemorySize(SnesMemoryType type)
{
switch(type) {
switch (type)
{
case SnesMemoryType::GbPrgRom: return _prgRomSize;
case SnesMemoryType::GbWorkRam: return _workRamSize;
case SnesMemoryType::GbCartRam: return _cartRamSize;
@ -233,7 +259,8 @@ uint32_t Gameboy::DebugGetMemorySize(SnesMemoryType type)
uint8_t* Gameboy::DebugGetMemory(SnesMemoryType type)
{
switch(type) {
switch (type)
{
case SnesMemoryType::GbPrgRom: return _prgRom;
case SnesMemoryType::GbWorkRam: return _workRam;
case SnesMemoryType::GbCartRam: return _cartRam;
@ -269,7 +296,8 @@ AddressInfo Gameboy::GetAbsoluteAddress(uint16_t addr)
{
AddressInfo addrInfo = {-1, SnesMemoryType::Register};
if(addr >= 0xFF80 && addr <= 0xFFFE) {
if (addr >= 0xFF80 && addr <= 0xFFFE)
{
addrInfo.Address = addr & 0x7F;
addrInfo.Type = SnesMemoryType::GbHighRam;
return addrInfo;
@ -277,22 +305,30 @@ AddressInfo Gameboy::GetAbsoluteAddress(uint16_t addr)
uint8_t* ptr = _memoryManager->GetMappedBlock(addr);
if(!ptr) {
if (!ptr)
{
return addrInfo;
}
ptr += (addr & 0xFF);
if(ptr >= _prgRom && ptr < _prgRom + _prgRomSize) {
if (ptr >= _prgRom && ptr < _prgRom + _prgRomSize)
{
addrInfo.Address = (int32_t)(ptr - _prgRom);
addrInfo.Type = SnesMemoryType::GbPrgRom;
} else if(ptr >= _workRam && ptr < _workRam + _workRamSize) {
}
else if (ptr >= _workRam && ptr < _workRam + _workRamSize)
{
addrInfo.Address = (int32_t)(ptr - _workRam);
addrInfo.Type = SnesMemoryType::GbWorkRam;
} else if(ptr >= _cartRam && ptr < _cartRam + _cartRamSize) {
}
else if (ptr >= _cartRam && ptr < _cartRam + _cartRamSize)
{
addrInfo.Address = (int32_t)(ptr - _cartRam);
addrInfo.Type = SnesMemoryType::GbCartRam;
} else if(ptr >= _bootRom && ptr < _bootRom + _bootRomSize) {
}
else if (ptr >= _bootRom && ptr < _bootRom + _bootRomSize)
{
addrInfo.Address = (int32_t)(ptr - _bootRom);
addrInfo.Type = SnesMemoryType::GbBootRom;
}
@ -301,13 +337,16 @@ AddressInfo Gameboy::GetAbsoluteAddress(uint16_t addr)
int32_t Gameboy::GetRelativeAddress(AddressInfo& absAddress)
{
if(absAddress.Type == SnesMemoryType::GbHighRam) {
if (absAddress.Type == SnesMemoryType::GbHighRam)
{
return 0xFF80 | (absAddress.Address & 0x7F);
}
for(int32_t i = 0; i < 0x10000; i += 0x100) {
for (int32_t i = 0; i < 0x10000; i += 0x100)
{
AddressInfo blockAddr = GetAbsoluteAddress(i);
if(blockAddr.Type == absAddress.Type && (blockAddr.Address & ~0xFF) == (absAddress.Address & ~0xFF)) {
if (blockAddr.Type == absAddress.Type && (blockAddr.Address & ~0xFF) == (absAddress.Address & ~0xFF))
{
return i | (absAddress.Address & 0xFF);
}
}

View file

@ -8,41 +8,73 @@
#include"../Utilities/HexUtilities.h"
constexpr const char* _opTemplate[256] = {
"NOP", "LD BC, e", "LD (BC), A", "INC BC", "INC B", "DEC B", "LD B, d", "RLCA", "LD (a), SP", "ADD HL, BC", "LD A, (BC)", "DEC BC", "INC C", "DEC C", "LD C, d", "RRCA",
"STOP", "LD DE, e", "LD (DE), A", "INC DE", "INC D", "DEC D", "LD D, d", "RLA", "JR r", "ADD HL, DE", "LD A, (DE)", "DEC DE", "INC E", "DEC E", "LD E, d", "RRA",
"JR NZ, r", "LD HL, e", "LD (HL+), A", "INC HL", "INC H", "DEC H", "LD H, d", "DAA", "JR Z, r", "ADD HL, HL", "LD A, (HL+)", "DEC HL", "INC L", "DEC L", "LD L, d", "CPL",
"JR NC, r", "LD SP, e", "LD (HL-), A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL), d", "SCF", "JR C, r", "ADD HL, SP", "LD A, (HL-)", "DEC SP", "INC A", "DEC A", "LD A, d", "CCF",
"LD B, B", "LD B, C", "LD B, D", "LD B, E", "LD B, H", "LD B, L", "LD B, (HL)", "LD B, A", "LD C, B", "LD C, C", "LD C, D", "LD C, E", "LD C, H", "LD C, L", "LD C, (HL)", "LD C, A",
"LD D, B", "LD D, C", "LD D, D", "LD D, E", "LD D, H", "LD D, L", "LD D, (HL)", "LD D, A", "LD E, B", "LD E, C", "LD E, D", "LD E, E", "LD E, H", "LD E, L", "LD E, (HL)", "LD E, A",
"LD H, B", "LD H, C", "LD H, D", "LD H, E", "LD H, H", "LD H, L", "LD H, (HL)", "LD H, A", "LD L, B", "LD L, C", "LD L, D", "LD L, E", "LD L, H", "LD L, L", "LD L, (HL)", "LD L, A",
"LD (HL), B", "LD (HL), C", "LD (HL), D", "LD (HL), E","LD (HL), H", "LD (HL), L", "HALT", "LD (HL), A","LD A, B", "LD A, C", "LD A, D", "LD A, E", "LD A, H", "LD A, L", "LD A, (HL)", "LD A, A",
"ADD A, B", "ADD A, C", "ADD A, D", "ADD A, E", "ADD A, H", "ADD A, L", "ADD A, (HL)", "ADD A, A", "ADC A, B", "ADC A, C", "ADC A, D", "ADC A, E", "ADC A, H", "ADC A, L", "ADC A, (HL)", "ADC A, A",
"SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", "SBC A, B", "SBC A, C", "SBC A, D", "SBC A, E", "SBC A, H", "SBC A, L", "SBC A, (HL)", "SBC A, A",
"AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", "XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A",
"OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", "CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP (HL)", "CP A",
"RET NZ", "POP BC", "JP NZ, a", "JP a", "CALL NZ, a", "PUSH BC", "ADD A, d", "RST 00H", "RET Z", "RET", "JP Z, a", "PREFIX", "CALL Z, a","CALL a", "ADC A, d", "RST 08H",
"RET NC", "POP DE", "JP NC, a", "ILL_D3", "CALL NC, a", "PUSH DE", "SUB d", "RST 10H", "RET C", "RETI", "JP C, a", "ILL_DB", "CALL C, a","ILL_DD", "SBC A, d", "RST 18H",
"LDH (c), A", "POP HL", "LD ($FF00+C), A","ILL_E3","ILL_E4", "PUSH HL", "AND d", "RST 20H", "ADD SP, d", "JP HL", "LD (a), A", "ILL_EB", "ILL_EC", "ILL_ED", "XOR d", "RST 28H",
"LDH A, (c)", "POP AF", "LD A, ($FF00+C)","DI", "ILL_F4", "PUSH AF", "OR d", "RST 30H", "LD HL, SP+d", "LD SP, HL", "LD A, (a)", "EI", "ILL_FC", "ILL_FD", "CP d", "RST 38H"
"NOP", "LD BC, e", "LD (BC), A", "INC BC", "INC B", "DEC B", "LD B, d", "RLCA", "LD (a), SP", "ADD HL, BC",
"LD A, (BC)", "DEC BC", "INC C", "DEC C", "LD C, d", "RRCA",
"STOP", "LD DE, e", "LD (DE), A", "INC DE", "INC D", "DEC D", "LD D, d", "RLA", "JR r", "ADD HL, DE", "LD A, (DE)",
"DEC DE", "INC E", "DEC E", "LD E, d", "RRA",
"JR NZ, r", "LD HL, e", "LD (HL+), A", "INC HL", "INC H", "DEC H", "LD H, d", "DAA", "JR Z, r", "ADD HL, HL",
"LD A, (HL+)", "DEC HL", "INC L", "DEC L", "LD L, d", "CPL",
"JR NC, r", "LD SP, e", "LD (HL-), A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL), d", "SCF", "JR C, r",
"ADD HL, SP", "LD A, (HL-)", "DEC SP", "INC A", "DEC A", "LD A, d", "CCF",
"LD B, B", "LD B, C", "LD B, D", "LD B, E", "LD B, H", "LD B, L", "LD B, (HL)", "LD B, A", "LD C, B", "LD C, C",
"LD C, D", "LD C, E", "LD C, H", "LD C, L", "LD C, (HL)", "LD C, A",
"LD D, B", "LD D, C", "LD D, D", "LD D, E", "LD D, H", "LD D, L", "LD D, (HL)", "LD D, A", "LD E, B", "LD E, C",
"LD E, D", "LD E, E", "LD E, H", "LD E, L", "LD E, (HL)", "LD E, A",
"LD H, B", "LD H, C", "LD H, D", "LD H, E", "LD H, H", "LD H, L", "LD H, (HL)", "LD H, A", "LD L, B", "LD L, C",
"LD L, D", "LD L, E", "LD L, H", "LD L, L", "LD L, (HL)", "LD L, A",
"LD (HL), B", "LD (HL), C", "LD (HL), D", "LD (HL), E", "LD (HL), H", "LD (HL), L", "HALT", "LD (HL), A", "LD A, B",
"LD A, C", "LD A, D", "LD A, E", "LD A, H", "LD A, L", "LD A, (HL)", "LD A, A",
"ADD A, B", "ADD A, C", "ADD A, D", "ADD A, E", "ADD A, H", "ADD A, L", "ADD A, (HL)", "ADD A, A", "ADC A, B",
"ADC A, C", "ADC A, D", "ADC A, E", "ADC A, H", "ADC A, L", "ADC A, (HL)", "ADC A, A",
"SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", "SBC A, B", "SBC A, C", "SBC A, D",
"SBC A, E", "SBC A, H", "SBC A, L", "SBC A, (HL)", "SBC A, A",
"AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", "XOR B", "XOR C", "XOR D", "XOR E",
"XOR H", "XOR L", "XOR (HL)", "XOR A",
"OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", "CP B", "CP C", "CP D", "CP E", "CP H", "CP L",
"CP (HL)", "CP A",
"RET NZ", "POP BC", "JP NZ, a", "JP a", "CALL NZ, a", "PUSH BC", "ADD A, d", "RST 00H", "RET Z", "RET", "JP Z, a",
"PREFIX", "CALL Z, a", "CALL a", "ADC A, d", "RST 08H",
"RET NC", "POP DE", "JP NC, a", "ILL_D3", "CALL NC, a", "PUSH DE", "SUB d", "RST 10H", "RET C", "RETI", "JP C, a",
"ILL_DB", "CALL C, a", "ILL_DD", "SBC A, d", "RST 18H",
"LDH (c), A", "POP HL", "LD ($FF00+C), A", "ILL_E3", "ILL_E4", "PUSH HL", "AND d", "RST 20H", "ADD SP, d", "JP HL",
"LD (a), A", "ILL_EB", "ILL_EC", "ILL_ED", "XOR d", "RST 28H",
"LDH A, (c)", "POP AF", "LD A, ($FF00+C)", "DI", "ILL_F4", "PUSH AF", "OR d", "RST 30H", "LD HL, SP+d", "LD SP, HL",
"LD A, (a)", "EI", "ILL_FC", "ILL_FD", "CP d", "RST 38H"
};
constexpr const char* _cbTemplate[256] = {
"RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (HL)", "RRC A",
"RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (HL)", "RR A",
"SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (HL)", "SRA A",
"SWAP B", "SWAP C", "SWAP D", "SWAP E", "SWAP H", "SWAP L", "SWAP (HL)", "SWAP A", "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A",
"BIT 0, B", "BIT 0, C", "BIT 0, D", "BIT 0, E", "BIT 0, H", "BIT 0, L", "BIT 0, (HL)", "BIT 0, A", "BIT 1, B", "BIT 1, C", "BIT 1, D", "BIT 1, E", "BIT 1, H", "BIT 1, L", "BIT 1, (HL)", "BIT 1, A",
"BIT 2, B", "BIT 2, C", "BIT 2, D", "BIT 2, E", "BIT 2, H", "BIT 2, L", "BIT 2, (HL)", "BIT 2, A", "BIT 3, B", "BIT 3, C", "BIT 3, D", "BIT 3, E", "BIT 3, H", "BIT 3, L", "BIT 3, (HL)", "BIT 3, A",
"BIT 4, B", "BIT 4, C", "BIT 4, D", "BIT 4, E", "BIT 4, H", "BIT 4, L", "BIT 4, (HL)", "BIT 4, A", "BIT 5, B", "BIT 5, C", "BIT 5, D", "BIT 5, E", "BIT 5, H", "BIT 5, L", "BIT 5, (HL)", "BIT 5, A",
"BIT 6, B", "BIT 6, C", "BIT 6, D", "BIT 6, E", "BIT 6, H", "BIT 6, L", "BIT 6, (HL)", "BIT 6, A", "BIT 7, B", "BIT 7, C", "BIT 7, D", "BIT 7, E", "BIT 7, H", "BIT 7, L", "BIT 7, (HL)", "BIT 7, A",
"RES 0, B", "RES 0, C", "RES 0, D", "RES 0, E", "RES 0, H", "RES 0, L", "RES 0, (HL)", "RES 0, A", "RES 1, B", "RES 1, C", "RES 1, D", "RES 1, E", "RES 1, H", "RES 1, L", "RES 1, (HL)", "RES 1, A",
"RES 2, B", "RES 2, C", "RES 2, D", "RES 2, E", "RES 2, H", "RES 2, L", "RES 2, (HL)", "RES 2, A", "RES 3, B", "RES 3, C", "RES 3, D", "RES 3, E", "RES 3, H", "RES 3, L", "RES 3, (HL)", "RES 3, A",
"RES 4, B", "RES 4, C", "RES 4, D", "RES 4, E", "RES 4, H", "RES 4, L", "RES 4, (HL)", "RES 4, A", "RES 5, B", "RES 5, C", "RES 5, D", "RES 5, E", "RES 5, H", "RES 5, L", "RES 5, (HL)", "RES 5, A",
"RES 6, B", "RES 6, C", "RES 6, D", "RES 6, E", "RES 6, H", "RES 6, L", "RES 6, (HL)", "RES 6, A", "RES 7, B", "RES 7, C", "RES 7, D", "RES 7, E", "RES 7, H", "RES 7, L", "RES 7, (HL)", "RES 7, A",
"SET 0, B", "SET 0, C", "SET 0, D", "SET 0, E", "SET 0, H", "SET 0, L", "SET 0, (HL)", "SET 0, A", "SET 1, B", "SET 1, C", "SET 1, D", "SET 1, E", "SET 1, H", "SET 1, L", "SET 1, (HL)", "SET 1, A",
"SET 2, B", "SET 2, C", "SET 2, D", "SET 2, E", "SET 2, H", "SET 2, L", "SET 2, (HL)", "SET 2, A", "SET 3, B", "SET 3, C", "SET 3, D", "SET 3, E", "SET 3, H", "SET 3, L", "SET 3, (HL)", "SET 3, A",
"SET 4, B", "SET 4, C", "SET 4, D", "SET 4, E", "SET 4, H", "SET 4, L", "SET 4, (HL)", "SET 4, A", "SET 5, B", "SET 5, C", "SET 5, D", "SET 5, E", "SET 5, H", "SET 5, L", "SET 5, (HL)", "SET 5, A",
"SET 6, B", "SET 6, C", "SET 6, D", "SET 6, E", "SET 6, H", "SET 6, L", "SET 6, (HL)", "SET 6, A", "SET 7, B", "SET 7, C", "SET 7, D", "SET 7, E", "SET 7, H", "SET 7, L", "SET 7, (HL)", "SET 7, A",
"RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", "RRC B", "RRC C", "RRC D", "RRC E",
"RRC H", "RRC L", "RRC (HL)", "RRC A",
"RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", "RR B", "RR C", "RR D", "RR E", "RR H", "RR L",
"RR (HL)", "RR A",
"SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", "SRA B", "SRA C", "SRA D", "SRA E",
"SRA H", "SRA L", "SRA (HL)", "SRA A",
"SWAP B", "SWAP C", "SWAP D", "SWAP E", "SWAP H", "SWAP L", "SWAP (HL)", "SWAP A", "SRL B", "SRL C", "SRL D",
"SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A",
"BIT 0, B", "BIT 0, C", "BIT 0, D", "BIT 0, E", "BIT 0, H", "BIT 0, L", "BIT 0, (HL)", "BIT 0, A", "BIT 1, B",
"BIT 1, C", "BIT 1, D", "BIT 1, E", "BIT 1, H", "BIT 1, L", "BIT 1, (HL)", "BIT 1, A",
"BIT 2, B", "BIT 2, C", "BIT 2, D", "BIT 2, E", "BIT 2, H", "BIT 2, L", "BIT 2, (HL)", "BIT 2, A", "BIT 3, B",
"BIT 3, C", "BIT 3, D", "BIT 3, E", "BIT 3, H", "BIT 3, L", "BIT 3, (HL)", "BIT 3, A",
"BIT 4, B", "BIT 4, C", "BIT 4, D", "BIT 4, E", "BIT 4, H", "BIT 4, L", "BIT 4, (HL)", "BIT 4, A", "BIT 5, B",
"BIT 5, C", "BIT 5, D", "BIT 5, E", "BIT 5, H", "BIT 5, L", "BIT 5, (HL)", "BIT 5, A",
"BIT 6, B", "BIT 6, C", "BIT 6, D", "BIT 6, E", "BIT 6, H", "BIT 6, L", "BIT 6, (HL)", "BIT 6, A", "BIT 7, B",
"BIT 7, C", "BIT 7, D", "BIT 7, E", "BIT 7, H", "BIT 7, L", "BIT 7, (HL)", "BIT 7, A",
"RES 0, B", "RES 0, C", "RES 0, D", "RES 0, E", "RES 0, H", "RES 0, L", "RES 0, (HL)", "RES 0, A", "RES 1, B",
"RES 1, C", "RES 1, D", "RES 1, E", "RES 1, H", "RES 1, L", "RES 1, (HL)", "RES 1, A",
"RES 2, B", "RES 2, C", "RES 2, D", "RES 2, E", "RES 2, H", "RES 2, L", "RES 2, (HL)", "RES 2, A", "RES 3, B",
"RES 3, C", "RES 3, D", "RES 3, E", "RES 3, H", "RES 3, L", "RES 3, (HL)", "RES 3, A",
"RES 4, B", "RES 4, C", "RES 4, D", "RES 4, E", "RES 4, H", "RES 4, L", "RES 4, (HL)", "RES 4, A", "RES 5, B",
"RES 5, C", "RES 5, D", "RES 5, E", "RES 5, H", "RES 5, L", "RES 5, (HL)", "RES 5, A",
"RES 6, B", "RES 6, C", "RES 6, D", "RES 6, E", "RES 6, H", "RES 6, L", "RES 6, (HL)", "RES 6, A", "RES 7, B",
"RES 7, C", "RES 7, D", "RES 7, E", "RES 7, H", "RES 7, L", "RES 7, (HL)", "RES 7, A",
"SET 0, B", "SET 0, C", "SET 0, D", "SET 0, E", "SET 0, H", "SET 0, L", "SET 0, (HL)", "SET 0, A", "SET 1, B",
"SET 1, C", "SET 1, D", "SET 1, E", "SET 1, H", "SET 1, L", "SET 1, (HL)", "SET 1, A",
"SET 2, B", "SET 2, C", "SET 2, D", "SET 2, E", "SET 2, H", "SET 2, L", "SET 2, (HL)", "SET 2, A", "SET 3, B",
"SET 3, C", "SET 3, D", "SET 3, E", "SET 3, H", "SET 3, L", "SET 3, (HL)", "SET 3, A",
"SET 4, B", "SET 4, C", "SET 4, D", "SET 4, E", "SET 4, H", "SET 4, L", "SET 4, (HL)", "SET 4, A", "SET 5, B",
"SET 5, C", "SET 5, D", "SET 5, E", "SET 5, H", "SET 5, L", "SET 5, (HL)", "SET 5, A",
"SET 6, B", "SET 6, C", "SET 6, D", "SET 6, E", "SET 6, H", "SET 6, L", "SET 6, (HL)", "SET 6, A", "SET 7, B",
"SET 7, C", "SET 7, D", "SET 7, E", "SET 7, H", "SET 7, L", "SET 7, (HL)", "SET 7, A",
};
constexpr const uint8_t _opSize[256] = {
@ -75,60 +107,106 @@ enum class AddrType : uint8_t
};
static constexpr const AddrType _gbEffAddrType[256] = {
AddrType::None,AddrType::None,AddrType::BC, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::BC, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,
AddrType::None,AddrType::None,AddrType::DE, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::DE, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,
AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,
AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::HL, AddrType::HL, AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,
AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,
AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,
AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,
AddrType::HL, AddrType::HL, AddrType::HL, AddrType::HL, AddrType::HL, AddrType::HL, AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,
AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,
AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,
AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,
AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::HL, AddrType::None,
AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::Suff,AddrType::None,AddrType::None,AddrType::None,AddrType::None,
AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,
AddrType::None,AddrType::None,AddrType::C,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,
AddrType::None,AddrType::None,AddrType::C,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None,AddrType::None
AddrType::None, AddrType::None, AddrType::BC, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::BC, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::DE, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::DE, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::HL, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::HL, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::HL, AddrType::None, AddrType::HL, AddrType::HL, AddrType::HL,
AddrType::None, AddrType::None, AddrType::None, AddrType::HL, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::HL,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::HL, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::HL,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::HL, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::HL,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::HL, AddrType::None,
AddrType::HL, AddrType::HL, AddrType::HL, AddrType::HL, AddrType::HL, AddrType::HL, AddrType::None, AddrType::HL,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::HL,
AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::HL,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::HL, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::HL,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::HL, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::HL,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::HL, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::HL,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::HL, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::Suff, AddrType::None, AddrType::None,
AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::C, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::C, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None, AddrType::None,
AddrType::None, AddrType::None
};
void GameboyDisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings)
void GameboyDisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr,
LabelManager* labelManager, EmuSettings* settings)
{
FastString str(settings->CheckDebuggerFlag(DebuggerFlags::UseLowerCaseDisassembly));
AddressInfo addrInfo{0, SnesMemoryType::GameboyMemory};
auto getOperand = [&str, &addrInfo, labelManager](uint16_t addr) {
auto getOperand = [&str, &addrInfo, labelManager](uint16_t addr)
{
addrInfo.Address = addr;
string label = labelManager ? labelManager->GetLabel(addrInfo) : "";
if(label.empty()) {
if (label.empty())
{
str.WriteAll('$', HexUtilities::ToHex(addr));
} else {
}
else
{
str.Write(label, true);
}
};
uint8_t* byteCode = info.GetByteCode();
const char* op = byteCode[0] == 0xCB ? _cbTemplate[byteCode[1]] : _opTemplate[byteCode[0]];
if(byteCode[0] == 0xCB) {
if (byteCode[0] == 0xCB)
{
byteCode++;
}
int i = 0;
while(op[i]) {
switch(op[i]) {
while (op[i])
{
switch (op[i])
{
//Relative jumps
case 'r': getOperand((uint16_t)(memoryAddr + (int8_t)byteCode[1] + GetOpSize(byteCode[0]))); break;
case 'r': getOperand((uint16_t)(memoryAddr + (int8_t)byteCode[1] + GetOpSize(byteCode[0])));
break;
//Jump addresses, memory addresses
case 'a': getOperand((uint16_t)(byteCode[1] | (byteCode[2] << 8))); break;
case 'c': getOperand((uint16_t)(0xFF00 | byteCode[1])); break;
case 'a': getOperand((uint16_t)(byteCode[1] | (byteCode[2] << 8)));
break;
case 'c': getOperand((uint16_t)(0xFF00 | byteCode[1]));
break;
//Immediate values
case 'd': str.WriteAll("$", HexUtilities::ToHex(byteCode[1])); break;
case 'e': str.WriteAll("$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8)))); break;
case 'd': str.WriteAll("$", HexUtilities::ToHex(byteCode[1]));
break;
case 'e': str.WriteAll("$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8))));
break;
default: str.Write(op[i]); break;
default: str.Write(op[i]);
break;
}
i++;
}
@ -138,7 +216,8 @@ void GameboyDisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_
int32_t GameboyDisUtils::GetEffectiveAddress(DisassemblyInfo& info, Console* console, GbCpuState& state)
{
switch(_gbEffAddrType[info.GetOpCode()]) {
switch (_gbEffAddrType[info.GetOpCode()])
{
default:
case AddrType::None: return -1;
@ -147,7 +226,8 @@ int32_t GameboyDisUtils::GetEffectiveAddress(DisassemblyInfo& info, Console* con
case AddrType::HL: return (state.H << 8) | state.L;
case AddrType::C: return 0xFF00 + state.C;
case AddrType::Suff:
if((info.GetByteCode()[1] & 0x07) == 0x06) {
if ((info.GetByteCode()[1] & 0x07) == 0x06)
{
return (state.H << 8) | state.L;
}
return -1;
@ -164,7 +244,8 @@ bool GameboyDisUtils::IsJumpToSub(uint8_t opCode)
return (
opCode == 0xC4 || opCode == 0xCC || opCode == 0xD4 || opCode == 0xDC || //CALL conditional
opCode == 0xCD || //Unconditional CALL
opCode == 0xC7 || opCode == 0xCF || opCode == 0xD7 || opCode == 0xDF || opCode == 0xE7 || opCode == 0xEF || opCode == 0xF7 || opCode == 0xFF //RST unconditional
opCode == 0xC7 || opCode == 0xCF || opCode == 0xD7 || opCode == 0xDF || opCode == 0xE7 || opCode == 0xEF || opCode
== 0xF7 || opCode == 0xFF //RST unconditional
);
}

View file

@ -10,7 +10,8 @@ struct GbCpuState;
class GameboyDisUtils
{
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);
static int32_t GetEffectiveAddress(DisassemblyInfo& info, Console* console, GbCpuState& state);
static uint8_t GetOpSize(uint8_t opCode);
static bool IsJumpToSub(uint8_t opCode);

View file

@ -20,7 +20,8 @@ struct GameboyHeader
uint32_t GetPrgRomSize()
{
if(PrgRomSize < 16) {
if (PrgRomSize < 16)
{
return 0x8000 << PrgRomSize;
}
return 0x8000;
@ -28,12 +29,14 @@ struct GameboyHeader
uint32_t GetCartRamSize()
{
if(CartType == 5 || CartType == 6) {
if (CartType == 5 || CartType == 6)
{
//MBC2 has 512x4bits of cart ram
return 0x200;
}
switch(CartRamSize) {
switch (CartRamSize)
{
case 0: return 0;
case 1: return 0x800;
case 2: return 0x2000;
@ -46,10 +49,19 @@ struct GameboyHeader
bool HasBattery()
{
switch(CartType) {
case 0x03: case 0x06: case 0x09: case 0x0D:
case 0x0F: case 0x10: case 0x13: case 0x1B:
case 0x1E: case 0x22: case 0xFF:
switch (CartType)
{
case 0x03:
case 0x06:
case 0x09:
case 0x0D:
case 0x0F:
case 0x10:
case 0x13:
case 0x1B:
case 0x1E:
case 0x22:
case 0xFF:
return true;
}
@ -59,8 +71,10 @@ struct GameboyHeader
string GetCartName()
{
int nameLength = 11;
for(int i = 0; i < 11; i++) {
if(Title[i] == 0) {
for (int i = 0; i < 11; i++)
{
if (Title[i] == 0)
{
nameLength = i;
break;
}
@ -68,9 +82,12 @@ struct GameboyHeader
string name = string(Title, nameLength);
size_t lastNonSpace = name.find_last_not_of(' ');
if(lastNonSpace != string::npos) {
if (lastNonSpace != string::npos)
{
return name.substr(0, lastNonSpace + 1);
} else {
}
else
{
return name;
}
}

View file

@ -37,10 +37,13 @@ void GbApu::Init(Console* console, Gameboy* gameboy)
blip_clear(_leftChannel);
blip_clear(_rightChannel);
if(_gameboy->IsSgb()) {
if (_gameboy->IsSgb())
{
blip_set_rates(_leftChannel, _gameboy->GetSgb()->GetClockRate(), GbApu::SampleRate);
blip_set_rates(_rightChannel, _gameboy->GetSgb()->GetClockRate(), GbApu::SampleRate);
} else {
}
else
{
blip_set_rates(_leftChannel, GbApu::ApuFrequency, GbApu::SampleRate);
blip_set_rates(_rightChannel, GbApu::ApuFrequency, GbApu::SampleRate);
}
@ -72,11 +75,18 @@ void GbApu::Run()
GameboyConfig cfg = _settings->GetGameboyConfig();
if(!_state.ApuEnabled) {
if (!_state.ApuEnabled)
{
_clockCounter += clocksToRun;
} else {
while(clocksToRun > 0) {
uint32_t minTimer = std::min<uint32_t>({ clocksToRun, _square1->GetState().Timer, _square2->GetState().Timer, _wave->GetState().Timer, _noise->GetState().Timer });
}
else
{
while (clocksToRun > 0)
{
uint32_t minTimer = std::min<uint32_t>({
clocksToRun, _square1->GetState().Timer, _square2->GetState().Timer, _wave->GetState().Timer,
_noise->GetState().Timer
});
clocksToRun -= minTimer;
_square1->Exec(minTimer);
@ -91,7 +101,8 @@ void GbApu::Run()
(_noise->GetOutput() & _state.EnableLeftNoise) * cfg.NoiseVol / 100
) * (_state.LeftVolume + 1) * 40;
if(_prevLeftOutput != leftOutput) {
if (_prevLeftOutput != leftOutput)
{
blip_add_delta(_leftChannel, _clockCounter, leftOutput - _prevLeftOutput);
_prevLeftOutput = leftOutput;
}
@ -103,7 +114,8 @@ void GbApu::Run()
(_noise->GetOutput() & _state.EnableRightNoise) * cfg.NoiseVol / 100
) * (_state.RightVolume + 1) * 40;
if(_prevRightOutput != rightOutput) {
if (_prevRightOutput != rightOutput)
{
blip_add_delta(_rightChannel, _clockCounter, rightOutput - _prevRightOutput);
_prevRightOutput = rightOutput;
}
@ -112,7 +124,8 @@ void GbApu::Run()
}
}
if(!_gameboy->IsSgb() && _clockCounter >= 20000) {
if (!_gameboy->IsSgb() && _clockCounter >= 20000)
{
blip_end_frame(_leftChannel, _clockCounter);
blip_end_frame(_rightChannel, _clockCounter);
@ -139,20 +152,25 @@ void GbApu::ClockFrameSequencer()
{
Run();
if(!_state.ApuEnabled) {
if (!_state.ApuEnabled)
{
return;
}
if((_state.FrameSequenceStep & 0x01) == 0) {
if ((_state.FrameSequenceStep & 0x01) == 0)
{
_square1->ClockLengthCounter();
_square2->ClockLengthCounter();
_wave->ClockLengthCounter();
_noise->ClockLengthCounter();
if((_state.FrameSequenceStep & 0x03) == 2) {
if ((_state.FrameSequenceStep & 0x03) == 2)
{
_square1->ClockSweepUnit();
}
} else if(_state.FrameSequenceStep == 7) {
}
else if (_state.FrameSequenceStep == 7)
{
_square1->ClockEnvelope();
_square2->ClockEnvelope();
_noise->ClockEnvelope();
@ -174,17 +192,32 @@ uint8_t GbApu::Read(uint16_t addr)
uint8_t GbApu::InternalRead(uint16_t addr)
{
switch(addr) {
case 0xFF10: case 0xFF11: case 0xFF12: case 0xFF13: case 0xFF14:
switch (addr)
{
case 0xFF10:
case 0xFF11:
case 0xFF12:
case 0xFF13:
case 0xFF14:
return _square1->Read(addr - 0xFF10);
case 0xFF16: case 0xFF17: case 0xFF18: case 0xFF19:
case 0xFF16:
case 0xFF17:
case 0xFF18:
case 0xFF19:
return _square2->Read(addr - 0xFF15);
case 0xFF1A: case 0xFF1B: case 0xFF1C: case 0xFF1D: case 0xFF1E:
case 0xFF1A:
case 0xFF1B:
case 0xFF1C:
case 0xFF1D:
case 0xFF1E:
return _wave->Read(addr - 0xFF1A);
case 0xFF20: case 0xFF21: case 0xFF22: case 0xFF23:
case 0xFF20:
case 0xFF21:
case 0xFF22:
case 0xFF23:
return _noise->Read(addr - 0xFF1F);
case 0xFF24:
@ -217,8 +250,22 @@ uint8_t GbApu::InternalRead(uint16_t addr)
((_state.ApuEnabled && _square1->Enabled()) ? 0x01 : 0)
);
case 0xFF30: case 0xFF31: case 0xFF32: case 0xFF33: case 0xFF34: case 0xFF35: case 0xFF36: case 0xFF37:
case 0xFF38: case 0xFF39: case 0xFF3A: case 0xFF3B: case 0xFF3C: case 0xFF3D: case 0xFF3E: case 0xFF3F:
case 0xFF30:
case 0xFF31:
case 0xFF32:
case 0xFF33:
case 0xFF34:
case 0xFF35:
case 0xFF36:
case 0xFF37:
case 0xFF38:
case 0xFF39:
case 0xFF3A:
case 0xFF3B:
case 0xFF3C:
case 0xFF3D:
case 0xFF3E:
case 0xFF3F:
return _wave->ReadRam(addr);
}
@ -230,30 +277,49 @@ void GbApu::Write(uint16_t addr, uint8_t value)
{
Run();
if(!_state.ApuEnabled) {
if(addr == 0xFF11 || addr == 0xFF16 || addr == 0xFF20) {
if (!_state.ApuEnabled)
{
if (addr == 0xFF11 || addr == 0xFF16 || addr == 0xFF20)
{
//Allow writes to length counter, but not the upper 2 bits (square duty)
value &= 0x3F;
} else if(addr < 0xFF26 && addr != 0xFF1B) {
}
else if (addr < 0xFF26 && addr != 0xFF1B)
{
//Ignore all writes to these registers when APU is disabled
return;
}
}
switch(addr) {
case 0xFF10: case 0xFF11: case 0xFF12: case 0xFF13: case 0xFF14:
switch (addr)
{
case 0xFF10:
case 0xFF11:
case 0xFF12:
case 0xFF13:
case 0xFF14:
_square1->Write(addr - 0xFF10, value);
break;
case 0xFF16: case 0xFF17: case 0xFF18: case 0xFF19:
case 0xFF16:
case 0xFF17:
case 0xFF18:
case 0xFF19:
_square2->Write(addr - 0xFF15, value); //Same as square1, but without a sweep unit
break;
case 0xFF1A: case 0xFF1B: case 0xFF1C: case 0xFF1D: case 0xFF1E:
case 0xFF1A:
case 0xFF1B:
case 0xFF1C:
case 0xFF1D:
case 0xFF1E:
_wave->Write(addr - 0xFF1A, value);
break;
case 0xFF20: case 0xFF21: case 0xFF22: case 0xFF23:
case 0xFF20:
case 0xFF21:
case 0xFF22:
case 0xFF23:
_noise->Write(addr - 0xFF1F, value);
break;
@ -276,17 +342,22 @@ void GbApu::Write(uint16_t addr, uint8_t value)
_state.EnableRightSq1 = (value & 0x01) ? 0xFF : 0;
break;
case 0xFF26: {
case 0xFF26:
{
bool apuEnabled = (value & 0x80) != 0;
if(_state.ApuEnabled != apuEnabled) {
if(!apuEnabled) {
if (_state.ApuEnabled != apuEnabled)
{
if (!apuEnabled)
{
_square1->Disable();
_square2->Disable();
_wave->Disable();
_noise->Disable();
Write(0xFF24, 0);
Write(0xFF25, 0);
} else {
}
else
{
//When powered on, the frame sequencer is reset so that the next step will be 0,
//the square duty units are reset to the first step of the waveform, and the wave channel's sample buffer is reset to 0.
_state.FrameSequenceStep = 0;
@ -295,8 +366,22 @@ void GbApu::Write(uint16_t addr, uint8_t value)
}
break;
}
case 0xFF30: case 0xFF31: case 0xFF32: case 0xFF33: case 0xFF34: case 0xFF35: case 0xFF36: case 0xFF37:
case 0xFF38: case 0xFF39: case 0xFF3A: case 0xFF3B: case 0xFF3C: case 0xFF3D: case 0xFF3E: case 0xFF3F:
case 0xFF30:
case 0xFF31:
case 0xFF32:
case 0xFF33:
case 0xFF34:
case 0xFF35:
case 0xFF36:
case 0xFF37:
case 0xFF38:
case 0xFF39:
case 0xFF3A:
case 0xFF3B:
case 0xFF3C:
case 0xFF3D:
case 0xFF3E:
case 0xFF3F:
_wave->WriteRam(addr, value);
break;
}
@ -304,7 +389,8 @@ void GbApu::Write(uint16_t addr, uint8_t value)
uint8_t GbApu::ReadCgbRegister(uint16_t addr)
{
switch(addr) {
switch (addr)
{
case 0xFF76: return _square1->GetOutput() | (_square2->GetOutput() << 4);
case 0xFF77: return _noise->GetOutput() | (_wave->GetOutput() << 4);
}
@ -317,16 +403,22 @@ template<typename T>
void GbApu::ProcessLengthEnableFlag(uint8_t value, T& length, bool& lengthEnabled, bool& enabled)
{
bool newLengthEnabled = (value & 0x40) != 0;
if(newLengthEnabled && !lengthEnabled && (_state.FrameSequenceStep & 0x01) == 1) {
if (newLengthEnabled && !lengthEnabled && (_state.FrameSequenceStep & 0x01) == 1)
{
//"Extra length clocking occurs when writing to NRx4 when the frame sequencer's next step is one that doesn't clock
//the length counter. In this case, if the length counter was PREVIOUSLY disabled and now enabled and the length counter
//is not zero, it is decremented. If this decrement makes it zero and trigger is clear, the channel is disabled."
if(length > 0) {
if (length > 0)
{
length--;
if(length == 0) {
if(value & 0x80) {
if (length == 0)
{
if (value & 0x80)
{
length = sizeof(T) == 1 ? 0x3F : 0xFF;
} else {
}
else
{
enabled = false;
}
}
@ -351,5 +443,7 @@ void GbApu::Serialize(Serializer& s)
s.Stream(_noise.get());
}
template void GbApu::ProcessLengthEnableFlag<uint8_t>(uint8_t value, uint8_t& length, bool& lengthEnabled, bool& enabled);
template void GbApu::ProcessLengthEnableFlag<uint16_t>(uint8_t value, uint16_t& length, bool& lengthEnabled, bool& enabled);
template void GbApu::ProcessLengthEnableFlag<uint8_t>(uint8_t value, uint8_t& length, bool& lengthEnabled,
bool& enabled);
template void GbApu::ProcessLengthEnableFlag<uint16_t>(uint8_t value, uint16_t& length, bool& lengthEnabled,
bool& enabled);

View file

@ -62,7 +62,8 @@ public:
uint8_t ReadCgbRegister(uint16_t addr);
template<typename T> void ProcessLengthEnableFlag(uint8_t value, T& length, bool& lengthEnabled, bool& enabled);
template <typename T>
void ProcessLengthEnableFlag(uint8_t value, T& length, bool& lengthEnabled, bool& enabled);
void Serialize(Serializer& s) override;
};

View file

@ -20,17 +20,21 @@ GbAssembler::~GbAssembler()
void GbAssembler::InitAssembler()
{
for(int i = 0; i < 512; i++) {
for (int i = 0; i < 512; i++)
{
string op = GameboyDisUtils::GetOpTemplate(i & 0xFF, i >= 256);
size_t spaceIndex = op.find(' ');
size_t commaIndex = op.find(',');
string opName;
OpCodeEntry entry = {};
if(spaceIndex != string::npos) {
if (spaceIndex != string::npos)
{
opName = op.substr(0, spaceIndex);
entry.ParamCount = commaIndex != string::npos ? 2 : 1;
} else {
}
else
{
opName = op;
entry.ParamCount = 0;
}
@ -38,18 +42,23 @@ void GbAssembler::InitAssembler()
entry.OpCode = i < 256 ? i : ((i << 8) | 0xCB);
std::transform(opName.begin(), opName.end(), opName.begin(), ::tolower);
if(_opCodes.find(opName) == _opCodes.end()) {
if (_opCodes.find(opName) == _opCodes.end())
{
_opCodes[opName] = vector<OpCodeEntry>();
}
if(entry.ParamCount > 0) {
if (entry.ParamCount > 0)
{
string operands = op.substr(spaceIndex + 1);
operands.erase(std::remove_if(operands.begin(), operands.end(), isspace), operands.end());
if(entry.ParamCount == 2) {
if (entry.ParamCount == 2)
{
vector<string> operandList = StringUtilities::Split(operands, ',');
InitParamEntry(entry.Param1, operandList[0]);
InitParamEntry(entry.Param2, operandList[1]);
} else if(entry.ParamCount == 1) {
}
else if (entry.ParamCount == 1)
{
InitParamEntry(entry.Param1, operands);
}
}
@ -59,21 +68,36 @@ void GbAssembler::InitAssembler()
void GbAssembler::InitParamEntry(ParamEntry& entry, string param)
{
if(param == "a") {
if (param == "a")
{
entry.Type = ParamType::Short;
} else if(param == "d") {
}
else if (param == "d")
{
entry.Type = ParamType::Byte;
} else if(param == "e") {
}
else if (param == "e")
{
entry.Type = ParamType::Short;
} else if(param == "r") {
}
else if (param == "r")
{
entry.Type = ParamType::RelAddress;
} else if(param == "(a)") {
}
else if (param == "(a)")
{
entry.Type = ParamType::Address;
} else if(param == "(c)") {
}
else if (param == "(c)")
{
entry.Type = ParamType::HighAddress;
} else if(param == "SP+d") {
}
else if (param == "SP+d")
{
entry.Type = ParamType::StackOffset;
} else {
}
else
{
std::transform(param.begin(), param.end(), param.begin(), ::tolower);
entry.Type = ParamType::Literal;
entry.Param = param;
@ -83,74 +107,99 @@ void GbAssembler::InitParamEntry(ParamEntry& entry, string param)
bool GbAssembler::IsRegisterName(string op)
{
return op == "hl" || op == "af" || op == "bc" || op == "de" || op == "a" || op == "b" || op == "c" || op == "d" || op == "e" || op == "f" || op == "l" || op == "h";
return op == "hl" || op == "af" || op == "bc" || op == "de" || op == "a" || op == "b" || op == "c" || op == "d" || op
== "e" || op == "f" || op == "l" || op == "h";
}
int GbAssembler::ReadValue(string operand, int min, int max, unordered_map<string, uint16_t>& localLabels, bool firstPass)
int GbAssembler::ReadValue(string operand, int min, int max, unordered_map<string, uint16_t>& localLabels,
bool firstPass)
{
int value = 0;
switch(operand[0]) {
switch (operand[0])
{
//Hex
case '$': value = HexUtilities::FromHex(operand.substr(1)); break;
case '$': value = HexUtilities::FromHex(operand.substr(1));
break;
case '%':
//Binary
for(size_t i = 1; i < operand.size(); i++) {
for (size_t i = 1; i < operand.size(); i++)
{
value <<= 1;
value |= operand[i] == '1' ? 1 : 0;
}
break;
default:
if(std::regex_match(operand, labelRegex)) {
if(firstPass) {
if (std::regex_match(operand, labelRegex))
{
if (firstPass)
{
return 0;
} else if(localLabels.find(operand) != localLabels.end()) {
}
else if (localLabels.find(operand) != localLabels.end())
{
value = localLabels.find(operand)->second;
} else {
}
else
{
int labelAddress = _labelManager->GetLabelRelativeAddress(operand, CpuType::Gameboy);
if(labelAddress >= 0) {
if (labelAddress >= 0)
{
//Matching label found
value = labelAddress;
}
}
} else {
}
else
{
//Decimal
for(size_t i = 0; i < operand.size(); i++) {
if(operand[i] != '-' && (operand[i] < '0' || operand[i] > '9')) {
for (size_t i = 0; i < operand.size(); i++)
{
if (operand[i] != '-' && (operand[i] < '0' || operand[i] > '9'))
{
return -1;
}
}
try {
try
{
value = std::stoi(operand);
if(value < 0) {
if (value < 0)
{
value = max + value + 1;
}
} catch(std::exception&) {
}
catch (std::exception&)
{
return -1;
}
}
break;
}
if(value < min || value > max) {
if (value < min || value > max)
{
return -1;
}
return value;
}
bool GbAssembler::IsMatch(ParamEntry& entry, string operand, uint32_t address, unordered_map<string, uint16_t>& localLabels, bool firstPass)
bool GbAssembler::IsMatch(ParamEntry& entry, string operand, uint32_t address,
unordered_map<string, uint16_t>& localLabels, bool firstPass)
{
if (entry.Type != ParamType::Literal && IsRegisterName(operand))
{
if(entry.Type != ParamType::Literal && IsRegisterName(operand)) {
return false;
}
switch(entry.Type) {
switch (entry.Type)
{
case ParamType::None: return false;
case ParamType::Literal: {
case ParamType::Literal:
{
string param = entry.Param;
std::transform(param.begin(), param.end(), param.begin(), ::tolower);
std::transform(operand.begin(), operand.end(), operand.begin(), ::tolower);
@ -164,30 +213,37 @@ bool GbAssembler::IsMatch(ParamEntry& entry, string operand, uint32_t address, u
return ReadValue(operand, -32768, 0xFFFF, localLabels, firstPass) >= 0;
case ParamType::Address:
if(operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')') {
if (operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')')
{
return ReadValue(operand.substr(1, operand.size() - 2), 0, 0xFFFF, localLabels, firstPass) >= 0;
}
return false;
case ParamType::HighAddress:
if(operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')') {
if (operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')')
{
return ReadValue(operand.substr(1, operand.size() - 2), 0xFF00, 0xFFFF, localLabels, firstPass) >= 0;
}
return false;
case ParamType::StackOffset:
std::transform(operand.begin(), operand.end(), operand.begin(), ::tolower);
if(operand.size() > 3 && operand.substr(0, 3) == "sp+") {
if (operand.size() > 3 && operand.substr(0, 3) == "sp+")
{
return ReadValue(operand.substr(3), 0, 0xFF, localLabels, firstPass) >= 0;
}
return false;
case ParamType::RelAddress: {
case ParamType::RelAddress:
{
int value = ReadValue(operand, 0, 0xFFFF, localLabels, firstPass);
if(value >= 0) {
if (value >= 0)
{
int offset = (value - (address + 2));
return offset >= -128 && offset <= 127;
} else if(firstPass) {
}
else if (firstPass)
{
return 0;
}
return false;
@ -199,9 +255,12 @@ bool GbAssembler::IsMatch(ParamEntry& entry, string operand, uint32_t address, u
void GbAssembler::PushOp(uint16_t opCode, vector<int16_t>& output, uint32_t& address)
{
if(opCode < 256) {
if (opCode < 256)
{
PushByte((uint8_t)opCode, output, address);
} else {
}
else
{
PushWord((uint16_t)opCode, output, address);
}
}
@ -219,9 +278,11 @@ void GbAssembler::PushWord(uint16_t operand, vector<int16_t>& output, uint32_t&
address += 2;
}
void GbAssembler::ProcessOperand(ParamEntry& entry, string operand, vector<int16_t>& output, uint32_t& address, unordered_map<string, uint16_t>& localLabels, bool firstPass)
void GbAssembler::ProcessOperand(ParamEntry& entry, string operand, vector<int16_t>& output, uint32_t& address,
unordered_map<string, uint16_t>& localLabels, bool firstPass)
{
switch (entry.Type)
{
switch(entry.Type) {
default:
break;
@ -234,25 +295,31 @@ void GbAssembler::ProcessOperand(ParamEntry& entry, string operand, vector<int16
break;
case ParamType::Address:
if(operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')') {
PushWord((uint16_t)ReadValue(operand.substr(1, operand.size() - 2), 0, 0xFFFF, localLabels, firstPass), output, address);
if (operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')')
{
PushWord((uint16_t)ReadValue(operand.substr(1, operand.size() - 2), 0, 0xFFFF, localLabels, firstPass), output,
address);
}
break;
case ParamType::HighAddress:
if(operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')') {
PushByte((uint8_t)ReadValue(operand.substr(1, operand.size() - 2), 0xFF00, 0xFFFF, localLabels, firstPass), output, address);
if (operand.size() > 2 && operand[0] == '(' && operand[operand.size() - 1] == ')')
{
PushByte((uint8_t)ReadValue(operand.substr(1, operand.size() - 2), 0xFF00, 0xFFFF, localLabels, firstPass),
output, address);
}
break;
case ParamType::StackOffset:
std::transform(operand.begin(), operand.end(), operand.begin(), ::tolower);
if(operand.size() > 3 && operand.substr(0, 3) == "sp+") {
if (operand.size() > 3 && operand.substr(0, 3) == "sp+")
{
PushByte((uint8_t)ReadValue(operand.substr(3), 0, 0xFF, localLabels, firstPass), output, address);
}
break;
case ParamType::RelAddress: {
case ParamType::RelAddress:
{
int value = ReadValue(operand, 0, 0xFFFF, localLabels, firstPass);
int offset = (value - (address + 1));
PushByte((uint8_t)offset, output, address);
@ -261,32 +328,42 @@ void GbAssembler::ProcessOperand(ParamEntry& entry, string operand, vector<int16
}
}
void GbAssembler::RunPass(vector<int16_t>& output, string code, uint32_t address, int16_t* assembledCode, bool firstPass, unordered_map<string, uint16_t>& localLabels)
void GbAssembler::RunPass(vector<int16_t>& output, string code, uint32_t address, int16_t* assembledCode,
bool firstPass, unordered_map<string, uint16_t>& localLabels)
{
unordered_set<string> currentPassLabels;
for(string line : StringUtilities::Split(code, '\n')) {
for (string line : StringUtilities::Split(code, '\n'))
{
//Remove comment
size_t commentIndex = line.find(';');
if(commentIndex != string::npos) {
if (commentIndex != string::npos)
{
line = line.substr(0, commentIndex);
}
//Check if this is a label definition
size_t labelDefIndex = line.find(':');
if(labelDefIndex != string::npos) {
if (labelDefIndex != string::npos)
{
std::smatch match;
string labelName = line.substr(0, labelDefIndex);
if(std::regex_search(labelName, match, labelRegex)) {
if (std::regex_search(labelName, match, labelRegex))
{
string label = match.str(1);
if(firstPass && currentPassLabels.find(label) != currentPassLabels.end()) {
if (firstPass && currentPassLabels.find(label) != currentPassLabels.end())
{
output.push_back(AssemblerSpecialCodes::LabelRedefinition);
continue;
} else {
}
else
{
localLabels[label] = address;
currentPassLabels.emplace(label);
line = line.substr(labelDefIndex + 1);
}
} else {
}
else
{
output.push_back(AssemblerSpecialCodes::InvalidLabel);
continue;
}
@ -294,20 +371,25 @@ void GbAssembler::RunPass(vector<int16_t>& output, string code, uint32_t address
//Trim left spaces
size_t startIndex = line.find_first_not_of("\t ");
if(startIndex > 0 && startIndex != string::npos) {
if (startIndex > 0 && startIndex != string::npos)
{
line = line.substr(startIndex);
}
//Check if this is a .db statement
if(line.size() > 3 && line.substr(0, 3) == ".db") {
if (line.size() > 3 && line.substr(0, 3) == ".db")
{
line = line.substr(3);
for(string byte : StringUtilities::Split(line, ' ')) {
if(byte.empty()) {
for (string byte : StringUtilities::Split(line, ' '))
{
if (byte.empty())
{
continue;
}
int value = ReadValue(byte, -128, 255, localLabels, true);
if(value >= 0) {
if (value >= 0)
{
PushByte((uint8_t)value, output, address);
}
}
@ -318,20 +400,25 @@ void GbAssembler::RunPass(vector<int16_t>& output, string code, uint32_t address
//Find op code name
size_t spaceIndex = line.find(' ');
string opName;
if(spaceIndex != string::npos) {
if (spaceIndex != string::npos)
{
opName = line.substr(0, spaceIndex);
} else {
}
else
{
opName = line;
}
if(opName.empty()) {
if (opName.empty())
{
output.push_back(AssemblerSpecialCodes::EndOfLine);
continue;
}
std::transform(opName.begin(), opName.end(), opName.begin(), ::tolower);
if(_opCodes.find(opName) == _opCodes.end()) {
if (_opCodes.find(opName) == _opCodes.end())
{
//No matching opcode found, mark it as invalid
output.push_back(AssemblerSpecialCodes::InvalidInstruction);
continue;
@ -340,28 +427,36 @@ void GbAssembler::RunPass(vector<int16_t>& output, string code, uint32_t address
//Find the operands given
int paramCount = 0;
vector<string> operandList;
if(spaceIndex != string::npos) {
if (spaceIndex != string::npos)
{
string operands = line.substr(spaceIndex + 1);
operands.erase(std::remove_if(operands.begin(), operands.end(), isspace), operands.end());
if(!operands.empty()) {
if (!operands.empty())
{
size_t commaIndex = line.find(',');
if(commaIndex != string::npos) {
if (commaIndex != string::npos)
{
paramCount = 2;
operandList = StringUtilities::Split(operands, ',');
bool invalid = operandList.size() > 2;
for(string operand : operandList) {
if(operand.empty()) {
for (string operand : operandList)
{
if (operand.empty())
{
invalid = true;
break;
}
}
if(invalid) {
if (invalid)
{
output.push_back(AssemblerSpecialCodes::InvalidOperands);
continue;
}
} else {
}
else
{
paramCount = 1;
operandList = {operands};
}
@ -370,21 +465,31 @@ void GbAssembler::RunPass(vector<int16_t>& output, string code, uint32_t address
bool matchFound = false;
//Find a matching set of opcode + operands
for(OpCodeEntry& entry : _opCodes.find(opName)->second) {
if(entry.ParamCount == paramCount) {
if(paramCount == 0) {
for (OpCodeEntry& entry : _opCodes.find(opName)->second)
{
if (entry.ParamCount == paramCount)
{
if (paramCount == 0)
{
PushOp(entry.OpCode, output, address);
matchFound = true;
break;
} else if(paramCount == 1) {
if(IsMatch(entry.Param1, operandList[0], address, localLabels, firstPass)) {
}
else if (paramCount == 1)
{
if (IsMatch(entry.Param1, operandList[0], address, localLabels, firstPass))
{
PushOp(entry.OpCode, output, address);
ProcessOperand(entry.Param1, operandList[0], output, address, localLabels, firstPass);
matchFound = true;
break;
}
} else if(paramCount == 2) {
if(IsMatch(entry.Param1, operandList[0], address, localLabels, firstPass) && IsMatch(entry.Param2, operandList[1], address, localLabels, firstPass)) {
}
else if (paramCount == 2)
{
if (IsMatch(entry.Param1, operandList[0], address, localLabels, firstPass) && IsMatch(
entry.Param2, operandList[1], address, localLabels, firstPass))
{
PushOp(entry.OpCode, output, address);
ProcessOperand(entry.Param1, operandList[0], output, address, localLabels, firstPass);
ProcessOperand(entry.Param2, operandList[1], output, address, localLabels, firstPass);
@ -395,9 +500,12 @@ void GbAssembler::RunPass(vector<int16_t>& output, string code, uint32_t address
}
}
if(!matchFound) {
if (!matchFound)
{
output.push_back(AssemblerSpecialCodes::InvalidOperands);
} else {
}
else
{
output.push_back(AssemblerSpecialCodes::EndOfLine);
}
}

View file

@ -40,13 +40,16 @@ private:
bool IsRegisterName(string op);
void InitAssembler();
int ReadValue(string operand, int min, int max, unordered_map<string, uint16_t>& localLabels, bool firstPass);
bool IsMatch(ParamEntry& entry, string operand, uint32_t address, unordered_map<string, uint16_t>& localLabels, bool firstPass);
bool IsMatch(ParamEntry& entry, string operand, uint32_t address, unordered_map<string, uint16_t>& localLabels,
bool firstPass);
void PushOp(uint16_t opCode, vector<int16_t>& output, uint32_t& address);
void PushByte(uint8_t operand, vector<int16_t>& output, uint32_t& address);
void PushWord(uint16_t operand, vector<int16_t>& output, uint32_t& address);
void ProcessOperand(ParamEntry& entry, string operand, vector<int16_t>& output, uint32_t& address, unordered_map<string, uint16_t>& localLabels, bool firstPass);
void ProcessOperand(ParamEntry& entry, string operand, vector<int16_t>& output, uint32_t& address,
unordered_map<string, uint16_t>& localLabels, bool firstPass);
void RunPass(vector<int16_t>& output, string code, uint32_t address, int16_t* assembledCode, bool firstPass, unordered_map<string, uint16_t>& localLabels);
void RunPass(vector<int16_t>& output, string code, uint32_t address, int16_t* assembledCode, bool firstPass,
unordered_map<string, uint16_t>& localLabels);
public:
GbAssembler(shared_ptr<LabelManager> labelManager);

View file

@ -11,23 +11,35 @@ class GbCartFactory
public:
static GbCart* CreateCart(uint8_t cartType)
{
switch(cartType) {
switch (cartType)
{
case 0:
return new GbCart();
case 1: case 2: case 3:
case 1:
case 2:
case 3:
return new GbMbc1();
case 5: case 6:
case 5:
case 6:
return new GbMbc2();
case 15: case 16:
case 15:
case 16:
return new GbMbc3(true);
case 17: case 18: case 19:
case 17:
case 18:
case 19:
return new GbMbc3(false);
case 25: case 26: case 27: case 28: case 29: case 30:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
return new GbMbc5();
};

File diff suppressed because it is too large Load diff

View file

@ -37,7 +37,8 @@ GbDebugger::GbDebugger(Debugger* debugger)
_step.reset(new StepRequest());
_assembler.reset(new GbAssembler(debugger->GetLabelManager()));
if(_gameboy->GetState().MemoryManager.ApuCycleCount == 0) {
if (_gameboy->GetState().MemoryManager.ApuCycleCount == 0)
{
//Enable breaking on uninit reads when debugger is opened at power on
_enableBreakOnUninitRead = true;
}
@ -59,18 +60,23 @@ void GbDebugger::ProcessRead(uint16_t addr, uint8_t value, MemoryOperationType t
MemoryOperationInfo operation{addr, value, type};
BreakSource breakSource = BreakSource::Unspecified;
if(type == MemoryOperationType::ExecOpCode) {
if (type == MemoryOperationType::ExecOpCode)
{
GbCpuState gbState = _gameboy->GetState().Cpu;
if(_traceLogger->IsCpuLogged(CpuType::Gameboy) || _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled)) {
if(addressInfo.Address >= 0) {
if(addressInfo.Type == SnesMemoryType::GbPrgRom) {
if (_traceLogger->IsCpuLogged(CpuType::Gameboy) || _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled))
{
if (addressInfo.Address >= 0)
{
if (addressInfo.Type == SnesMemoryType::GbPrgRom)
{
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code);
}
_disassembler->BuildCache(addressInfo, 0, CpuType::Gameboy);
}
if(_traceLogger->IsCpuLogged(CpuType::Gameboy)) {
if (_traceLogger->IsCpuLogged(CpuType::Gameboy))
{
DebugState debugState;
_debugger->GetState(debugState, true);
@ -79,38 +85,58 @@ void GbDebugger::ProcessRead(uint16_t addr, uint8_t value, MemoryOperationType t
}
}
if(GameboyDisUtils::IsJumpToSub(_prevOpCode) && gbState.PC != _prevProgramCounter + GameboyDisUtils::GetOpSize(_prevOpCode)) {
if (GameboyDisUtils::IsJumpToSub(_prevOpCode) && gbState.PC != _prevProgramCounter +
GameboyDisUtils::GetOpSize(_prevOpCode))
{
//CALL and RST, and PC doesn't match the next instruction, so the call was (probably) done
uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, 0, CpuType::Gameboy);
uint16_t returnPc = _prevProgramCounter + opSize;
AddressInfo src = _gameboy->GetAbsoluteAddress(_prevProgramCounter);
AddressInfo ret = _gameboy->GetAbsoluteAddress(returnPc);
_callstackManager->Push(src, _prevProgramCounter, addressInfo, gbState.PC, ret, returnPc, StackFrameFlags::None);
} else if(GameboyDisUtils::IsReturnInstruction(_prevOpCode) && gbState.PC != _prevProgramCounter + GameboyDisUtils::GetOpSize(_prevOpCode)) {
_callstackManager->Push(src, _prevProgramCounter, addressInfo, gbState.PC, ret, returnPc,
StackFrameFlags::None);
}
else if (GameboyDisUtils::IsReturnInstruction(_prevOpCode) && gbState.PC != _prevProgramCounter +
GameboyDisUtils::GetOpSize(_prevOpCode))
{
//RET used, and PC doesn't match the next instruction, so the ret was (probably) taken
_callstackManager->Pop(addressInfo, gbState.PC);
}
if(_step->BreakAddress == (int32_t)gbState.PC && GameboyDisUtils::IsReturnInstruction(_prevOpCode)) {
if (_step->BreakAddress == (int32_t)gbState.PC && GameboyDisUtils::IsReturnInstruction(_prevOpCode))
{
//RET/RETI found, if we're on the expected return address, break immediately (for step over/step out)
_step->StepCount = 0;
}
if(_settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled)) {
if (_settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled))
{
bool needBreak = false;
switch(value) {
switch (value)
{
case 0x40:
needBreak = _settings->CheckDebuggerFlag(DebuggerFlags::GbBreakOnNopLoad);
breakSource = BreakSource::GbNopLoad;
break;
case 0xD3: case 0xDB: case 0xDD: case 0xE3: case 0xE4: case 0xEB: case 0xEC: case 0xED: case 0xF4: case 0xFC: case 0xFD:
case 0xD3:
case 0xDB:
case 0xDD:
case 0xE3:
case 0xE4:
case 0xEB:
case 0xEC:
case 0xED:
case 0xF4:
case 0xFC:
case 0xFD:
needBreak = _settings->CheckDebuggerFlag(DebuggerFlags::GbBreakOnInvalidOpCode);
breakSource = BreakSource::GbInvalidOpCode;
break;
}
if(needBreak) {
if (needBreak)
{
_step->StepCount = 0;
}
}
@ -118,30 +144,43 @@ void GbDebugger::ProcessRead(uint16_t addr, uint8_t value, MemoryOperationType t
_prevOpCode = value;
_prevProgramCounter = gbState.PC;
if(_step->StepCount > 0) {
if (_step->StepCount > 0)
{
_step->StepCount--;
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _console->GetMasterClock());
} else if(type == MemoryOperationType::ExecOperand) {
if(addressInfo.Address >= 0 && addressInfo.Type == SnesMemoryType::GbPrgRom) {
}
else if (type == MemoryOperationType::ExecOperand)
{
if (addressInfo.Address >= 0 && addressInfo.Type == SnesMemoryType::GbPrgRom)
{
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _console->GetMasterClock());
} else {
if(addressInfo.Address >= 0 && addressInfo.Type == SnesMemoryType::GbPrgRom) {
}
else
{
if (addressInfo.Address >= 0 && addressInfo.Type == SnesMemoryType::GbPrgRom)
{
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data);
}
if(addr < 0xFE00 || addr >= 0xFF80) {
if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _console->GetMasterClock())) {
if (addr < 0xFE00 || addr >= 0xFF80)
{
if (_memoryAccessCounter->ProcessMemoryRead(addressInfo, _console->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("[GB] Uninitialized memory read: $" + HexUtilities::ToHex(addr));
}
if(_settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled) && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnUninitRead)) {
if (_settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled) && _settings->CheckDebuggerFlag(
DebuggerFlags::BreakOnUninitRead))
{
breakSource = BreakSource::BreakOnUninitMemoryRead;
_step->StepCount = 0;
}
@ -149,12 +188,14 @@ void GbDebugger::ProcessRead(uint16_t addr, uint8_t value, MemoryOperationType t
}
}
if(addr == 0xFFFF || (addr >= 0xFE00 && addr < 0xFF80) || (addr >= 0x8000 && addr <= 0x9FFF)) {
if (addr == 0xFFFF || (addr >= 0xFE00 && addr < 0xFF80) || (addr >= 0x8000 && addr <= 0x9FFF))
{
_eventManager->AddEvent(DebugEventType::Register, operation);
}
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo, breakSource);
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo,
breakSource);
}
void GbDebugger::ProcessWrite(uint16_t addr, uint8_t value, MemoryOperationType type)
@ -163,11 +204,14 @@ void GbDebugger::ProcessWrite(uint16_t addr, uint8_t value, MemoryOperationType
MemoryOperationInfo operation{addr, value, type};
_debugger->ProcessBreakConditions(false, GetBreakpointManager(), operation, addressInfo);
if(addressInfo.Type == SnesMemoryType::GbWorkRam || addressInfo.Type == SnesMemoryType::GbCartRam || addressInfo.Type == SnesMemoryType::GbHighRam) {
if (addressInfo.Type == SnesMemoryType::GbWorkRam || addressInfo.Type == SnesMemoryType::GbCartRam || addressInfo.
Type == SnesMemoryType::GbHighRam)
{
_disassembler->InvalidateCache(addressInfo, CpuType::Gameboy);
}
if(addr == 0xFFFF || (addr >= 0xFE00 && addr < 0xFF80) || (addr >= 0x8000 && addr <= 0x9FFF)) {
if (addr == 0xFFFF || (addr >= 0xFE00 && addr < 0xFF80) || (addr >= 0x8000 && addr <= 0x9FFF))
{
_eventManager->AddEvent(DebugEventType::Register, operation);
}
@ -184,24 +228,35 @@ void GbDebugger::Step(int32_t stepCount, StepType type)
StepRequest step;
GbCpuState gbState = _gameboy->GetState().Cpu;
if((type == StepType::StepOver || type == StepType::StepOut || type == StepType::Step) && gbState.Halted) {
if ((type == StepType::StepOver || type == StepType::StepOut || type == StepType::Step) && gbState.Halted)
{
//CPU isn't running - use the PPU to break execution instead
step.PpuStepCount = 1;
} else {
switch(type) {
case StepType::Step: step.StepCount = stepCount; break;
case StepType::StepOut: step.BreakAddress = _callstackManager->GetReturnAddress(); break;
}
else
{
switch (type)
{
case StepType::Step: step.StepCount = stepCount;
break;
case StepType::StepOut: step.BreakAddress = _callstackManager->GetReturnAddress();
break;
case StepType::StepOver:
if(GameboyDisUtils::IsJumpToSub(_prevOpCode)) {
if (GameboyDisUtils::IsJumpToSub(_prevOpCode))
{
step.BreakAddress = _prevProgramCounter + DisassemblyInfo::GetOpSize(_prevOpCode, 0, CpuType::Gameboy);
} else {
}
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));
@ -218,14 +273,17 @@ void GbDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc)
void GbDebugger::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);
}

View file

@ -19,13 +19,16 @@ GbDmaControllerState GbDmaController::GetState()
void GbDmaController::Exec()
{
if(_cpu->IsHalted()) {
if (_cpu->IsHalted())
{
//OAM DMA is halted while the CPU is in halt mode, and resumes when the CPU resumes
return;
}
if(_state.DmaCounter > 0) {
if(_state.DmaCounter <= 160) {
if (_state.DmaCounter > 0)
{
if (_state.DmaCounter <= 160)
{
_memoryManager->WriteDma(0xFE00 + (160 - _state.DmaCounter), _state.DmaReadBuffer);
}
@ -33,8 +36,10 @@ void GbDmaController::Exec()
_state.DmaReadBuffer = _memoryManager->ReadDma((_state.OamDmaSource << 8) + (160 - _state.DmaCounter));
}
if(_state.DmaStartDelay > 0) {
if(--_state.DmaStartDelay == 0) {
if (_state.DmaStartDelay > 0)
{
if (--_state.DmaStartDelay == 0)
{
_state.InternalDest = _state.OamDmaSource;
_state.DmaCounter = 161;
}
@ -59,7 +64,8 @@ void GbDmaController::Write(uint8_t value)
uint8_t GbDmaController::ReadCgb(uint16_t addr)
{
switch(addr) {
switch (addr)
{
case 0xFF55: return _state.CgbDmaLength | (_state.CgbHdmaDone ? 0x80 : 0);
}
@ -68,40 +74,55 @@ uint8_t GbDmaController::ReadCgb(uint16_t addr)
void GbDmaController::WriteCgb(uint16_t addr, uint8_t value)
{
switch(addr) {
case 0xFF51: _state.CgbDmaSource = (_state.CgbDmaSource & 0xFF) | (value << 8); break;
case 0xFF52: _state.CgbDmaSource = (_state.CgbDmaSource & 0xFF00) | (value & 0xF0); break;
case 0xFF53: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF) | (value << 8); break;
case 0xFF54: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF00) | (value & 0xF0); break;
case 0xFF55: {
switch (addr)
{
case 0xFF51: _state.CgbDmaSource = (_state.CgbDmaSource & 0xFF) | (value << 8);
break;
case 0xFF52: _state.CgbDmaSource = (_state.CgbDmaSource & 0xFF00) | (value & 0xF0);
break;
case 0xFF53: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF) | (value << 8);
break;
case 0xFF54: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF00) | (value & 0xF0);
break;
case 0xFF55:
{
bool hdmaMode = (value & 0x80) != 0;
_state.CgbDmaLength = value & 0x7F;
if(!hdmaMode) {
if(_state.CgbHdmaRunning) {
if (!hdmaMode)
{
if (_state.CgbHdmaRunning)
{
//"If HDMA5 is written during a HDMA copy, the behaviour depends on the written bit 7.
// - New bit 7 is 0: Stop copy. HDMA5 new value is ( 80h OR written_value ).
// - New bit 7 is 1: Restart copy. New size is the value of written_value bits 0-6.
//This means that HDMA can't switch to GDMA with only one write. It must be stopped first."
_state.CgbHdmaRunning = false;
_state.CgbHdmaDone = true;
} else {
}
else
{
//4 cycles for setup
_memoryManager->Exec();
_memoryManager->Exec();
do {
do
{
ProcessDmaBlock();
} while(_state.CgbDmaLength != 0x7F);
}
} else {
while (_state.CgbDmaLength != 0x7F);
}
}
else
{
//"After writing a value to HDMA5 that starts the HDMA copy, the upper bit (that indicates HDMA mode when set to '1') will be cleared"
_state.CgbHdmaDone = false;
_state.CgbHdmaRunning = true;
//"If a HDMA transfer is started when the screen is off, one block is copied. "
//" When a HDMA transfer is started during HBL it will start right away."
if(!_ppu->IsLcdEnabled() || _ppu->GetMode() == PpuMode::HBlank) {
if (!_ppu->IsLcdEnabled() || _ppu->GetMode() == PpuMode::HBlank)
{
ProcessHdma();
}
}
@ -112,7 +133,8 @@ void GbDmaController::WriteCgb(uint16_t addr, uint8_t value)
void GbDmaController::ProcessHdma()
{
if(_state.CgbHdmaRunning) {
if (_state.CgbHdmaRunning)
{
//4 cycles for setup
_memoryManager->Exec();
_memoryManager->Exec();
@ -124,13 +146,15 @@ void GbDmaController::ProcessHdma()
void GbDmaController::ProcessDmaBlock()
{
//TODO check invalid dma sources/etc.
for(int i = 0; i < 16; i++) {
for (int i = 0; i < 16; i++)
{
uint16_t dst = 0x8000 | ((_state.CgbDmaDest + i) & 0x1FFF);
//2 or 4 cycles per byte transfered (2x more cycles in high speed mode - effective speed is the same in both modes
_memoryManager->Exec();
uint8_t value = _memoryManager->Read<MemoryOperationType::DmaRead>(_state.CgbDmaSource + i);
if(_memoryManager->IsHighSpeed()) {
if (_memoryManager->IsHighSpeed())
{
_memoryManager->Exec();
}
_memoryManager->Write<MemoryOperationType::DmaWrite>(dst, value);
@ -141,7 +165,8 @@ void GbDmaController::ProcessDmaBlock()
_state.CgbDmaDest += 16;
_state.CgbDmaLength = (_state.CgbDmaLength - 1) & 0x7F;
if(_state.CgbHdmaRunning && _state.CgbDmaLength == 0x7F) {
if (_state.CgbHdmaRunning && _state.CgbDmaLength == 0x7F)
{
_state.CgbHdmaRunning = false;
_state.CgbHdmaDone = true;
}

View file

@ -63,8 +63,10 @@ DebugEventInfo GbEventManager::GetEvent(uint16_t scanline, uint16_t cycle, Event
{
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;
}
}
@ -93,85 +95,132 @@ void GbEventManager::FilterEvents(EventViewerDisplayOptions& options)
_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;
for (DebugEventInfo& evt : events)
{
bool isWrite = evt.Operation.Type == MemoryOperationType::Write || evt.Operation.Type ==
MemoryOperationType::DmaWrite;
bool showEvent = false;
switch(evt.Type) {
switch (evt.Type)
{
default: break;
case DebugEventType::Breakpoint: showEvent = options.ShowMarkedBreakpoints; break;
case DebugEventType::Irq: showEvent = options.ShowIrq; break;
case DebugEventType::Breakpoint: showEvent = options.ShowMarkedBreakpoints;
break;
case DebugEventType::Irq: showEvent = options.ShowIrq;
break;
case DebugEventType::Register:
uint16_t reg = evt.Operation.Address & 0xFFFF;
if(reg >= 0xFE00 && reg <= 0xFE9F) {
if (reg >= 0xFE00 && reg <= 0xFE9F)
{
showEvent = isWrite ? options.ShowPpuRegisterOamWrites : options.ShowPpuRegisterReads;
} else if(reg >= 0xFF42 && reg <= 0xFF43) {
}
else if (reg >= 0xFF42 && reg <= 0xFF43)
{
showEvent = isWrite ? options.ShowPpuRegisterBgScrollWrites : options.ShowPpuRegisterReads;
} else if(reg >= 0x8000 && reg <= 0x9FFF) {
}
else if (reg >= 0x8000 && reg <= 0x9FFF)
{
showEvent = isWrite ? options.ShowPpuRegisterVramWrites : options.ShowPpuRegisterReads;
} else if((reg >= 0xFF47 && reg <= 0xFF49) || (reg >= 0xFF68 && reg <= 0xFF6B)) {
}
else if ((reg >= 0xFF47 && reg <= 0xFF49) || (reg >= 0xFF68 && reg <= 0xFF6B))
{
showEvent = isWrite ? options.ShowPpuRegisterCgramWrites : options.ShowPpuRegisterReads;
} else if(reg >= 0xFF4A && reg <= 0xFF4B) {
}
else if (reg >= 0xFF4A && reg <= 0xFF4B)
{
showEvent = isWrite ? options.ShowPpuRegisterWindowWrites : options.ShowPpuRegisterReads;
} else if(reg >= 0xFF40 && reg <= 0xFF70) {
}
else if (reg >= 0xFF40 && reg <= 0xFF70)
{
showEvent = isWrite ? options.ShowPpuRegisterOtherWrites : options.ShowPpuRegisterReads;
} else if(reg >= 0xFF10 && reg <= 0xFF3F) {
}
else if (reg >= 0xFF10 && reg <= 0xFF3F)
{
showEvent = isWrite ? options.ShowApuRegisterWrites : options.ShowApuRegisterReads;
} else {
}
else
{
showEvent = isWrite ? options.ShowCpuRegisterWrites : options.ShowCpuRegisterReads;
}
break;
}
if(showEvent) {
if (showEvent)
{
_sentEvents.push_back(evt);
}
}
}
void GbEventManager::DrawEvent(DebugEventInfo& evt, bool drawBackground, uint32_t* buffer, EventViewerDisplayOptions& options)
void GbEventManager::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;
uint32_t ppuReadColor = options.PpuRegisterReadColor;
switch(evt.Type) {
switch (evt.Type)
{
default: break;
case DebugEventType::Breakpoint: color = options.BreakpointColor; break;
case DebugEventType::Irq: color = options.IrqColor; break;
case DebugEventType::Breakpoint: color = options.BreakpointColor;
break;
case DebugEventType::Irq: color = options.IrqColor;
break;
case DebugEventType::Register:
uint16_t reg = evt.Operation.Address & 0xFFFF;
if(reg >= 0xFE00 && reg <= 0xFE9F) {
if (reg >= 0xFE00 && reg <= 0xFE9F)
{
color = isWrite ? options.PpuRegisterWriteOamColor : ppuReadColor;
} else if(reg >= 0xFF42 && reg <= 0xFF43) {
}
else if (reg >= 0xFF42 && reg <= 0xFF43)
{
color = isWrite ? options.PpuRegisterWriteBgScrollColor : ppuReadColor;
} else if(reg >= 0x8000 && reg <= 0x9FFF) {
}
else if (reg >= 0x8000 && reg <= 0x9FFF)
{
color = isWrite ? options.PpuRegisterWriteVramColor : ppuReadColor;
} else if((reg >= 0xFF47 && reg <= 0xFF49) || (reg >= 0xFF68 && reg <= 0xFF6B)) {
}
else if ((reg >= 0xFF47 && reg <= 0xFF49) || (reg >= 0xFF68 && reg <= 0xFF6B))
{
color = isWrite ? options.PpuRegisterWriteCgramColor : ppuReadColor;
} else if(reg >= 0xFF4A && reg <= 0xFF4B) {
}
else if (reg >= 0xFF4A && reg <= 0xFF4B)
{
color = isWrite ? options.PpuRegisterWriteWindowColor : ppuReadColor;
} else if(reg >= 0xFF40 && reg <= 0xFF70) {
}
else if (reg >= 0xFF40 && reg <= 0xFF70)
{
color = isWrite ? options.PpuRegisterWriteOtherColor : ppuReadColor;
} else if(reg >= 0xFF10 && reg <= 0xFF3F) {
}
else if (reg >= 0xFF10 && reg <= 0xFF3F)
{
color = isWrite ? options.ApuRegisterWriteColor : options.ApuRegisterReadColor;
} else {
}
else
{
color = isWrite ? options.CpuRegisterWriteColor : options.CpuRegisterReadColor;
}
break;
}
if(drawBackground) {
if (drawBackground)
{
color = 0xFF000000 | ((color >> 1) & 0x7F7F7F);
} else {
}
else
{
color |= 0xFF000000;
}
@ -182,10 +231,13 @@ void GbEventManager::DrawEvent(DebugEventInfo& evt, bool drawBackground, uint32_
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) * GbEventManager::ScanlineWidth + x + j;
if(pos < 0 || pos >= GbEventManager::ScanlineWidth * (int)_scanlineCount * 2) {
if (pos < 0 || pos >= GbEventManager::ScanlineWidth * (int)_scanlineCount * 2)
{
continue;
}
buffer[pos] = color;
@ -202,9 +254,12 @@ uint32_t GbEventManager::TakeEventSnapshot(EventViewerDisplayOptions options)
uint16_t cycle = _ppu->GetState().Cycle;
uint16_t scanline = _ppu->GetState().Scanline;
if(scanline >= GbEventManager::VBlankScanline || scanline == 0) {
if (scanline >= GbEventManager::VBlankScanline || scanline == 0)
{
memcpy(_ppuBuffer, _ppu->GetEventViewerBuffer(), 456 * GbEventManager::ScreenHeight * sizeof(uint16_t));
} else {
}
else
{
uint32_t size = 456 * GbEventManager::ScreenHeight;
uint32_t offset = 456 * scanline;
memcpy(_ppuBuffer, _ppu->GetEventViewerBuffer(), offset * sizeof(uint16_t));
@ -222,13 +277,16 @@ void GbEventManager::GetDisplayBuffer(uint32_t* buffer, uint32_t bufferSize, Eve
{
auto lock = _lock.AcquireSafe();
if(_snapshotScanline < 0 || bufferSize < _scanlineCount * 2 * GbEventManager::ScanlineWidth * 4) {
if (_snapshotScanline < 0 || bufferSize < _scanlineCount * 2 * GbEventManager::ScanlineWidth * 4)
{
return;
}
uint16_t* src = _ppuBuffer;
for(uint32_t y = 0, len = GbEventManager::ScreenHeight*2; y < len; y++) {
for(uint32_t x = 0; x < GbEventManager::ScanlineWidth; x++) {
for (uint32_t y = 0, len = GbEventManager::ScreenHeight * 2; y < len; y++)
{
for (uint32_t x = 0; x < GbEventManager::ScanlineWidth; x++)
{
int srcOffset = (y >> 1) * 456 + (x >> 1);
buffer[y * GbEventManager::ScanlineWidth + x] = DefaultVideoFilter::ToArgb(src[srcOffset]);
}
@ -236,18 +294,22 @@ void GbEventManager::GetDisplayBuffer(uint32_t* buffer, uint32_t bufferSize, Eve
constexpr uint32_t currentScanlineColor = 0xFFFFFF55;
uint32_t scanlineOffset = _snapshotScanline * 2 * GbEventManager::ScanlineWidth;
if(_snapshotScanline != 0) {
for(int i = 0; i < GbEventManager::ScanlineWidth; i++) {
if (_snapshotScanline != 0)
{
for (int i = 0; i < GbEventManager::ScanlineWidth; i++)
{
buffer[scanlineOffset + i] = currentScanlineColor;
buffer[scanlineOffset + GbEventManager::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);
}
}

View file

@ -28,11 +28,14 @@ public:
uint8_t prgBank = _prgBank | (_ramBank << 5);
Map(0x4000, 0x7FFF, GbMemoryType::PrgRom, prgBank * prgBankSize, true);
if(_ramEnabled) {
if (_ramEnabled)
{
uint8_t ramBank = _mode ? _ramBank : 0;
Map(0xA000, 0xBFFF, GbMemoryType::CartRam, ramBank * ramBankSize, false);
_memoryManager->MapRegisters(0xA000, 0xBFFF, RegisterAccess::None);
} else {
}
else
{
Unmap(0xA000, 0xBFFF);
_memoryManager->MapRegisters(0xA000, 0xBFFF, RegisterAccess::Read);
}
@ -46,11 +49,16 @@ public:
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0x6000) {
case 0x0000: _ramEnabled = ((value & 0x0F) == 0x0A); break;
case 0x2000: _prgBank = std::max(1, value & 0x1F); break;
case 0x4000: _ramBank = value & 0x03; break;
case 0x6000: _mode = (value & 0x01) != 0; break;
switch (addr & 0x6000)
{
case 0x0000: _ramEnabled = ((value & 0x0F) == 0x0A);
break;
case 0x2000: _prgBank = std::max(1, value & 0x1F);
break;
case 0x4000: _ramBank = value & 0x03;
break;
case 0x6000: _mode = (value & 0x01) != 0;
break;
}
RefreshMappings();
}

View file

@ -15,7 +15,8 @@ public:
{
_memoryManager->MapRegisters(0x0000, 0x3FFF, RegisterAccess::Write);
for(int i = 0; i < 512; i++) {
for (int i = 0; i < 512; i++)
{
//Ensure cart RAM contains $F in the upper nibble, no matter the contents of save ram
_cartRam[i] |= 0xF0;
}
@ -29,12 +30,16 @@ public:
Map(0x4000, 0x7FFF, GbMemoryType::PrgRom, _prgBank * prgBankSize, true);
if(_ramEnabled) {
for(int i = 0; i < 16; i++) {
if (_ramEnabled)
{
for (int i = 0; i < 16; i++)
{
Map(0xA000 + 0x200 * i, 0xA1FF + 0x200 * i, GbMemoryType::CartRam, 0, false);
}
_memoryManager->MapRegisters(0xA000, 0xBFFF, RegisterAccess::Write);
} else {
}
else
{
Unmap(0xA000, 0xBFFF);
_memoryManager->MapRegisters(0xA000, 0xBFFF, RegisterAccess::Read);
}
@ -48,14 +53,20 @@ public:
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr >= 0xA000 && addr <= 0xBFFF) {
if (addr >= 0xA000 && addr <= 0xBFFF)
{
//Cut off the top 4 bits for all cart ram writes
//Set top nibble to $F to mimic open bus
_cartRam[addr & 0x1FF] = (value & 0x0F) | 0xF0;
} else {
switch(addr & 0x100) {
case 0x000: _ramEnabled = ((value & 0x0F) == 0x0A); break;
case 0x100: _prgBank = std::max(1, value & 0x0F); break;
}
else
{
switch (addr & 0x100)
{
case 0x000: _ramEnabled = ((value & 0x0F) == 0x0A);
break;
case 0x100: _prgBank = std::max(1, value & 0x0F);
break;
}
RefreshMappings();
}

View file

@ -32,12 +32,17 @@ public:
Map(0x0000, 0x3FFF, GbMemoryType::PrgRom, 0, true);
Map(0x4000, 0x7FFF, GbMemoryType::PrgRom, _prgBank * prgBankSize, true);
if(_ramRtcEnabled && _ramBank <= 3) {
if (_ramRtcEnabled && _ramBank <= 3)
{
Map(0xA000, 0xBFFF, GbMemoryType::CartRam, _ramBank, false);
_memoryManager->MapRegisters(0xA000, 0xBFFF, RegisterAccess::None);
} else if(_hasRtcTimer && _ramRtcEnabled && _ramBank >= 0x08 && _ramBank <= 0x0C) {
}
else if (_hasRtcTimer && _ramRtcEnabled && _ramBank >= 0x08 && _ramBank <= 0x0C)
{
_memoryManager->MapRegisters(0xA000, 0xBFFF, RegisterAccess::ReadWrite);
} else {
}
else
{
Unmap(0xA000, 0xBFFF);
_memoryManager->MapRegisters(0xA000, 0xBFFF, RegisterAccess::Read);
}
@ -45,13 +50,15 @@ public:
uint8_t ReadRegister(uint16_t addr) override
{
if(_ramRtcEnabled) {
if (_ramRtcEnabled)
{
//Disabled RAM/RTC registers returns 0xFF on reads (?)
return 0xFF;
}
//RTC register read (TODO)
switch(_ramBank) {
switch (_ramBank)
{
case 0x08: return _rtcRegisters[0]; //Seconds
case 0x09: return _rtcRegisters[1]; //Minutes
case 0x0A: return _rtcRegisters[2]; //Hours
@ -65,24 +72,37 @@ public:
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr <= 0x7FFF) {
switch(addr & 0x6000) {
case 0x0000: _ramRtcEnabled = ((value & 0x0F) == 0x0A); break;
case 0x2000: _prgBank = std::max<uint8_t>(1, value); break;
case 0x4000: _ramBank = value & 0x0F; break;
if (addr <= 0x7FFF)
{
switch (addr & 0x6000)
{
case 0x0000: _ramRtcEnabled = ((value & 0x0F) == 0x0A);
break;
case 0x2000: _prgBank = std::max<uint8_t>(1, value);
break;
case 0x4000: _ramBank = value & 0x0F;
break;
case 0x6000:
//RTC register read latch
break;
}
RefreshMappings();
} else if(addr >= 0xA000 && addr <= 0xBFFF) {
}
else if (addr >= 0xA000 && addr <= 0xBFFF)
{
//RTC register write (TODO)
switch(_ramBank) {
case 0x08: _rtcRegisters[0] = value; break; //Seconds
case 0x09: _rtcRegisters[1] = value; break; //Minutes
case 0x0A: _rtcRegisters[2] = value; break; //Hours
case 0x0B: _rtcRegisters[3] = value; break; //Day counter
case 0x0C: _rtcRegisters[4] = value; break; //Day counter (upper bit) + carry/halt flags
switch (_ramBank)
{
case 0x08: _rtcRegisters[0] = value;
break; //Seconds
case 0x09: _rtcRegisters[1] = value;
break; //Minutes
case 0x0A: _rtcRegisters[2] = value;
break; //Hours
case 0x0B: _rtcRegisters[3] = value;
break; //Day counter
case 0x0C: _rtcRegisters[4] = value;
break; //Day counter (upper bit) + carry/halt flags
}
}
}

View file

@ -25,10 +25,13 @@ public:
Map(0x0000, 0x3FFF, GbMemoryType::PrgRom, 0, true);
Map(0x4000, 0x7FFF, GbMemoryType::PrgRom, _prgBank * prgBankSize, true);
if(_ramEnabled) {
if (_ramEnabled)
{
Map(0xA000, 0xBFFF, GbMemoryType::CartRam, _ramBank * ramBankSize, false);
_memoryManager->MapRegisters(0xA000, 0xBFFF, RegisterAccess::None);
} else {
}
else
{
Unmap(0xA000, 0xBFFF);
_memoryManager->MapRegisters(0xA000, 0xBFFF, RegisterAccess::Read);
}
@ -42,7 +45,8 @@ public:
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0x7000) {
switch (addr & 0x7000)
{
case 0x0000:
case 0x1000:
_ramEnabled = (value == 0x0A);

View file

@ -18,7 +18,8 @@
#include "../Utilities/Serializer.h"
#include "../Utilities/HexUtilities.h"
void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer, GbDmaController* dmaController)
void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer,
GbDmaController* dmaController)
{
_highRam = gameboy->DebugGetMemory(SnesMemoryType::GbHighRam);
@ -65,9 +66,11 @@ void GbMemoryManager::RefreshMappings()
_cart->RefreshMappings();
if(!_state.DisableBootRom) {
if (!_state.DisableBootRom)
{
Map(0x0000, 0x00FF, GbMemoryType::BootRom, 0, true);
if(_gameboy->IsCgb()) {
if (_gameboy->IsCgb())
{
Map(0x0200, 0x08FF, GbMemoryType::BootRom, 0x200, true);
}
}
@ -79,13 +82,16 @@ void GbMemoryManager::Exec()
_state.ApuCycleCount += _state.CgbHighSpeed ? 1 : 2;
_timer->Exec();
_ppu->Exec();
if((_state.CycleCount & 0x03) == 0) {
if ((_state.CycleCount & 0x03) == 0)
{
_dmaController->Exec();
}
if(_state.SerialBitCount && (_state.CycleCount & 0x1FF) == 0) {
if (_state.SerialBitCount && (_state.CycleCount & 0x1FF) == 0)
{
_state.SerialData = (_state.SerialData << 1) | 0x01;
if(--_state.SerialBitCount == 0) {
if (--_state.SerialBitCount == 0)
{
//"It will be notified that the transfer is complete in two ways:
//SC's Bit 7 will be cleared"
_state.SerialControl &= 0x7F;
@ -98,7 +104,8 @@ void GbMemoryManager::Exec()
void GbMemoryManager::MapRegisters(uint16_t start, uint16_t end, RegisterAccess access)
{
for(int i = start; i < end; i += 0x100) {
for (int i = start; i < end; i += 0x100)
{
_state.IsReadRegister[i >> 8] = ((int)access & (int)RegisterAccess::Read) != 0;
_state.IsWriteRegister[i >> 8] = ((int)access & (int)RegisterAccess::Write) != 0;
}
@ -109,13 +116,16 @@ void GbMemoryManager::Map(uint16_t start, uint16_t end, GbMemoryType type, uint3
uint8_t* src = _gameboy->DebugGetMemory((SnesMemoryType)type);
uint32_t size = _gameboy->DebugGetMemorySize((SnesMemoryType)type);
if(size > 0) {
while(offset >= size) {
if (size > 0)
{
while (offset >= size)
{
offset -= size;
}
src += offset;
for(int i = start; i < end; i += 0x100) {
for (int i = start; i < end; i += 0x100)
{
_reads[i >> 8] = src;
_writes[i >> 8] = readonly ? nullptr : src;
@ -123,23 +133,28 @@ void GbMemoryManager::Map(uint16_t start, uint16_t end, GbMemoryType type, uint3
_state.MemoryOffset[i >> 8] = offset;
_state.MemoryAccessType[i >> 8] = readonly ? RegisterAccess::Read : RegisterAccess::ReadWrite;
if(src) {
if (src)
{
src += 0x100;
offset = (offset + 0x100);
if(offset >= size) {
if (offset >= size)
{
offset = 0;
src = _gameboy->DebugGetMemory((SnesMemoryType)type);
}
}
}
} else {
}
else
{
Unmap(start, end);
}
}
void GbMemoryManager::Unmap(uint16_t start, uint16_t end)
{
for(int i = start; i < end; i += 0x100) {
for (int i = start; i < end; i += 0x100)
{
_reads[i >> 8] = nullptr;
_writes[i >> 8] = nullptr;
@ -153,9 +168,12 @@ template<MemoryOperationType opType>
uint8_t GbMemoryManager::Read(uint16_t addr)
{
uint8_t value = 0;
if(_state.IsReadRegister[addr >> 8]) {
if (_state.IsReadRegister[addr >> 8])
{
value = ReadRegister(addr);
} else if(_reads[addr >> 8]) {
}
else if (_reads[addr >> 8])
{
value = _reads[addr >> 8][(uint8_t)addr];
}
_console->ProcessMemoryRead<CpuType::Gameboy>(addr, value, opType);
@ -176,9 +194,12 @@ void GbMemoryManager::WriteDma(uint16_t addr, uint8_t value)
uint8_t GbMemoryManager::ReadDma(uint16_t addr)
{
uint8_t value = 0;
if(_reads[addr >> 8]) {
if (_reads[addr >> 8])
{
value = _reads[addr >> 8][(uint8_t)addr];
} else if(addr >= 0x8000 && addr <= 0x9FFF) {
}
else if (addr >= 0x8000 && addr <= 0x9FFF)
{
value = ReadRegister(addr);
}
_console->ProcessMemoryRead<CpuType::Gameboy>(addr, value, MemoryOperationType::DmaRead);
@ -189,23 +210,32 @@ template<MemoryOperationType type>
void GbMemoryManager::Write(uint16_t addr, uint8_t value)
{
_console->ProcessMemoryWrite<CpuType::Gameboy>(addr, value, type);
if(_state.IsWriteRegister[addr >> 8]) {
if (_state.IsWriteRegister[addr >> 8])
{
WriteRegister(addr, value);
} else if(_writes[addr >> 8]) {
}
else if (_writes[addr >> 8])
{
_writes[addr >> 8][(uint8_t)addr] = value;
}
}
uint8_t GbMemoryManager::DebugRead(uint16_t addr)
{
if(_state.IsReadRegister[addr >> 8]) {
if(addr >= 0xFE00) {
if (_state.IsReadRegister[addr >> 8])
{
if (addr >= 0xFE00)
{
return PeekRegister(addr);
} else {
}
else
{
//Avoid potential read side effects
return 0xFF;
}
} else if(_reads[addr >> 8]) {
}
else if (_reads[addr >> 8])
{
return _reads[addr >> 8][(uint8_t)addr];
}
return 0;
@ -213,16 +243,20 @@ uint8_t GbMemoryManager::DebugRead(uint16_t addr)
void GbMemoryManager::DebugWrite(uint16_t addr, uint8_t value)
{
if(_state.IsWriteRegister[addr >> 8]) {
if (_state.IsWriteRegister[addr >> 8])
{
//Do not write to registers via debug tools
} else if(_writes[addr >> 8]) {
}
else if (_writes[addr >> 8])
{
_writes[addr >> 8][(uint8_t)addr] = value;
}
}
uint8_t* GbMemoryManager::GetMappedBlock(uint16_t addr)
{
if(_reads[addr >> 8]) {
if (_reads[addr >> 8])
{
return _reads[addr >> 8];
}
return nullptr;
@ -231,32 +265,50 @@ uint8_t* GbMemoryManager::GetMappedBlock(uint16_t addr)
uint8_t GbMemoryManager::PeekRegister(uint16_t addr)
{
//Peek on oam/vram to avoid triggering the invalid oam/vram access break options
if(addr >= 0xFE00 && addr <= 0xFE9F) {
if (addr >= 0xFE00 && addr <= 0xFE9F)
{
return _ppu->PeekOam((uint8_t)addr);
} else if(addr >= 0x8000 && addr <= 0x9FFF) {
}
else if (addr >= 0x8000 && addr <= 0x9FFF)
{
return _ppu->PeekVram(addr);
} else if(addr >= 0xFF10 && addr <= 0xFF3F) {
}
else if (addr >= 0xFF10 && addr <= 0xFF3F)
{
return _apu->Peek(addr);
} else {
}
else
{
return ReadRegister(addr);
}
}
uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
{
if(addr >= 0xFF00) {
if(addr == 0xFFFF) {
if (addr >= 0xFF00)
{
if (addr == 0xFFFF)
{
return _state.IrqEnabled; //IE - Interrupt Enable (R/W)
} else if(addr == 0xFF46) {
}
else if (addr == 0xFF46)
{
return _dmaController->Read();
} else if(addr >= 0xFF80) {
}
else if (addr >= 0xFF80)
{
return _highRam[addr & 0x7F]; //80-FE
} else if(addr >= 0xFF4C) {
if(_gameboy->IsCgb()) {
switch(addr) {
}
else if (addr >= 0xFF4C)
{
if (_gameboy->IsCgb())
{
switch (addr)
{
//FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch
case 0xFF4D:
if(_ppu->IsCgbEnabled()) {
if (_ppu->IsCgbEnabled())
{
return (
(_state.CgbHighSpeed ? 0x80 : 0) |
(_state.CgbSwitchSpeedRequest ? 0x01 : 0) |
@ -269,7 +321,10 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
return _ppu->IsCgbEnabled() ? _dmaController->ReadCgb(addr) : 0xFF;
case 0xFF4F: //CGB - VRAM bank
case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: //CGB - Palette
case 0xFF68:
case 0xFF69:
case 0xFF6A:
case 0xFF6B: //CGB - Palette
return _ppu->ReadCgbRegister(addr);
//FF70 - SVBK - CGB Mode Only - WRAM Bank
@ -278,42 +333,60 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
case 0xFF73: return _state.CgbRegFF73;
case 0xFF74:
if(_ppu->IsCgbEnabled()) {
if (_ppu->IsCgbEnabled())
{
return _state.CgbRegFF74;
}
return 0xFF;
case 0xFF75: return _state.CgbRegFF75 | 0x8F;
case 0xFF76: case 0xFF77:
case 0xFF76:
case 0xFF77:
return _apu->ReadCgbRegister(addr);
}
}
LogDebug("[Debug] GB - Missing read handler: $" + HexUtilities::ToHex(addr));
return 0xFF; //4C-7F, open bus
} else if(addr >= 0xFF40) {
}
else if (addr >= 0xFF40)
{
return _ppu->Read(addr); //40-4B
} else if(addr >= 0xFF10) {
}
else if (addr >= 0xFF10)
{
return _apu->Read(addr); //10-3F
} else {
}
else
{
//00-0F
switch(addr) {
case 0xFF00: return ReadInputPort(); break;
switch (addr)
{
case 0xFF00: return ReadInputPort();
break;
case 0xFF01: return _state.SerialData; //SB - Serial transfer data (R/W)
case 0xFF02: return _state.SerialControl | 0x7E; //SC - Serial Transfer Control (R/W)
case 0xFF04: case 0xFF05: case 0xFF06: case 0xFF07:
case 0xFF04:
case 0xFF05:
case 0xFF06:
case 0xFF07:
return _timer->Read(addr);
case 0xFF0F: return _state.IrqRequests | 0xE0; break; //IF - Interrupt flags (R/W)
case 0xFF0F: return _state.IrqRequests | 0xE0;
break; //IF - Interrupt flags (R/W)
default: return 0xFF; //Open bus
}
}
} else if(addr >= 0xFE00) {
}
else if (addr >= 0xFE00)
{
return _ppu->ReadOam((uint8_t)addr);
} else if(addr >= 0x8000 && addr <= 0x9FFF) {
}
else if (addr >= 0x8000 && addr <= 0x9FFF)
{
return _ppu->ReadVram(addr);
}
@ -322,100 +395,150 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value)
{
if(addr >= 0xFF00) {
if(addr == 0xFFFF) {
if (addr >= 0xFF00)
{
if (addr == 0xFFFF)
{
_state.IrqEnabled = value; //IE register
} else if(addr == 0xFF46) {
}
else if (addr == 0xFF46)
{
_dmaController->Write(value);
} else if(addr >= 0xFF80) {
}
else if (addr >= 0xFF80)
{
_highRam[addr & 0x7F] = value; //80-FE
} else if(addr >= 0xFF4C) {
}
else if (addr >= 0xFF4C)
{
//4C-7F
if(addr == 0xFF50) {
if(value & 0x01) {
if (addr == 0xFF50)
{
if (value & 0x01)
{
_state.DisableBootRom = true;
RefreshMappings();
}
} else if(_gameboy->IsCgb()) {
switch(addr) {
}
else if (_gameboy->IsCgb())
{
switch (addr)
{
case 0xFF4D:
//FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch
if(_ppu->IsCgbEnabled()) {
if (_ppu->IsCgbEnabled())
{
_state.CgbSwitchSpeedRequest = (value & 0x01) != 0;
}
break;
case 0xFF51: case 0xFF52: case 0xFF53: case 0xFF54: case 0xFF55: //CGB - DMA
if(_ppu->IsCgbEnabled()) {
case 0xFF51:
case 0xFF52:
case 0xFF53:
case 0xFF54:
case 0xFF55: //CGB - DMA
if (_ppu->IsCgbEnabled())
{
_dmaController->WriteCgb(addr, value);
}
break;
case 0xFF4C: //CGB - "LCDMODE", set by boot rom to turn off CGB features for the LCD for DMG games
if(!_state.DisableBootRom) {
if (!_state.DisableBootRom)
{
_ppu->WriteCgbRegister(addr, value);
}
break;
case 0xFF4F: //CGB - VRAM banking
case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: //CGB - Palette
case 0xFF68:
case 0xFF69:
case 0xFF6A:
case 0xFF6B: //CGB - Palette
_ppu->WriteCgbRegister(addr, value);
break;
case 0xFF70:
//FF70 - SVBK - CGB Mode Only - WRAM Bank
if(_ppu->IsCgbEnabled()) {
if (_ppu->IsCgbEnabled())
{
_state.CgbWorkRamBank = std::max(1, value & 0x07);
RefreshMappings();
}
break;
case 0xFF72: _state.CgbRegFF72 = value; break;
case 0xFF73: _state.CgbRegFF73 = value; break;
case 0xFF72: _state.CgbRegFF72 = value;
break;
case 0xFF73: _state.CgbRegFF73 = value;
break;
case 0xFF74:
if(_ppu->IsCgbEnabled()) {
if (_ppu->IsCgbEnabled())
{
_state.CgbRegFF74 = value;
}
break;
case 0xFF75: _state.CgbRegFF75 = value; break;
case 0xFF75: _state.CgbRegFF75 = value;
break;
default:
LogDebug("[Debug] GBC - Missing write handler: $" + HexUtilities::ToHex(addr));
break;
}
}
} else if(addr >= 0xFF40) {
}
else if (addr >= 0xFF40)
{
_ppu->Write(addr, value); //40-4B
} else if(addr >= 0xFF10) {
}
else if (addr >= 0xFF10)
{
_apu->Write(addr, value); //10-3F
} else {
}
else
{
//00-0F
switch(addr) {
case 0xFF00: WriteInputPort(value); break;
case 0xFF01: _state.SerialData = value; break; //FF01 - SB - Serial transfer data (R/W)
switch (addr)
{
case 0xFF00: WriteInputPort(value);
break;
case 0xFF01: _state.SerialData = value;
break; //FF01 - SB - Serial transfer data (R/W)
case 0xFF02:
//FF02 - SC - Serial Transfer Control (R/W)
_state.SerialControl = value & (_gameboy->IsCgb() ? 0x83 : 0x81);
if((_state.SerialControl & 0x80) && (_state.SerialControl & 0x01)) {
if ((_state.SerialControl & 0x80) && (_state.SerialControl & 0x01))
{
_state.SerialBitCount = 8;
} else {
}
else
{
_state.SerialBitCount = 0;
}
break;
case 0xFF04: case 0xFF05: case 0xFF06: case 0xFF07:
case 0xFF04:
case 0xFF05:
case 0xFF06:
case 0xFF07:
_timer->Write(addr, value);
break;
case 0xFF0F: _state.IrqRequests = value & 0x1F; break; //IF - Interrupt flags (R/W)
case 0xFF0F: _state.IrqRequests = value & 0x1F;
break; //IF - Interrupt flags (R/W)
}
}
} else if(addr >= 0xFE00) {
}
else if (addr >= 0xFE00)
{
_ppu->WriteOam((uint8_t)addr, value, false);
} else if(addr >= 0x8000 && addr <= 0x9FFF) {
}
else if (addr >= 0x8000 && addr <= 0x9FFF)
{
_ppu->WriteVram(addr, value);
} else {
}
else
{
_cart->WriteRegister(addr, value);
}
}
@ -433,16 +556,26 @@ void GbMemoryManager::ClearIrqRequest(uint8_t source)
uint8_t GbMemoryManager::ProcessIrqRequests()
{
uint8_t irqsToProcess = _state.IrqRequests & _state.IrqEnabled;
if(irqsToProcess) {
if(irqsToProcess & GbIrqSource::VerticalBlank) {
if (irqsToProcess)
{
if (irqsToProcess & GbIrqSource::VerticalBlank)
{
return GbIrqSource::VerticalBlank;
} else if(irqsToProcess & GbIrqSource::LcdStat) {
}
else if (irqsToProcess & GbIrqSource::LcdStat)
{
return GbIrqSource::LcdStat;
} else if(irqsToProcess & GbIrqSource::Timer) {
}
else if (irqsToProcess & GbIrqSource::Timer)
{
return GbIrqSource::Timer;
} else if(irqsToProcess & GbIrqSource::Serial) {
}
else if (irqsToProcess & GbIrqSource::Serial)
{
return GbIrqSource::Serial;
} else if(irqsToProcess & GbIrqSource::Joypad) {
}
else if (irqsToProcess & GbIrqSource::Joypad)
{
return GbIrqSource::Joypad;
}
}
@ -487,28 +620,39 @@ uint8_t GbMemoryManager::ReadInputPort()
//Bit 0 - P10 Input Right or Button A (0=Pressed) (Read Only)
uint8_t result = 0x0F;
if(_gameboy->IsSgb()) {
if (_gameboy->IsSgb())
{
SuperGameboy* sgb = _console->GetCartridge()->GetSuperGameboy();
if((_state.InputSelect & 0x30) == 0x30) {
if ((_state.InputSelect & 0x30) == 0x30)
{
result = sgb->GetInputIndex();
} else {
if(!(_state.InputSelect & 0x20)) {
}
else
{
if (!(_state.InputSelect & 0x20))
{
result &= sgb->GetInput() >> 4;
}
if(!(_state.InputSelect & 0x10)) {
if (!(_state.InputSelect & 0x10))
{
result &= sgb->GetInput() & 0x0F;
}
}
} else {
}
else
{
BaseControlDevice* controller = (SnesController*)_controlManager->GetControlDevice(0).get();
if(controller && controller->GetControllerType() == ControllerType::SnesController) {
if(!(_state.InputSelect & 0x20)) {
if (controller && controller->GetControllerType() == ControllerType::SnesController)
{
if (!(_state.InputSelect & 0x20))
{
result &= ~(controller->IsPressed(SnesController::A) ? 0x01 : 0);
result &= ~(controller->IsPressed(SnesController::B) ? 0x02 : 0);
result &= ~(controller->IsPressed(SnesController::Select) ? 0x04 : 0);
result &= ~(controller->IsPressed(SnesController::Start) ? 0x08 : 0);
}
if(!(_state.InputSelect & 0x10)) {
if (!(_state.InputSelect & 0x10))
{
result &= ~(controller->IsPressed(SnesController::Right) ? 0x01 : 0);
result &= ~(controller->IsPressed(SnesController::Left) ? 0x02 : 0);
result &= ~(controller->IsPressed(SnesController::Up) ? 0x04 : 0);
@ -523,7 +667,8 @@ uint8_t GbMemoryManager::ReadInputPort()
void GbMemoryManager::WriteInputPort(uint8_t value)
{
_state.InputSelect = value;
if(_gameboy->IsSgb()) {
if (_gameboy->IsSgb())
{
_console->GetCartridge()->GetSuperGameboy()->ProcessInputPortWrite(value & 0x30);
}
}
@ -542,10 +687,13 @@ void GbMemoryManager::Serialize(Serializer& s)
s.StreamArray(_state.IsReadRegister, 0x100);
s.StreamArray(_state.IsWriteRegister, 0x100);
if(!s.IsSaving()) {
if (!s.IsSaving())
{
//Restore mappings based on state
for(int i = 0; i < 0x100; i++) {
Map(i*0x100, i*0x100+0xFF, _state.MemoryType[i], _state.MemoryOffset[i], _state.MemoryAccessType[i] == RegisterAccess::ReadWrite ? false : true);
for (int i = 0; i < 0x100; i++)
{
Map(i * 0x100, i * 0x100 + 0xFF, _state.MemoryType[i], _state.MemoryOffset[i],
_state.MemoryAccessType[i] == RegisterAccess::ReadWrite ? false : true);
}
RefreshMappings();
}

View file

@ -42,7 +42,8 @@ public:
GbMemoryManagerState GetState();
void Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer, GbDmaController* dmaController);
void Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer,
GbDmaController* dmaController);
void MapRegisters(uint16_t start, uint16_t end, RegisterAccess access);
void Map(uint16_t start, uint16_t end, GbMemoryType type, uint32_t offset, bool readonly);
void Unmap(uint16_t start, uint16_t end);

View file

@ -26,9 +26,11 @@ void GbNoiseChannel::Disable()
void GbNoiseChannel::ClockLengthCounter()
{
if(_state.LengthEnabled && _state.Length > 0) {
if (_state.LengthEnabled && _state.Length > 0)
{
_state.Length--;
if(_state.Length == 0) {
if (_state.Length == 0)
{
//"Length becoming 0 should clear status"
_state.Enabled = false;
}
@ -37,13 +39,18 @@ void GbNoiseChannel::ClockLengthCounter()
void GbNoiseChannel::ClockEnvelope()
{
if(_state.EnvTimer > 0) {
if (_state.EnvTimer > 0)
{
_state.EnvTimer--;
if(_state.EnvTimer == 0) {
if(_state.EnvRaiseVolume && _state.Volume < 0x0F) {
if (_state.EnvTimer == 0)
{
if (_state.EnvRaiseVolume && _state.Volume < 0x0F)
{
_state.Volume++;
} else if(!_state.EnvRaiseVolume && _state.Volume > 0) {
}
else if (!_state.EnvRaiseVolume && _state.Volume > 0)
{
_state.Volume--;
}
@ -59,29 +66,37 @@ uint8_t GbNoiseChannel::GetOutput()
uint32_t GbNoiseChannel::GetPeriod()
{
if(_state.Divisor == 0) {
if (_state.Divisor == 0)
{
return 8 << _state.PeriodShift;
} else {
}
else
{
return (16 * _state.Divisor) << _state.PeriodShift;
}
}
void GbNoiseChannel::Exec(uint32_t clocksToRun)
{
if(_state.PeriodShift >= 14) {
if (_state.PeriodShift >= 14)
{
//Using a noise channel clock shift of 14 or 15 results in the LFSR receiving no clocks.
return;
}
_state.Timer -= clocksToRun;
if(_state.Enabled) {
if (_state.Enabled)
{
_state.Output = ((_state.ShiftRegister & 0x01) ^ 0x01) * _state.Volume;
} else {
}
else
{
_state.Output = 0;
}
if(_state.Timer == 0) {
if (_state.Timer == 0)
{
_state.Timer = GetPeriod();
//When clocked by the frequency timer, the low two bits (0 and 1) are XORed, all bits are shifted right by one,
@ -90,7 +105,8 @@ void GbNoiseChannel::Exec(uint32_t clocksToRun)
uint8_t xorResult = (_state.ShiftRegister & 0x01) ^ (shiftedValue & 0x01);
_state.ShiftRegister = (xorResult << 14) | shiftedValue;
if(_state.ShortWidthMode) {
if (_state.ShortWidthMode)
{
//If width mode is 1 (NR43), the XOR result is ALSO put into bit 6 AFTER the shift, resulting in a 7-bit LFSR.
_state.ShiftRegister &= ~0x40;
_state.ShiftRegister |= (xorResult << 6);
@ -103,7 +119,8 @@ uint8_t GbNoiseChannel::Read(uint16_t addr)
constexpr uint8_t openBusBits[5] = {0xFF, 0xFF, 0x00, 0x00, 0xBF};
uint8_t value = 0;
switch(addr) {
switch (addr)
{
case 2:
value = (
(_state.EnvVolume << 4) |
@ -120,7 +137,8 @@ uint8_t GbNoiseChannel::Read(uint16_t addr)
);
break;
case 4: value = _state.LengthEnabled ? 0x40 : 0; break;
case 4: value = _state.LengthEnabled ? 0x40 : 0;
break;
}
return value | openBusBits[addr];
@ -128,7 +146,8 @@ uint8_t GbNoiseChannel::Read(uint16_t addr)
void GbNoiseChannel::Write(uint16_t addr, uint8_t value)
{
switch(addr) {
switch (addr)
{
case 0: break;
case 1:
_state.Length = 64 - (value & 0x3F);
@ -139,7 +158,8 @@ void GbNoiseChannel::Write(uint16_t addr, uint8_t value)
_state.EnvRaiseVolume = (value & 0x08) != 0;
_state.EnvVolume = (value & 0xF0) >> 4;
if(!(value & 0xF8)) {
if (!(value & 0xF8))
{
_state.Enabled = false;
}
break;
@ -150,8 +170,10 @@ void GbNoiseChannel::Write(uint16_t addr, uint8_t value)
_state.Divisor = value & 0x07;
break;
case 4: {
if(value & 0x80) {
case 4:
{
if (value & 0x80)
{
//Writing a value to NRx4 with bit 7 set causes the following things to occur :
//Channel is enabled, if volume is not 0 or raise volume flag is set
@ -164,7 +186,8 @@ void GbNoiseChannel::Write(uint16_t addr, uint8_t value)
_state.ShiftRegister = 0x7FFF;
//If length counter is zero, it is set to 64 (256 for wave channel).
if(_state.Length == 0) {
if (_state.Length == 0)
{
_state.Length = 64;
_state.LengthEnabled = false;
}

View file

@ -17,7 +17,8 @@
constexpr uint16_t evtColors[6] = {0x18C6, 0x294A, 0x108C, 0x4210, 0x3084, 0x1184};
void GbPpu::Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager, GbDmaController* dmaController, uint8_t* vram, uint8_t* oam)
void GbPpu::Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager, GbDmaController* dmaController,
uint8_t* vram, uint8_t* oam)
{
_console = console;
_gameboy = gameboy;
@ -76,10 +77,12 @@ uint16_t* GbPpu::GetPreviousEventViewerBuffer()
void GbPpu::Exec()
{
if(!_state.LcdEnabled) {
if (!_state.LcdEnabled)
{
//LCD is disabled, prevent IRQs, etc.
//Not quite correct in terms of frame pacing
if(_gameboy->GetApuCycleCount() - _lastFrameTime > 70224) {
if (_gameboy->GetApuCycleCount() - _lastFrameTime > 70224)
{
//More than a full frame's worth of time has passed since the last frame, send another blank frame
_lastFrameTime = _gameboy->GetApuCycleCount();
SendFrame();
@ -88,9 +91,11 @@ void GbPpu::Exec()
}
uint8_t cyclesToRun = _memoryManager->IsHighSpeed() ? 1 : 2;
for(int i = 0; i < cyclesToRun; i++) {
for (int i = 0; i < cyclesToRun; i++)
{
_state.Cycle++;
if(_state.IdleCycles > 0) {
if (_state.IdleCycles > 0)
{
_state.IdleCycles--;
ProcessPpuCycle();
continue;
@ -104,32 +109,44 @@ void GbPpu::ExecCycle()
{
PpuMode oldMode = _state.IrqMode;
if(_state.Scanline < 144) {
if(_state.Scanline == 0 && _isFirstFrame) {
if (_state.Scanline < 144)
{
if (_state.Scanline == 0 && _isFirstFrame)
{
ProcessFirstScanlineAfterPowerOn();
} else {
}
else
{
ProcessVisibleScanline();
}
} else {
}
else
{
ProcessVblankScanline();
}
if(_state.Mode == PpuMode::Drawing) {
if (_state.Mode == PpuMode::Drawing)
{
RunDrawCycle();
if(_drawnPixels == 160) {
if (_drawnPixels == 160)
{
//Mode turns to hblank on the same cycle as the last pixel is output (IRQ is on next cycle)
_state.Mode = PpuMode::HBlank;
if(_state.Scanline < 143) {
if (_state.Scanline < 143)
{
//"This mode will transfer one block (16 bytes) during each H-Blank. No data is transferred during VBlank (LY = 143 153)"
_dmaController->ProcessHdma();
}
}
} else if(_state.Mode == PpuMode::OamEvaluation) {
}
else if (_state.Mode == PpuMode::OamEvaluation)
{
RunSpriteEvaluation();
}
bool coincidenceFlag = (_state.LyCompare == _state.LyForCompare);
if(_state.IrqMode != oldMode || _state.LyCoincidenceFlag != coincidenceFlag) {
if (_state.IrqMode != oldMode || _state.LyCoincidenceFlag != coincidenceFlag)
{
_state.LyCoincidenceFlag = coincidenceFlag;
UpdateStatIrq();
}
@ -139,17 +156,21 @@ void GbPpu::ExecCycle()
void GbPpu::ProcessVblankScanline()
{
switch(_state.Cycle) {
switch (_state.Cycle)
{
case 2:
if(_state.Scanline == 144) {
if (_state.Scanline == 144)
{
_state.IrqMode = PpuMode::OamEvaluation;
}
break;
case 4:
if(_state.Scanline < 153) {
if (_state.Scanline < 153)
{
_state.LyForCompare = _state.Scanline;
if(_state.Scanline == 144) {
if (_state.Scanline == 144)
{
_state.Mode = PpuMode::VBlank;
_state.IrqMode = PpuMode::VBlank;
_windowCounter = -1;
@ -160,20 +181,23 @@ void GbPpu::ProcessVblankScanline()
break;
case 6:
if(_state.Scanline == 153) {
if (_state.Scanline == 153)
{
_state.Ly = 0;
_state.LyForCompare = _state.Scanline;
}
break;
case 8:
if(_state.Scanline == 153) {
if (_state.Scanline == 153)
{
_state.LyForCompare = -1;
}
break;
case 12:
if(_state.Scanline == 153) {
if (_state.Scanline == 153)
{
_state.LyForCompare = 0;
}
_state.IdleCycles = 456 - 12 - 1;
@ -183,16 +207,22 @@ void GbPpu::ProcessVblankScanline()
_state.Cycle = 0;
_state.Scanline++;
if(_state.Scanline == 154) {
if (_state.Scanline == 154)
{
_state.Scanline = 0;
_state.Ly = 0;
_state.LyForCompare = 0;
if(_console->IsDebugging()) {
if (_console->IsDebugging())
{
_console->ProcessEvent(EventType::GbStartFrame);
_currentEventViewerBuffer = _currentEventViewerBuffer == _eventViewerBuffers[0] ? _eventViewerBuffers[1] : _eventViewerBuffers[0];
_currentEventViewerBuffer = _currentEventViewerBuffer == _eventViewerBuffers[0]
? _eventViewerBuffers[1]
: _eventViewerBuffers[0];
}
} else {
}
else
{
_state.Ly = _state.Scanline;
_state.LyForCompare = -1;
}
@ -202,14 +232,16 @@ void GbPpu::ProcessVblankScanline()
void GbPpu::ProcessFirstScanlineAfterPowerOn()
{
if(_drawnPixels == 160) {
if (_drawnPixels == 160)
{
//IRQ flag for Hblank is 1 cycle late compared to the mode register
_state.IrqMode = PpuMode::HBlank;
_drawnPixels = 0;
_state.IdleCycles = 448 - _state.Cycle - 1;
}
switch(_state.Cycle) {
switch (_state.Cycle)
{
case 1:
_state.IrqMode = PpuMode::NoIrq;
break;
@ -240,22 +272,27 @@ void GbPpu::ProcessFirstScanlineAfterPowerOn()
void GbPpu::ProcessVisibleScanline()
{
if(_drawnPixels == 160) {
if (_drawnPixels == 160)
{
//IRQ flag for Hblank is 1 cycle late compared to the mode register
_state.IrqMode = PpuMode::HBlank;
_drawnPixels = 0;
_state.IdleCycles = 456 - _state.Cycle - 1;
}
switch(_state.Cycle) {
switch (_state.Cycle)
{
case 3:
_state.Ly = _state.Scanline;
if(_state.Scanline > 0) {
if (_state.Scanline > 0)
{
//On scanlines 1-143, the OAM IRQ fires 1 cycle early
_state.IrqMode = PpuMode::OamEvaluation;
_state.LyForCompare = -1;
} else {
}
else
{
//On scanline 0, hblank gets set for 1 cycle here
_state.Mode = PpuMode::HBlank;
}
@ -291,7 +328,8 @@ void GbPpu::ProcessVisibleScanline()
case 456:
_state.Cycle = 0;
_state.Scanline++;
if(_state.Scanline == 144) {
if (_state.Scanline == 144)
{
_state.Ly = 144;
_state.LyForCompare = -1;
}
@ -301,14 +339,20 @@ void GbPpu::ProcessVisibleScanline()
void GbPpu::ProcessPpuCycle()
{
if(_console->IsDebugging()) {
if (_console->IsDebugging())
{
_console->ProcessPpuCycle<CpuType::Gameboy>();
if(_state.Mode <= PpuMode::OamEvaluation) {
if (_state.Mode <= PpuMode::OamEvaluation)
{
_currentEventViewerBuffer[456 * _state.Scanline + _state.Cycle] = evtColors[(int)_state.Mode];
} else if(_prevDrawnPixels != _drawnPixels && _drawnPixels > 0) {
}
else if (_prevDrawnPixels != _drawnPixels && _drawnPixels > 0)
{
uint16_t color = _currentBuffer[_state.Scanline * 256 + (_drawnPixels - 1)];
_currentEventViewerBuffer[456 * _state.Scanline + _state.Cycle] = color;
} else {
}
else
{
_currentEventViewerBuffer[456 * _state.Scanline + _state.Cycle] = evtColors[(int)_evtColor];
}
_prevDrawnPixels = _drawnPixels;
@ -317,14 +361,16 @@ void GbPpu::ProcessPpuCycle()
void GbPpu::RunDrawCycle()
{
if(_rendererIdle) {
if (_rendererIdle)
{
//Idle cycles
_evtColor = EvtColor::RenderingIdle;
return;
}
bool fetchWindow = _latchWindowEnabled && _drawnPixels >= _latchWindowX - 7 && _state.Scanline >= _latchWindowY;
if(_fetchWindow != fetchWindow) {
if (_fetchWindow != fetchWindow)
{
//Switched between window & background, reset fetcher & pixel FIFO
_fetchWindow = fetchWindow;
_fetchColumn = 0;
@ -339,39 +385,55 @@ void GbPpu::RunDrawCycle()
}
FindNextSprite();
if(_fetchSprite >= 0 && _bgFetcher.Step >= 5 && _bgFifo.Size > 0) {
if (_fetchSprite >= 0 && _bgFetcher.Step >= 5 && _bgFifo.Size > 0)
{
_evtColor = EvtColor::RenderingOamLoad;
ClockSpriteFetcher();
FindNextSprite();
return;
}
if(_fetchSprite == -1 && _bgFifo.Size > 0) {
if(_drawnPixels >= 0) {
if (_fetchSprite == -1 && _bgFifo.Size > 0)
{
if (_drawnPixels >= 0)
{
uint16_t outOffset = _state.Scanline * 256 + _drawnPixels;
GbFifoEntry entry = _bgFifo.Content[_bgFifo.Position];
GbFifoEntry sprite = _oamFifo.Content[_oamFifo.Position];
if(sprite.Color != 0 && (entry.Color == 0 || (!(sprite.Attributes & 0x80) && !(entry.Attributes & 0x80)) || (_state.CgbEnabled && !_state.BgEnabled))) {
if (sprite.Color != 0 && (entry.Color == 0 || (!(sprite.Attributes & 0x80) && !(entry.Attributes & 0x80)) || (
_state.CgbEnabled && !_state.BgEnabled)))
{
//Use sprite pixel if:
// -BG color is 0, OR
// -Sprite is background priority AND BG does not have its priority bit set, OR
// -On CGB, the "bg enabled" flag is cleared, causing all sprites to appear above BG tiles
if(_state.CgbEnabled) {
if (_state.CgbEnabled)
{
_currentBuffer[outOffset] = _state.CgbObjPalettes[sprite.Color | ((sprite.Attributes & 0x07) << 2)];
} else {
uint8_t colorIndex = (((sprite.Attributes & 0x10) ? _state.ObjPalette1 : _state.ObjPalette0) >> (sprite.Color * 2)) & 0x03;
if(_gameboy->IsSgb()) {
}
else
{
uint8_t colorIndex = (((sprite.Attributes & 0x10) ? _state.ObjPalette1 : _state.ObjPalette0) >> (sprite.
Color * 2)) & 0x03;
if (_gameboy->IsSgb())
{
_gameboy->GetSgb()->WriteLcdColor(_state.Scanline, (uint8_t)_drawnPixels, colorIndex);
}
_currentBuffer[outOffset] = _state.CgbObjPalettes[((sprite.Attributes & 0x10) ? 4 : 0) | colorIndex];
}
} else {
if(_state.CgbEnabled) {
}
else
{
if (_state.CgbEnabled)
{
_currentBuffer[outOffset] = _state.CgbBgPalettes[entry.Color | ((entry.Attributes & 0x07) << 2)];
} else {
}
else
{
uint8_t colorIndex = (_state.BgPalette >> (entry.Color * 2)) & 0x03;
if(_gameboy->IsSgb()) {
if (_gameboy->IsSgb())
{
_gameboy->GetSgb()->WriteLcdColor(_state.Scanline, (uint8_t)_drawnPixels, colorIndex);
}
_currentBuffer[outOffset] = _state.CgbBgPalettes[colorIndex];
@ -382,7 +444,8 @@ void GbPpu::RunDrawCycle()
_bgFifo.Pop();
_drawnPixels++;
if(_oamFifo.Size > 0) {
if (_oamFifo.Size > 0)
{
_oamFifo.Pop();
}
}
@ -392,17 +455,22 @@ void GbPpu::RunDrawCycle()
void GbPpu::RunSpriteEvaluation()
{
if(_state.Cycle & 0x01) {
if(_spriteCount < 10) {
if (_state.Cycle & 0x01)
{
if (_spriteCount < 10)
{
uint8_t spriteIndex = ((_state.Cycle - 4) >> 1) * 4;
int16_t sprY = _dmaController->IsOamDmaRunning() ? 0xFF : ((int16_t)_oam[spriteIndex] - 16);
if(_state.Scanline >= sprY && _state.Scanline < sprY + (_state.LargeSprites ? 16 : 8)) {
if (_state.Scanline >= sprY && _state.Scanline < sprY + (_state.LargeSprites ? 16 : 8))
{
_spriteX[_spriteCount] = _oam[spriteIndex + 1];
_spriteIndexes[_spriteCount] = spriteIndex;
_spriteCount++;
}
}
} else {
}
else
{
//TODO check proper timing for even&odd cycles
}
}
@ -425,8 +493,10 @@ void GbPpu::ResetRenderer()
void GbPpu::ClockSpriteFetcher()
{
switch(_oamFetcher.Step++) {
case 1: {
switch (_oamFetcher.Step++)
{
case 1:
{
//Fetch tile index
int16_t sprY = (int16_t)_oam[_fetchSprite] - 16;
uint8_t sprTile = _oam[_fetchSprite + 2];
@ -434,8 +504,11 @@ void GbPpu::ClockSpriteFetcher()
bool vMirror = (sprAttr & 0x40) != 0;
uint16_t tileBank = _state.CgbEnabled ? ((sprAttr & 0x08) ? 0x2000 : 0x0000) : 0;
uint8_t sprOffsetY = vMirror ? (_state.LargeSprites ? 15 : 7) - (_state.Scanline - sprY) : (_state.Scanline - sprY);
if(_state.LargeSprites) {
uint8_t sprOffsetY = vMirror
? (_state.LargeSprites ? 15 : 7) - (_state.Scanline - sprY)
: (_state.Scanline - sprY);
if (_state.LargeSprites)
{
sprTile &= 0xFE;
}
@ -445,9 +518,11 @@ void GbPpu::ClockSpriteFetcher()
break;
}
case 3: _oamFetcher.LowByte = _vram[_oamFetcher.Addr]; break;
case 3: _oamFetcher.LowByte = _vram[_oamFetcher.Addr];
break;
case 5: {
case 5:
{
//Fetch sprite data (high byte)
_oamFetcher.HighByte = _vram[_oamFetcher.Addr + 1];
PushSpriteToPixelFifo();
@ -458,9 +533,12 @@ void GbPpu::ClockSpriteFetcher()
void GbPpu::FindNextSprite()
{
if(_fetchSprite < 0 && (_state.SpritesEnabled || _state.CgbEnabled)) {
for(int i = 0; i < _spriteCount; i++) {
if((int)_spriteX[i] - 8 == _drawnPixels) {
if (_fetchSprite < 0 && (_state.SpritesEnabled || _state.CgbEnabled))
{
for (int i = 0; i < _spriteCount; i++)
{
if ((int)_spriteX[i] - 8 == _drawnPixels)
{
_fetchSprite = _spriteIndexes[i];
_spriteX[i] = 0xFF; //Prevent processing the same sprite again
_oamFetcher.Step = 0;
@ -474,15 +552,20 @@ void GbPpu::ClockTileFetcher()
{
_evtColor = EvtColor::RenderingBgLoad;
switch(_bgFetcher.Step++) {
case 1: {
switch (_bgFetcher.Step++)
{
case 1:
{
//Fetch tile index
uint16_t tilemapAddr;
uint8_t yOffset;
if(_fetchWindow) {
if (_fetchWindow)
{
tilemapAddr = _state.WindowTilemapSelect ? 0x1C00 : 0x1800;
yOffset = (uint8_t)_windowCounter;
} else {
}
else
{
tilemapAddr = _state.BgTilemapSelect ? 0x1C00 : 0x1800;
yOffset = _state.ScrollY + _state.Scanline;
}
@ -504,13 +587,15 @@ void GbPpu::ClockTileFetcher()
break;
}
case 3: {
case 3:
{
//Fetch tile data (low byte)
_bgFetcher.LowByte = _vram[_bgFetcher.Addr];
break;
}
case 5: {
case 5:
{
//Fetch tile data (high byte)
_bgFetcher.HighByte = _vram[_bgFetcher.Addr + 1];
@ -519,9 +604,12 @@ void GbPpu::ClockTileFetcher()
case 6:
case 7:
if(_bgFifo.Size == 0) {
if (_bgFifo.Size == 0)
{
PushTileToPixelFifo();
} else if(_bgFetcher.Step == 8) {
}
else if (_bgFetcher.Step == 8)
{
//Wait until fifo is empty to push pixels
_bgFetcher.Step = 7;
}
@ -534,19 +622,22 @@ void GbPpu::PushSpriteToPixelFifo()
_fetchSprite = -1;
_oamFetcher.Step = 0;
if(!_state.SpritesEnabled) {
if (!_state.SpritesEnabled)
{
return;
}
uint8_t pos = _oamFifo.Position;
//Overlap sprite
for(int i = 0; i < 8; i++) {
for (int i = 0; i < 8; i++)
{
uint8_t shift = (_oamFetcher.Attributes & 0x20) ? i : (7 - i);
uint8_t bits = ((_oamFetcher.LowByte >> shift) & 0x01);
bits |= ((_oamFetcher.HighByte >> shift) & 0x01) << 1;
if(bits > 0 && _oamFifo.Content[pos].Color == 0) {
if (bits > 0 && _oamFifo.Content[pos].Color == 0)
{
_oamFifo.Content[pos].Color = bits;
_oamFifo.Content[pos].Attributes = _oamFetcher.Attributes;
}
@ -558,7 +649,8 @@ void GbPpu::PushSpriteToPixelFifo()
void GbPpu::PushTileToPixelFifo()
{
//Add new tile to fifo
for(int i = 0; i < 8; i++) {
for (int i = 0; i < 8; i++)
{
uint8_t shift = (_bgFetcher.Attributes & 0x20) ? i : (7 - i);
uint8_t bits = ((_bgFetcher.LowByte >> shift) & 0x01);
bits |= ((_bgFetcher.HighByte >> shift) & 0x01) << 1;
@ -583,7 +675,8 @@ void GbPpu::UpdateStatIrq()
(_state.IrqMode == PpuMode::VBlank && (_state.Status & GbPpuStatusFlags::VBlankIrq)))
);
if(irqFlag && !_state.StatIrqFlag) {
if (irqFlag && !_state.StatIrqFlag)
{
_memoryManager->RequestIrq(GbIrqSource::LcdStat);
}
_state.StatIrqFlag = irqFlag;
@ -624,7 +717,8 @@ void GbPpu::SendFrame()
_console->ProcessEvent(EventType::GbEndFrame);
_state.FrameCount++;
if(_gameboy->IsSgb()) {
if (_gameboy->IsSgb())
{
return;
}
@ -632,11 +726,15 @@ void GbPpu::SendFrame()
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone);
if(_isFirstFrame) {
if(!_state.CgbEnabled) {
if (_isFirstFrame)
{
if (!_state.CgbEnabled)
{
//Send blank frame on the first frame after enabling LCD (DMG only)
std::fill(_currentBuffer, _currentBuffer + 256 * 239, 0x7FFF);
} else {
}
else
{
//CGB repeats the previous frame?
uint16_t* src = _currentBuffer == _outputBuffers[0] ? _outputBuffers[1] : _outputBuffers[0];
std::copy(src, src + 256 * 239, _currentBuffer);
@ -647,9 +745,12 @@ void GbPpu::SendFrame()
#ifdef LIBRETRO
_console->GetVideoDecoder()->UpdateFrameSync(_currentBuffer, 256, 239, _state.FrameCount, false);
#else
if(_console->GetRewindManager()->IsRewinding()) {
if (_console->GetRewindManager()->IsRewinding())
{
_console->GetVideoDecoder()->UpdateFrameSync(_currentBuffer, 256, 239, _state.FrameCount, true);
} else {
}
else
{
_console->GetVideoDecoder()->UpdateFrame(_currentBuffer, 256, 239, _state.FrameCount);
}
#endif
@ -658,7 +759,8 @@ void GbPpu::SendFrame()
uint8_t prevInput = _memoryManager->ReadInputPort();
_console->ProcessEndOfFrame();
uint8_t newInput = _memoryManager->ReadInputPort();
if(prevInput != newInput) {
if (prevInput != newInput)
{
_memoryManager->RequestIrq(GbIrqSource::Joypad);
}
@ -667,17 +769,22 @@ void GbPpu::SendFrame()
void GbPpu::UpdatePalette()
{
if(!_gameboy->IsCgb()) {
if (!_gameboy->IsCgb())
{
GameboyConfig cfg = _console->GetSettings()->GetGameboyConfig();
for(int i = 0; i < 4; i++) {
for (int i = 0; i < 4; i++)
{
//Set palette based on settings (DMG)
uint16_t bgColor = ((cfg.BgColors[i] & 0xF8) << 7) | ((cfg.BgColors[i] & 0xF800) >> 6) | ((cfg.BgColors[i] & 0xF80000) >> 19);
uint16_t bgColor = ((cfg.BgColors[i] & 0xF8) << 7) | ((cfg.BgColors[i] & 0xF800) >> 6) | ((cfg.BgColors[i] &
0xF80000) >> 19);
_state.CgbBgPalettes[i] = bgColor;
uint16_t obj0Color = ((cfg.Obj0Colors[i] & 0xF8) << 7) | ((cfg.Obj0Colors[i] & 0xF800) >> 6) | ((cfg.Obj0Colors[i] & 0xF80000) >> 19);
uint16_t obj0Color = ((cfg.Obj0Colors[i] & 0xF8) << 7) | ((cfg.Obj0Colors[i] & 0xF800) >> 6) | ((cfg.Obj0Colors
[i] & 0xF80000) >> 19);
_state.CgbObjPalettes[i] = obj0Color;
uint16_t obj1Color = ((cfg.Obj1Colors[i] & 0xF8) << 7) | ((cfg.Obj1Colors[i] & 0xF800) >> 6) | ((cfg.Obj1Colors[i] & 0xF80000) >> 19);
uint16_t obj1Color = ((cfg.Obj1Colors[i] & 0xF8) << 7) | ((cfg.Obj1Colors[i] & 0xF800) >> 6) | ((cfg.Obj1Colors
[i] & 0xF80000) >> 19);
_state.CgbObjPalettes[i + 4] = obj1Color;
}
}
@ -685,7 +792,8 @@ void GbPpu::UpdatePalette()
uint8_t GbPpu::Read(uint16_t addr)
{
switch(addr) {
switch (addr)
{
case 0xFF40: return _state.Control;
case 0xFF41:
//FF41 - STAT - LCDC Status (R/W)
@ -713,15 +821,19 @@ uint8_t GbPpu::Read(uint16_t addr)
void GbPpu::Write(uint16_t addr, uint8_t value)
{
switch(addr) {
switch (addr)
{
case 0xFF40:
_state.Control = value;
if(_state.LcdEnabled != ((value & 0x80) != 0)) {
if (_state.LcdEnabled != ((value & 0x80) != 0))
{
_state.LcdEnabled = (value & 0x80) != 0;
if(!_state.LcdEnabled) {
if (!_state.LcdEnabled)
{
//Reset LCD to top of screen when it gets turned off
if(_state.Mode != PpuMode::VBlank) {
if (_state.Mode != PpuMode::VBlank)
{
_console->BreakImmediately(BreakSource::GbDisableLcdOutsideVblank);
SendFrame();
}
@ -736,7 +848,9 @@ void GbPpu::Write(uint16_t addr, uint8_t value)
//"If the HDMA started when the screen was on, when the screen is switched off it will copy one block after the switch."
_dmaController->ProcessHdma();
} else {
}
else
{
_isFirstFrame = true;
_state.Cycle = -1;
_state.IdleCycles = 0;
@ -744,11 +858,15 @@ void GbPpu::Write(uint16_t addr, uint8_t value)
_state.LyCoincidenceFlag = _state.LyCompare == _state.LyForCompare;
UpdateStatIrq();
if(_console->IsDebugging()) {
if (_console->IsDebugging())
{
_console->ProcessEvent(EventType::GbStartFrame);
_currentEventViewerBuffer = _currentEventViewerBuffer == _eventViewerBuffers[0] ? _eventViewerBuffers[1] : _eventViewerBuffers[0];
for(int i = 0; i < 456 * 154; i++) {
_currentEventViewerBuffer = _currentEventViewerBuffer == _eventViewerBuffers[0]
? _eventViewerBuffers[1]
: _eventViewerBuffers[0];
for (int i = 0; i < 456 * 154; i++)
{
_currentEventViewerBuffer[i] = 0x18C6;
}
}
@ -764,7 +882,8 @@ void GbPpu::Write(uint16_t addr, uint8_t value)
break;
case 0xFF41:
if(!_gameboy->IsCgb()) {
if (!_gameboy->IsCgb())
{
//STAT write bug (DMG ONLY)
//Writing to STAT causes all IRQ types to be turned on for a single cycle
_state.Status = 0xF8 | (_state.Status & 0x07);
@ -775,22 +894,30 @@ void GbPpu::Write(uint16_t addr, uint8_t value)
UpdateStatIrq();
break;
case 0xFF42: _state.ScrollY = value; break;
case 0xFF43: _state.ScrollX = value; break;
case 0xFF42: _state.ScrollY = value;
break;
case 0xFF43: _state.ScrollX = value;
break;
case 0xFF45:
_state.LyCompare = value;
if(_state.LcdEnabled) {
if (_state.LcdEnabled)
{
_state.IdleCycles = 0;
_state.LyCoincidenceFlag = (_state.LyCompare == _state.LyForCompare);
UpdateStatIrq();
}
break;
case 0xFF47: _state.BgPalette = value; break;
case 0xFF48: _state.ObjPalette0 = value; break;
case 0xFF49: _state.ObjPalette1 = value; break;
case 0xFF4A: _state.WindowY = value; break;
case 0xFF4B: _state.WindowX = value; break;
case 0xFF47: _state.BgPalette = value;
break;
case 0xFF48: _state.ObjPalette0 = value;
break;
case 0xFF49: _state.ObjPalette1 = value;
break;
case 0xFF4A: _state.WindowY = value;
break;
case 0xFF4B: _state.WindowX = value;
break;
default:
LogDebug("[Debug] GB - Missing write handler: $" + HexUtilities::ToHex(addr));
@ -810,11 +937,14 @@ bool GbPpu::IsVramWriteAllowed()
uint8_t GbPpu::ReadVram(uint16_t addr)
{
if(IsVramReadAllowed()) {
if (IsVramReadAllowed())
{
uint16_t vramAddr = (_state.CgbVramBank << 13) | (addr & 0x1FFF);
_console->ProcessPpuRead(vramAddr, _vram[vramAddr], SnesMemoryType::GbVideoRam);
return _vram[vramAddr];
} else {
}
else
{
_console->BreakImmediately(BreakSource::GbInvalidVramAccess);
return 0xFF;
}
@ -827,44 +957,56 @@ uint8_t GbPpu::PeekVram(uint16_t addr)
void GbPpu::WriteVram(uint16_t addr, uint8_t value)
{
if(IsVramWriteAllowed()) {
if (IsVramWriteAllowed())
{
uint16_t vramAddr = (_state.CgbVramBank << 13) | (addr & 0x1FFF);
_console->ProcessPpuWrite(vramAddr, value, SnesMemoryType::GbVideoRam);
_vram[vramAddr] = value;
} else {
}
else
{
_console->BreakImmediately(BreakSource::GbInvalidVramAccess);
}
}
bool GbPpu::IsOamWriteAllowed()
{
if(_memoryManager->IsOamDmaRunning()) {
if (_memoryManager->IsOamDmaRunning())
{
return false;
}
if(_state.Scanline == 0 && _isFirstFrame) {
if (_state.Scanline == 0 && _isFirstFrame)
{
return _state.Mode == PpuMode::HBlank && _state.Cycle != 77 && _state.Cycle != 78;
} else {
}
else
{
return _state.Mode <= PpuMode::VBlank || (_state.Cycle >= 80 && _state.Cycle < 84);
}
}
bool GbPpu::IsOamReadAllowed()
{
if(_memoryManager->IsOamDmaRunning()) {
if (_memoryManager->IsOamDmaRunning())
{
return false;
}
if(_state.Scanline == 0 && _isFirstFrame) {
if (_state.Scanline == 0 && _isFirstFrame)
{
return _state.Mode == PpuMode::HBlank;
} else {
}
else
{
return _state.Mode == PpuMode::VBlank || (_state.Mode == PpuMode::HBlank && _state.Cycle != 3);
}
}
uint8_t GbPpu::PeekOam(uint8_t addr)
{
if(addr < 0xA0) {
if (addr < 0xA0)
{
return IsOamReadAllowed() ? _oam[addr] : 0xFF;
}
return 0;
@ -872,11 +1014,15 @@ uint8_t GbPpu::PeekOam(uint8_t addr)
uint8_t GbPpu::ReadOam(uint8_t addr)
{
if(addr < 0xA0) {
if(IsOamReadAllowed()) {
if (addr < 0xA0)
{
if (IsOamReadAllowed())
{
_console->ProcessPpuRead(addr, _oam[addr], SnesMemoryType::GbSpriteRam);
return _oam[addr];
} else {
}
else
{
_console->BreakImmediately(BreakSource::GbInvalidOamAccess);
return 0xFF;
}
@ -889,14 +1035,20 @@ void GbPpu::WriteOam(uint8_t addr, uint8_t value, bool forDma)
//During DMA or rendering/oam evaluation, ignore writes to OAM
//The DMA controller is always allowed to write to OAM (presumably the PPU can't read OAM during that time? TODO implement)
//On the DMG, there is a 4 clock gap (80 to 83) between OAM evaluation & rendering where writing is allowed
if(addr < 0xA0) {
if(forDma) {
if (addr < 0xA0)
{
if (forDma)
{
_oam[addr] = value;
_console->ProcessPpuWrite(addr, value, SnesMemoryType::GbSpriteRam);
} else if(IsOamWriteAllowed()) {
}
else if (IsOamWriteAllowed())
{
_oam[addr] = value;
_console->ProcessPpuWrite(addr, value, SnesMemoryType::GbSpriteRam);
} else {
}
else
{
_console->BreakImmediately(BreakSource::GbInvalidOamAccess);
}
}
@ -904,16 +1056,21 @@ void GbPpu::WriteOam(uint8_t addr, uint8_t value, bool forDma)
uint8_t GbPpu::ReadCgbRegister(uint16_t addr)
{
if(!_state.CgbEnabled) {
if (!_state.CgbEnabled)
{
return 0xFF;
}
switch(addr) {
switch (addr)
{
case 0xFF4F: return _state.CgbVramBank | 0xFE;
case 0xFF68: return _state.CgbBgPalPosition | (_state.CgbBgPalAutoInc ? 0x80 : 0) | 0x40;
case 0xFF69: return (_state.CgbBgPalettes[_state.CgbBgPalPosition >> 1] >> ((_state.CgbBgPalPosition & 0x01) ? 8 : 0) & 0xFF);
case 0xFF69: return (_state.CgbBgPalettes[_state.CgbBgPalPosition >> 1] >> ((_state.CgbBgPalPosition & 0x01) ? 8 : 0)
& 0xFF);
case 0xFF6A: return _state.CgbObjPalPosition | (_state.CgbObjPalAutoInc ? 0x80 : 0) | 0x40;
case 0xFF6B: return (_state.CgbObjPalettes[_state.CgbObjPalPosition >> 1] >> ((_state.CgbObjPalPosition & 0x01) ? 8 : 0) & 0xFF);
case 0xFF6B: return (_state.CgbObjPalettes[_state.CgbObjPalPosition >> 1] >> ((_state.CgbObjPalPosition & 0x01)
? 8
: 0) & 0xFF);
}
LogDebug("[Debug] GBC - Missing read handler: $" + HexUtilities::ToHex(addr));
return 0xFF;
@ -921,13 +1078,17 @@ uint8_t GbPpu::ReadCgbRegister(uint16_t addr)
void GbPpu::WriteCgbRegister(uint16_t addr, uint8_t value)
{
if(!_state.CgbEnabled && _memoryManager->IsBootRomDisabled()) {
if (!_state.CgbEnabled && _memoryManager->IsBootRomDisabled())
{
return;
}
switch(addr) {
case 0xFF4C: _state.CgbEnabled = (value & 0x0C) == 0; break;
case 0xFF4F: _state.CgbVramBank = value & 0x01; break;
switch (addr)
{
case 0xFF4C: _state.CgbEnabled = (value & 0x0C) == 0;
break;
case 0xFF4F: _state.CgbVramBank = value & 0x01;
break;
case 0xFF68:
//FF68 - BCPS/BGPI - CGB Mode Only - Background Palette Index
@ -935,7 +1096,8 @@ void GbPpu::WriteCgbRegister(uint16_t addr, uint8_t value)
_state.CgbBgPalAutoInc = (value & 0x80) != 0;
break;
case 0xFF69: {
case 0xFF69:
{
//FF69 - BCPD/BGPD - CGB Mode Only - Background Palette Data
WriteCgbPalette(_state.CgbBgPalPosition, _state.CgbBgPalettes, _state.CgbBgPalAutoInc, value);
break;
@ -960,15 +1122,20 @@ void GbPpu::WriteCgbRegister(uint16_t addr, uint8_t value)
void GbPpu::WriteCgbPalette(uint8_t& pos, uint16_t* pal, bool autoInc, uint8_t value)
{
if(_state.Mode <= PpuMode::OamEvaluation) {
if(pos & 0x01) {
if (_state.Mode <= PpuMode::OamEvaluation)
{
if (pos & 0x01)
{
pal[pos >> 1] = (pal[pos >> 1] & 0xFF) | ((value & 0x7F) << 8);
} else {
}
else
{
pal[pos >> 1] = (pal[pos >> 1] & 0xFF00) | value;
}
}
if(autoInc) {
if (autoInc)
{
pos = (pos + 1) & 0x3F;
}
}
@ -976,9 +1143,12 @@ void GbPpu::WriteCgbPalette(uint8_t& pos, uint16_t* pal, bool autoInc, uint8_t v
void GbPpu::Serialize(Serializer& s)
{
s.Stream(
_state.Scanline, _state.Cycle, _state.Mode, _state.LyCompare, _state.BgPalette, _state.ObjPalette0, _state.ObjPalette1,
_state.ScrollX, _state.ScrollY, _state.WindowX, _state.WindowY, _state.Control, _state.LcdEnabled, _state.WindowTilemapSelect,
_state.WindowEnabled, _state.BgTileSelect, _state.BgTilemapSelect, _state.LargeSprites, _state.SpritesEnabled, _state.BgEnabled,
_state.Scanline, _state.Cycle, _state.Mode, _state.LyCompare, _state.BgPalette, _state.ObjPalette0,
_state.ObjPalette1,
_state.ScrollX, _state.ScrollY, _state.WindowX, _state.WindowY, _state.Control, _state.LcdEnabled,
_state.WindowTilemapSelect,
_state.WindowEnabled, _state.BgTileSelect, _state.BgTilemapSelect, _state.LargeSprites, _state.SpritesEnabled,
_state.BgEnabled,
_state.Status, _state.FrameCount, _lastFrameTime, _state.LyCoincidenceFlag,
_state.CgbBgPalAutoInc, _state.CgbBgPalPosition,
_state.CgbObjPalAutoInc, _state.CgbObjPalPosition, _state.CgbVramBank, _state.CgbEnabled,
@ -996,7 +1166,8 @@ void GbPpu::Serialize(Serializer& s)
_bgFifo.Position, _bgFifo.Size, _oamFifo.Position, _oamFifo.Size
);
for(int i = 0; i < 8; i++) {
for (int i = 0; i < 8; i++)
{
s.Stream(_bgFifo.Content[i].Color, _bgFifo.Content[i].Attributes);
s.Stream(_oamFifo.Content[i].Color, _oamFifo.Content[i].Attributes);
}

View file

@ -76,7 +76,8 @@ private:
public:
virtual ~GbPpu();
void Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager, GbDmaController* dmaController, uint8_t* vram, uint8_t* oam);
void Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager, GbDmaController* dmaController,
uint8_t* vram, uint8_t* oam);
GbPpuState GetState();
uint16_t* GetOutputBuffer();

View file

@ -26,30 +26,37 @@ void GbSquareChannel::Disable()
void GbSquareChannel::ClockSweepUnit()
{
if(!_state.SweepEnabled) {
if (!_state.SweepEnabled)
{
return;
}
if(_state.SweepTimer > 0 && _state.SweepPeriod > 0) {
if (_state.SweepTimer > 0 && _state.SweepPeriod > 0)
{
_state.SweepTimer--;
if(_state.SweepTimer == 0) {
if (_state.SweepTimer == 0)
{
_state.SweepTimer = _state.SweepPeriod;
//"When it generates a clock and the sweep's internal enabled flag is set and the sweep period is not zero, a new frequency is calculated and the overflow"
uint16_t newFreq = GetSweepTargetFrequency();
if(_state.SweepShift > 0 && newFreq < 2048) {
if (_state.SweepShift > 0 && newFreq < 2048)
{
//"If the new frequency is 2047 or less and the sweep shift is not zero, this new frequency is written back to the shadow frequency and square 1's frequency in NR13 and NR14,"
_state.Frequency = _state.SweepFreq;
_state.SweepFreq = newFreq;
newFreq = GetSweepTargetFrequency();
if(newFreq >= 2048) {
if (newFreq >= 2048)
{
//"then frequency calculation and overflow check are run AGAIN immediately using this new value, but this second new frequency is not written back."
_state.SweepEnabled = false;
_state.Enabled = false;
}
} else {
}
else
{
_state.SweepEnabled = false;
_state.Enabled = false;
}
@ -60,18 +67,23 @@ void GbSquareChannel::ClockSweepUnit()
uint16_t GbSquareChannel::GetSweepTargetFrequency()
{
uint16_t shiftResult = (_state.SweepFreq >> _state.SweepShift);
if(_state.SweepNegate) {
if (_state.SweepNegate)
{
return _state.SweepFreq - shiftResult;
} else {
}
else
{
return _state.SweepFreq + shiftResult;
}
}
void GbSquareChannel::ClockLengthCounter()
{
if(_state.LengthEnabled && _state.Length > 0) {
if (_state.LengthEnabled && _state.Length > 0)
{
_state.Length--;
if(_state.Length == 0) {
if (_state.Length == 0)
{
//"Length becoming 0 should clear status"
_state.Enabled = false;
}
@ -80,15 +92,22 @@ void GbSquareChannel::ClockLengthCounter()
void GbSquareChannel::ClockEnvelope()
{
if(_state.EnvTimer > 0 && !_state.EnvStopped) {
if (_state.EnvTimer > 0 && !_state.EnvStopped)
{
_state.EnvTimer--;
if(_state.EnvTimer == 0) {
if(_state.EnvRaiseVolume && _state.Volume < 0x0F) {
if (_state.EnvTimer == 0)
{
if (_state.EnvRaiseVolume && _state.Volume < 0x0F)
{
_state.Volume++;
} else if(!_state.EnvRaiseVolume && _state.Volume > 0) {
}
else if (!_state.EnvRaiseVolume && _state.Volume > 0)
{
_state.Volume--;
} else {
}
else
{
_state.EnvStopped = true;
}
@ -105,14 +124,18 @@ uint8_t GbSquareChannel::GetOutput()
void GbSquareChannel::Exec(uint32_t clocksToRun)
{
_state.Timer -= clocksToRun;
if(_state.Enabled) {
if (_state.Enabled)
{
_state.Output = _dutySequences[_state.Duty][_state.DutyPos] * _state.Volume;
} else {
}
else
{
_state.Output = 0;
}
if(_state.Timer == 0) {
if (_state.Timer == 0)
{
_state.Timer = (2048 - _state.Frequency) * 4;
_state.DutyPos = (_state.DutyPos + 1) & 0x07;
}
@ -123,7 +146,8 @@ uint8_t GbSquareChannel::Read(uint16_t addr)
constexpr uint8_t openBusBits[5] = {0x80, 0x3F, 0x00, 0xFF, 0xBF};
uint8_t value = 0;
switch(addr) {
switch (addr)
{
case 0:
value = (
(_state.SweepPeriod << 4) |
@ -132,7 +156,8 @@ uint8_t GbSquareChannel::Read(uint16_t addr)
);
break;
case 1: value = _state.Duty << 6; break;
case 1: value = _state.Duty << 6;
break;
case 2:
value = (
@ -142,7 +167,8 @@ uint8_t GbSquareChannel::Read(uint16_t addr)
);
break;
case 4: value = _state.LengthEnabled ? 0x40 : 0; break;
case 4: value = _state.LengthEnabled ? 0x40 : 0;
break;
}
return value | openBusBits[addr];
@ -150,7 +176,8 @@ uint8_t GbSquareChannel::Read(uint16_t addr)
void GbSquareChannel::Write(uint16_t addr, uint8_t value)
{
switch(addr) {
switch (addr)
{
case 0:
_state.SweepShift = value & 0x07;
_state.SweepNegate = (value & 0x08) != 0;
@ -164,16 +191,20 @@ void GbSquareChannel::Write(uint16_t addr, uint8_t value)
case 2:
{
if(_state.EnvPeriod == 0 && !_state.EnvStopped) {
if (_state.EnvPeriod == 0 && !_state.EnvStopped)
{
//"If the old envelope period was zero and the envelope is still doing automatic updates, volume is incremented by 1"
_state.Volume++;
} else if(!_state.EnvRaiseVolume) {
}
else if (!_state.EnvRaiseVolume)
{
//"otherwise if the envelope was in subtract mode, volume is incremented by 2"
_state.Volume += 2;
}
bool raiseVolume = (value & 0x08) != 0;
if(raiseVolume != _state.EnvRaiseVolume) {
if (raiseVolume != _state.EnvRaiseVolume)
{
//"If the mode was changed (add to subtract or subtract to add), volume is set to 16 - volume."
_state.Volume = 16 - _state.Volume;
}
@ -185,7 +216,8 @@ void GbSquareChannel::Write(uint16_t addr, uint8_t value)
_state.EnvRaiseVolume = raiseVolume;
_state.EnvVolume = (value & 0xF0) >> 4;
if(!(value & 0xF8)) {
if (!(value & 0xF8))
{
_state.Enabled = false;
}
break;
@ -195,10 +227,12 @@ void GbSquareChannel::Write(uint16_t addr, uint8_t value)
_state.Frequency = (_state.Frequency & 0x700) | value;
break;
case 4: {
case 4:
{
_state.Frequency = (_state.Frequency & 0xFF) | ((value & 0x07) << 8);
if(value & 0x80) {
if (value & 0x80)
{
//"Writing a value to NRx4 with bit 7 set causes the following things to occur :"
//"Channel is enabled, if volume is not 0 or raise volume flag is set"
@ -208,7 +242,8 @@ void GbSquareChannel::Write(uint16_t addr, uint8_t value)
_state.Timer = (2048 - _state.Frequency) * 4;
//"If length counter is zero, it is set to 64 (256 for wave channel)."
if(_state.Length == 0) {
if (_state.Length == 0)
{
_state.Length = 64;
_state.LengthEnabled = false;
}
@ -230,9 +265,11 @@ void GbSquareChannel::Write(uint16_t addr, uint8_t value)
_state.SweepTimer = _state.SweepPeriod;
_state.SweepEnabled = _state.SweepPeriod > 0 || _state.SweepShift > 0;
if(_state.SweepShift > 0) {
if (_state.SweepShift > 0)
{
_state.SweepFreq = GetSweepTargetFrequency();
if(_state.SweepFreq > 2047) {
if (_state.SweepFreq > 2047)
{
_state.SweepEnabled = false;
_state.Enabled = false;
}
@ -248,8 +285,10 @@ void GbSquareChannel::Write(uint16_t addr, uint8_t value)
void GbSquareChannel::Serialize(Serializer& s)
{
s.Stream(
_state.SweepPeriod, _state.SweepNegate, _state.SweepShift, _state.SweepTimer, _state.SweepEnabled, _state.SweepFreq,
_state.Volume, _state.EnvVolume, _state.EnvRaiseVolume, _state.EnvPeriod, _state.EnvTimer, _state.Duty, _state.Frequency,
_state.SweepPeriod, _state.SweepNegate, _state.SweepShift, _state.SweepTimer, _state.SweepEnabled,
_state.SweepFreq,
_state.Volume, _state.EnvVolume, _state.EnvRaiseVolume, _state.EnvPeriod, _state.EnvTimer, _state.Duty,
_state.Frequency,
_state.Length, _state.LengthEnabled, _state.Enabled, _state.Timer, _state.DutyPos, _state.Output
);
}

View file

@ -28,9 +28,11 @@ GbTimerState GbTimer::GetState()
void GbTimer::Exec()
{
if((_state.Divider & 0x03) == 2) {
if ((_state.Divider & 0x03) == 2)
{
_state.Reloaded = false;
if(_state.NeedReload) {
if (_state.NeedReload)
{
ReloadCounter();
}
}
@ -47,15 +49,18 @@ void GbTimer::ReloadCounter()
void GbTimer::SetDivider(uint16_t newValue)
{
if(_state.TimerEnabled && !(newValue & _state.TimerDivider) && (_state.Divider & _state.TimerDivider)) {
if (_state.TimerEnabled && !(newValue & _state.TimerDivider) && (_state.Divider & _state.TimerDivider))
{
_state.Counter++;
if(_state.Counter == 0) {
if (_state.Counter == 0)
{
_state.NeedReload = true;
}
}
uint16_t frameSeqBit = _memoryManager->IsHighSpeed() ? 0x2000 : 0x1000;
if(!(newValue & frameSeqBit) && (_state.Divider & frameSeqBit)) {
if (!(newValue & frameSeqBit) && (_state.Divider & frameSeqBit))
{
_apu->ClockFrameSequencer();
}
@ -64,7 +69,8 @@ void GbTimer::SetDivider(uint16_t newValue)
uint8_t GbTimer::Read(uint16_t addr)
{
switch(addr) {
switch (addr)
{
case 0xFF04: return _state.Divider >> 8;
case 0xFF05: return _state.Counter; //FF05 - TIMA - Timer counter (R/W)
case 0xFF06: return _state.Modulo; //FF06 - TMA - Timer Modulo (R/W)
@ -75,19 +81,22 @@ uint8_t GbTimer::Read(uint16_t addr)
void GbTimer::Write(uint16_t addr, uint8_t value)
{
switch(addr) {
switch (addr)
{
case 0xFF04:
SetDivider(0);
break;
case 0xFF05:
//FF05 - TIMA - Timer counter (R/W)
if(_state.NeedReload) {
if (_state.NeedReload)
{
//Writing to TIMA when a reload is pending will cancel the reload and IRQ request
_state.NeedReload = false;
}
if(!_state.Reloaded) {
if (!_state.Reloaded)
{
//Writes to TIMA on the cycle TIMA was reloaded with TMA are ignored
_state.Counter = value;
}
@ -96,36 +105,49 @@ void GbTimer::Write(uint16_t addr, uint8_t value)
case 0xFF06:
//FF06 - TMA - Timer Modulo (R/W)
_state.Modulo = value;
if(_state.Reloaded) {
if (_state.Reloaded)
{
//Writing to TMA on the same cycle it was reloaded into TIMA will also update TIMA
_state.Counter = value;
}
break;
case 0xFF07: {
case 0xFF07:
{
//FF07 - TAC - Timer Control (R/W)
_state.Control = value;
bool enabled = (value & 0x04) != 0;
uint16_t newDivider = 0;
switch(value & 0x03) {
case 0: newDivider = 1 << 9; break;
case 1: newDivider = 1 << 3; break;
case 2: newDivider = 1 << 5; break;
case 3: newDivider = 1 << 7; break;
switch (value & 0x03)
{
case 0: newDivider = 1 << 9;
break;
case 1: newDivider = 1 << 3;
break;
case 2: newDivider = 1 << 5;
break;
case 3: newDivider = 1 << 7;
break;
}
if(_state.TimerEnabled) {
if (_state.TimerEnabled)
{
//When changing the value of TAC, the TIMA register can get incremented due to a glitch
bool incrementCounter;
if(enabled) {
if (enabled)
{
incrementCounter = (_state.Divider & _state.TimerDivider) != 0 && (_state.Divider & newDivider) == 0;
} else {
}
else
{
incrementCounter = (_state.Divider & _state.TimerDivider) != 0;
}
if(incrementCounter) {
if (incrementCounter)
{
_state.Counter++;
if(_state.Counter == 0) {
if (_state.Counter == 0)
{
ReloadCounter();
}
}
@ -140,5 +162,6 @@ void GbTimer::Write(uint16_t addr, uint8_t value)
void GbTimer::Serialize(Serializer& s)
{
s.Stream(_state.Divider, _state.Counter, _state.Modulo, _state.Control, _state.TimerEnabled, _state.TimerDivider, _state.NeedReload, _state.Reloaded);
s.Stream(_state.Divider, _state.Counter, _state.Modulo, _state.Control, _state.TimerEnabled, _state.TimerDivider,
_state.NeedReload, _state.Reloaded);
}

View file

@ -29,9 +29,11 @@ void GbWaveChannel::Disable()
void GbWaveChannel::ClockLengthCounter()
{
if(_state.LengthEnabled && _state.Length > 0) {
if (_state.LengthEnabled && _state.Length > 0)
{
_state.Length--;
if(_state.Length == 0) {
if (_state.Length == 0)
{
//"Length becoming 0 should clear status"
_state.Enabled = false;
}
@ -48,13 +50,17 @@ void GbWaveChannel::Exec(uint32_t clocksToRun)
_state.Timer -= clocksToRun;
//The DAC receives the current value from the upper/lower nibble of the sample buffer, shifted right by the volume control.
if(_state.Volume && _state.Enabled) {
if (_state.Volume && _state.Enabled)
{
_state.Output = _state.SampleBuffer >> (_state.Volume - 1);
} else {
}
else
{
_state.Output = 0;
}
if(_state.Timer == 0) {
if (_state.Timer == 0)
{
//The wave channel's frequency timer period is set to (2048-frequency)*2.
_state.Timer = (2048 - _state.Frequency) * 2;
@ -63,9 +69,12 @@ void GbWaveChannel::Exec(uint32_t clocksToRun)
_state.Position = (_state.Position + 1) & 0x1F;
//then a sample is read into the sample buffer from this NEW position.
if(_state.Position & 0x01) {
if (_state.Position & 0x01)
{
_state.SampleBuffer = _state.Ram[_state.Position >> 1] & 0x0F;
} else {
}
else
{
_state.SampleBuffer = _state.Ram[_state.Position >> 1] >> 4;
}
}
@ -76,10 +85,14 @@ uint8_t GbWaveChannel::Read(uint16_t addr)
constexpr uint8_t openBusBits[5] = {0x7F, 0xFF, 0x9F, 0xFF, 0xBF};
uint8_t value = 0;
switch(addr) {
case 0: value = _state.DacEnabled ? 0x80 : 0; break;
case 2: value = _state.Volume << 5; break;
case 4: value = _state.LengthEnabled ? 0x40 : 0; break;
switch (addr)
{
case 0: value = _state.DacEnabled ? 0x80 : 0;
break;
case 2: value = _state.Volume << 5;
break;
case 4: value = _state.LengthEnabled ? 0x40 : 0;
break;
}
return value | openBusBits[addr];
@ -87,7 +100,8 @@ uint8_t GbWaveChannel::Read(uint16_t addr)
void GbWaveChannel::Write(uint16_t addr, uint8_t value)
{
switch(addr) {
switch (addr)
{
case 0:
_state.DacEnabled = (value & 0x80) != 0;
_state.Enabled &= _state.DacEnabled;
@ -105,10 +119,12 @@ void GbWaveChannel::Write(uint16_t addr, uint8_t value)
_state.Frequency = (_state.Frequency & 0x700) | value;
break;
case 4: {
case 4:
{
_state.Frequency = (_state.Frequency & 0xFF) | ((value & 0x07) << 8);
if(value & 0x80) {
if (value & 0x80)
{
//Start playback
//Channel is enabled, if DAC is enabled
@ -118,7 +134,8 @@ void GbWaveChannel::Write(uint16_t addr, uint8_t value)
_state.Timer = (2048 - _state.Frequency) * 2;
//If length counter is zero, it is set to 64 (256 for wave channel).
if(_state.Length == 0) {
if (_state.Length == 0)
{
_state.Length = 256;
_state.LengthEnabled = false;
}

Some files were not shown because too many files have changed in this diff Show more