Debugger: Fixed crash in disassembly, slightly improved search performance
This commit is contained in:
parent
1290fa90f6
commit
60067db242
11 changed files with 489 additions and 47 deletions
|
@ -29,48 +29,48 @@ void CpuDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t me
|
|||
if(label.size()) {
|
||||
operand.Write(label);
|
||||
} else {
|
||||
operand.Write('$', HexUtilities::ToHex24(opAddr));
|
||||
operand.WriteAll('$', HexUtilities::ToHex24(opAddr));
|
||||
}
|
||||
} else if(opSize == 2) {
|
||||
operand.Write('$', HexUtilities::ToHex((uint8_t)opAddr));
|
||||
operand.WriteAll('$', HexUtilities::ToHex((uint8_t)opAddr));
|
||||
} else if(opSize == 3) {
|
||||
operand.Write('$', HexUtilities::ToHex((uint16_t)opAddr));
|
||||
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.Write('(', operand, ",X)"); break;
|
||||
case AddrMode::AbsIdxX: str.Write(operand, ",X"); break;
|
||||
case AddrMode::AbsIdxY: str.Write(operand, ",Y"); break;
|
||||
case AddrMode::AbsInd: str.Write('(', operand, ')'); break;
|
||||
case AddrMode::AbsIndLng: str.Write('[', operand, ']'); break;
|
||||
case AddrMode::AbsLngIdxX: str.Write(operand, ",X"); 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.Write('$', operand[1], operand[2], " -> "); str.Write('$', operand[3], operand[4]); break;
|
||||
case AddrMode::DirIdxIndX: str.Write('(', operand, ",X)"); break;
|
||||
case AddrMode::DirIdxX: str.Write(operand, ",X"); break;
|
||||
case AddrMode::DirIdxY: str.Write(operand, ",Y"); break;
|
||||
case AddrMode::DirIndIdxY: str.Write("(", operand, "),Y"); break;
|
||||
case AddrMode::DirIndLngIdxY: str.Write("[", operand, "],Y"); break;
|
||||
case AddrMode::DirIndLng: str.Write("[", operand, "]"); break;
|
||||
case AddrMode::DirInd: 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:
|
||||
str.Write('#', operand);
|
||||
str.WriteAll('#', operand);
|
||||
break;
|
||||
|
||||
case AddrMode::Sig8: str.Write('#', 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::Stk: break;
|
||||
case AddrMode::StkRel: str.Write(operand, ",S"); break;
|
||||
case AddrMode::StkRelIndIdxY: str.Write('(', 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");
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "DebugBreakHelper.h"
|
||||
#include "BaseCartridge.h"
|
||||
#include "EmuSettings.h"
|
||||
#include "../Utilities/FastString.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
#include "../Utilities/StringUtilities.h"
|
||||
|
||||
|
@ -307,7 +308,7 @@ void Disassembler::Disassemble(CpuType cpuType)
|
|||
|
||||
if(inUnknownBlock || inVerifiedBlock) {
|
||||
int flags = LineFlags::BlockEnd | (inVerifiedBlock ? LineFlags::VerifiedData : 0) | (((inVerifiedBlock && showData) || (inUnknownBlock && showUnident)) ? LineFlags::ShowAsData : 0);
|
||||
results.push_back(DisassemblyResult(addrInfo, maxAddr + 1, flags));
|
||||
results.push_back(DisassemblyResult(addrInfo, maxAddr, flags));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,14 +389,16 @@ bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &d
|
|||
bool isBlockStartEnd = (data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd)) != 0;
|
||||
if(!isBlockStartEnd && result.Address.Address >= 0) {
|
||||
if((data.Flags & LineFlags::ShowAsData)) {
|
||||
string str = ".db";
|
||||
int nextAddr = lineIndex < source.size() - 1 ? source[lineIndex+1].CpuAddress : (maxAddr + 1);
|
||||
FastString str(".db", 3);
|
||||
SnesMemoryType memType = isSpc ? SnesMemoryType::SpcMemory : SnesMemoryType::CpuMemory;
|
||||
int nextAddr = lineIndex < source.size() - 2 ? source[lineIndex+1].CpuAddress : (maxAddr + 1);
|
||||
for(int i = 0; i < 8 && result.CpuAddress+i < nextAddr; i++) {
|
||||
str += " $" + HexUtilities::ToHex(_memoryDumper->GetMemoryValue(isSpc ? SnesMemoryType::SpcMemory : SnesMemoryType::CpuMemory, result.CpuAddress+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.c_str(), str.size());
|
||||
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];
|
||||
data.Flags |= LineFlags::VerifiedCode;
|
||||
|
|
|
@ -96,7 +96,7 @@ void DisassemblyInfo::GetByteCode(string &out)
|
|||
{
|
||||
FastString str;
|
||||
for(int i = 0; i < _opSize; i++) {
|
||||
str.Write('$', HexUtilities::ToHex(_byteCode[i]));
|
||||
str.WriteAll('$', HexUtilities::ToHex(_byteCode[i]));
|
||||
if(i < _opSize - 1) {
|
||||
str.Write(' ');
|
||||
}
|
||||
|
|
|
@ -21,14 +21,14 @@ void NecDspDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t
|
|||
uint8_t source = (opCode >> 4) & 0x0F;
|
||||
uint8_t accSelect = (opCode >> 15) & 0x01;
|
||||
if(aluOperation) {
|
||||
str.Write(aluOperations[aluOperation], " ");
|
||||
str.WriteAll(aluOperations[aluOperation], " ");
|
||||
|
||||
if(aluOperation <= 7) {
|
||||
uint8_t pSelect = (opCode >> 20) & 0x03;
|
||||
|
||||
switch(pSelect) {
|
||||
case 0: str.Write("RAM, "); break;
|
||||
case 1: str.Write(sourceNames[source], ", "); break;
|
||||
case 1: str.WriteAll(sourceNames[source], ", "); break;
|
||||
case 2: str.Write("M, "); break;
|
||||
case 3: str.Write("N, "); break;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ void NecDspDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t
|
|||
if(dest) {
|
||||
str.Delimiter(" | ");
|
||||
str.Write("MOV ");
|
||||
str.Write(sourceNames[source], ", ");
|
||||
str.WriteAll(sourceNames[source], ", ");
|
||||
str.Write(destNames[dest]);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ void NecDspDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t
|
|||
uint8_t dpHighModify = (opCode >> 9) & 0x0F;
|
||||
if(dpHighModify) {
|
||||
str.Delimiter(" | ");
|
||||
str.Write("DPH:$", HexUtilities::ToHex(dpHighModify));
|
||||
str.WriteAll("DPH:$", HexUtilities::ToHex(dpHighModify));
|
||||
}
|
||||
|
||||
uint8_t rpDecrement = (opCode >> 8) & 0x01;
|
||||
|
@ -116,14 +116,14 @@ void NecDspDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t
|
|||
default: str.Write("<unknown jump>"); break;
|
||||
}
|
||||
|
||||
str.Write(" $", HexUtilities::ToHex(target));
|
||||
str.WriteAll(" $", HexUtilities::ToHex(target));
|
||||
} else if(operationType == 3) {
|
||||
//Load
|
||||
uint16_t value = opCode >> 6;
|
||||
uint8_t dest = opCode & 0x0F;
|
||||
|
||||
str.Write("LD ");
|
||||
str.Write("$", HexUtilities::ToHex(value), ", ");
|
||||
str.WriteAll("$", HexUtilities::ToHex(value), ", ");
|
||||
str.Write(destNames[dest]);
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ void SpcDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t me
|
|||
addrInfo.Address = addr;
|
||||
string label = labelManager ? labelManager->GetLabel(addrInfo) : "";
|
||||
if(label.empty()) {
|
||||
str.Write('$', HexUtilities::ToHex(addr));
|
||||
str.WriteAll('$', HexUtilities::ToHex(addr));
|
||||
} else {
|
||||
str.Write(label);
|
||||
}
|
||||
|
@ -108,16 +108,16 @@ void SpcDisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t me
|
|||
|
||||
case 'a': getOperand((uint16_t)(byteCode[1] | (byteCode[2] << 8))); break;
|
||||
|
||||
case 'd': str.Write('$', HexUtilities::ToHex(byteCode[1])); break;
|
||||
case 'e': str.Write('$', HexUtilities::ToHex(byteCode[2])); break; //direct 2nd byte
|
||||
case 'd': str.WriteAll('$', HexUtilities::ToHex(byteCode[1])); break;
|
||||
case 'e': str.WriteAll('$', HexUtilities::ToHex(byteCode[2])); break; //direct 2nd byte
|
||||
|
||||
case 's': str.Write('$', HexUtilities::ToHex(byteCode[1])); break;
|
||||
case 't': str.Write('$', HexUtilities::ToHex(byteCode[2])); break;
|
||||
case 's': str.WriteAll('$', HexUtilities::ToHex(byteCode[1])); break;
|
||||
case 't': str.WriteAll('$', HexUtilities::ToHex(byteCode[2])); break;
|
||||
|
||||
case 'i': str.Write('$', HexUtilities::ToHex(byteCode[1])); break;
|
||||
case 'i': str.WriteAll('$', HexUtilities::ToHex(byteCode[1])); break;
|
||||
|
||||
case 'm': getOperand((uint16_t)((byteCode[1] | (byteCode[2] << 8)) & 0x1FFF)); break;
|
||||
case 'b': str.Write('$', (char)('0' + (byteCode[2] >> 5))); break;
|
||||
case 'b': str.WriteAll('$', (char)('0' + (byteCode[2] >> 5))); break;
|
||||
|
||||
default: str.Write(op[i]); break;
|
||||
}
|
||||
|
|
|
@ -138,6 +138,7 @@
|
|||
this.cboSearch.TabIndex = 4;
|
||||
this.cboSearch.TextUpdate += new System.EventHandler(this.cboSearch_TextUpdate);
|
||||
this.cboSearch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.cboSearch_KeyDown);
|
||||
this.cboSearch.Leave += new System.EventHandler(this.cboSearch_Leave);
|
||||
//
|
||||
// hScrollBar
|
||||
//
|
||||
|
@ -149,14 +150,15 @@
|
|||
//
|
||||
// ctrlTextbox
|
||||
//
|
||||
this.ctrlTextbox.CodeHighlightingEnabled = true;
|
||||
this.ctrlTextbox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ctrlTextbox.HideSelection = false;
|
||||
this.ctrlTextbox.HorizontalScrollWidth = 0;
|
||||
this.ctrlTextbox.Location = new System.Drawing.Point(0, 0);
|
||||
this.ctrlTextbox.Name = "ctrlTextbox";
|
||||
this.ctrlTextbox.ShowCompactPrgAddresses = false;
|
||||
this.ctrlTextbox.ShowByteCode = false;
|
||||
this.ctrlTextbox.ShowAbsoluteAddreses = false;
|
||||
this.ctrlTextbox.ShowByteCode = false;
|
||||
this.ctrlTextbox.ShowCompactPrgAddresses = false;
|
||||
this.ctrlTextbox.ShowLineNumbers = true;
|
||||
this.ctrlTextbox.ShowMemoryValues = false;
|
||||
this.ctrlTextbox.ShowSingleContentLineNotes = true;
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Mesen.GUI.Debugger.Controls
|
|||
{
|
||||
public event EventHandler ScrollPositionChanged;
|
||||
|
||||
private string _noMatchString = null;
|
||||
private bool _showScrollbars = true;
|
||||
|
||||
public new event MouseEventHandler MouseUp
|
||||
|
@ -466,13 +467,24 @@ namespace Mesen.GUI.Debugger.Controls
|
|||
|
||||
private void cboSearch_TextUpdate(object sender, EventArgs e)
|
||||
{
|
||||
if(_noMatchString != null && this.cboSearch.Text.StartsWith(_noMatchString)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.ctrlTextbox.Search(this.cboSearch.Text, false, true)) {
|
||||
_noMatchString = this.cboSearch.Text;
|
||||
this.cboSearch.BackColor = Color.Coral;
|
||||
} else {
|
||||
_noMatchString = null;
|
||||
this.cboSearch.BackColor = Color.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private void cboSearch_Leave(object sender, EventArgs e)
|
||||
{
|
||||
_noMatchString = null;
|
||||
}
|
||||
|
||||
private void cboSearch_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if(e.KeyCode == Keys.Enter) {
|
||||
|
|
|
@ -7,11 +7,11 @@ private:
|
|||
char _buffer[1000];
|
||||
uint16_t _pos = 0;
|
||||
|
||||
void Write() {}
|
||||
void WriteAll() {}
|
||||
|
||||
public:
|
||||
FastString() {}
|
||||
FastString(const char* str, uint16_t size) { Write(str, size); }
|
||||
FastString(const char* str, int size) { Write(str, size); }
|
||||
FastString(string &str) { Write(str); }
|
||||
|
||||
void Write(char c)
|
||||
|
@ -19,7 +19,7 @@ public:
|
|||
_buffer[_pos++] = c;
|
||||
}
|
||||
|
||||
void Write(const char* str, uint16_t size)
|
||||
void Write(const char* str, int size)
|
||||
{
|
||||
memcpy(_buffer + _pos, str, size);
|
||||
_pos += size;
|
||||
|
@ -55,11 +55,16 @@ public:
|
|||
return _buffer;
|
||||
}
|
||||
|
||||
uint16_t GetSize()
|
||||
{
|
||||
return _pos;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void Write(T first, Args... args)
|
||||
void WriteAll(T first, Args... args)
|
||||
{
|
||||
Write(first);
|
||||
Write(args...);
|
||||
WriteAll(args...);
|
||||
}
|
||||
|
||||
const char operator[](int idx)
|
||||
|
|
|
@ -20,6 +20,26 @@ const vector<string> HexUtilities::_hexCache = { {
|
|||
"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
|
||||
} };
|
||||
|
||||
constexpr const char* _hexCharCache[256] = {
|
||||
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
|
||||
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
|
||||
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
|
||||
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
|
||||
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
|
||||
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
|
||||
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
|
||||
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
|
||||
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
|
||||
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
|
||||
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
|
||||
"B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
|
||||
"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
|
||||
"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
|
||||
"E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
|
||||
"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
|
||||
};
|
||||
|
||||
|
||||
int HexUtilities::FromHex(string hex)
|
||||
{
|
||||
int value = 0;
|
||||
|
@ -41,6 +61,11 @@ string HexUtilities::ToHex(uint8_t value)
|
|||
return _hexCache[value];
|
||||
}
|
||||
|
||||
const char* HexUtilities::ToHexChar(uint8_t value)
|
||||
{
|
||||
return _hexCharCache[value];
|
||||
}
|
||||
|
||||
string HexUtilities::ToHex(uint16_t value)
|
||||
{
|
||||
return _hexCache[value >> 8] + _hexCache[value & 0xFF];
|
||||
|
|
|
@ -8,6 +8,7 @@ private:
|
|||
|
||||
public:
|
||||
static string ToHex(uint8_t value);
|
||||
static const char* ToHexChar(uint8_t value);
|
||||
static string ToHex(uint16_t value);
|
||||
static string ToHex(uint32_t value, bool fullSize = false);
|
||||
static string ToHex(int32_t value, bool fullSize = false);
|
||||
|
|
394
enc_temp_folder/38a4414c6e65c44ed0de164f19c546ab/PpuTools.cpp
Normal file
394
enc_temp_folder/38a4414c6e65c44ed0de164f19c546ab/PpuTools.cpp
Normal file
|
@ -0,0 +1,394 @@
|
|||
#include "stdafx.h"
|
||||
#include "PpuTools.h"
|
||||
#include "Ppu.h"
|
||||
#include "DebugTypes.h"
|
||||
#include "Console.h"
|
||||
#include "BaseCartridge.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "NotificationManager.h"
|
||||
|
||||
PpuTools::PpuTools(Console *console, Ppu *ppu)
|
||||
{
|
||||
_console = console;
|
||||
_ppu = ppu;
|
||||
}
|
||||
|
||||
uint8_t PpuTools::GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, const uint8_t bpp, const uint32_t pixelStart, const uint8_t shift)
|
||||
{
|
||||
uint8_t color;
|
||||
if(bpp == 2) {
|
||||
color = (((ram[(pixelStart + 0) & ramMask] >> shift) & 0x01) << 0);
|
||||
color |= (((ram[(pixelStart + 1) & ramMask] >> shift) & 0x01) << 1);
|
||||
} else if(bpp == 4) {
|
||||
color = (((ram[(pixelStart + 0) & ramMask] >> shift) & 0x01) << 0);
|
||||
color |= (((ram[(pixelStart + 1) & ramMask] >> shift) & 0x01) << 1);
|
||||
color |= (((ram[(pixelStart + 16) & ramMask] >> shift) & 0x01) << 2);
|
||||
color |= (((ram[(pixelStart + 17) & ramMask] >> shift) & 0x01) << 3);
|
||||
} else if(bpp == 8) {
|
||||
color = (((ram[(pixelStart + 0) & ramMask] >> shift) & 0x01) << 0);
|
||||
color |= (((ram[(pixelStart + 1) & ramMask] >> shift) & 0x01) << 1);
|
||||
color |= (((ram[(pixelStart + 16) & ramMask] >> shift) & 0x01) << 2);
|
||||
color |= (((ram[(pixelStart + 17) & ramMask] >> shift) & 0x01) << 3);
|
||||
color |= (((ram[(pixelStart + 32) & ramMask] >> shift) & 0x01) << 4);
|
||||
color |= (((ram[(pixelStart + 33) & ramMask] >> shift) & 0x01) << 5);
|
||||
color |= (((ram[(pixelStart + 48) & ramMask] >> shift) & 0x01) << 6);
|
||||
color |= (((ram[(pixelStart + 49) & ramMask] >> shift) & 0x01) << 7);
|
||||
} else {
|
||||
throw std::runtime_error("unsupported bpp");
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
uint32_t PpuTools::ToArgb(uint16_t color)
|
||||
{
|
||||
uint8_t b = (color >> 10) << 3;
|
||||
uint8_t g = ((color >> 5) & 0x1F) << 3;
|
||||
uint8_t r = (color & 0x1F) << 3;
|
||||
|
||||
return 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
void PpuTools::BlendColors(uint8_t output[4], uint8_t input[4])
|
||||
{
|
||||
uint8_t alpha = input[3] + 1;
|
||||
uint8_t invertedAlpha = 256 - input[3];
|
||||
output[0] = (uint8_t)((alpha * input[0] + invertedAlpha * output[0]) >> 8);
|
||||
output[1] = (uint8_t)((alpha * input[1] + invertedAlpha * output[1]) >> 8);
|
||||
output[2] = (uint8_t)((alpha * input[2] + invertedAlpha * output[2]) >> 8);
|
||||
output[3] = 0xFF;
|
||||
}
|
||||
|
||||
uint32_t PpuTools::GetRgbPixelColor(uint8_t* cgram, uint8_t colorIndex, uint8_t palette, uint8_t bpp, bool directColorMode, uint16_t basePaletteOffset)
|
||||
{
|
||||
uint16_t paletteColor;
|
||||
if(bpp == 8 && directColorMode) {
|
||||
paletteColor = (
|
||||
((((colorIndex & 0x07) << 1) | (palette & 0x01)) << 1) |
|
||||
(((colorIndex & 0x38) | ((palette & 0x02) << 1)) << 4) |
|
||||
(((colorIndex & 0xC0) | ((palette & 0x04) << 3)) << 7)
|
||||
);
|
||||
} else {
|
||||
uint16_t paletteRamOffset = basePaletteOffset + (palette * (1 << bpp) + colorIndex) * 2;
|
||||
paletteColor = cgram[paletteRamOffset] | (cgram[paletteRamOffset + 1] << 8);
|
||||
}
|
||||
return ToArgb(paletteColor);
|
||||
}
|
||||
|
||||
void PpuTools::GetTileView(GetTileViewOptions options, uint8_t *source, uint32_t srcSize, uint8_t *cgram, uint32_t *outBuffer)
|
||||
{
|
||||
uint8_t* ram = source;
|
||||
uint32_t ramMask = srcSize - 1;
|
||||
uint8_t bpp;
|
||||
|
||||
bool directColor = false;
|
||||
switch(options.Format) {
|
||||
case TileFormat::Bpp2: bpp = 2; break;
|
||||
case TileFormat::Bpp4: bpp = 4; break;
|
||||
|
||||
case TileFormat::DirectColor:
|
||||
directColor = true;
|
||||
bpp = 8;
|
||||
break;
|
||||
|
||||
case TileFormat::Mode7:
|
||||
bpp = 16;
|
||||
break;
|
||||
|
||||
case TileFormat::Mode7DirectColor:
|
||||
directColor = true;
|
||||
bpp = 16;
|
||||
break;
|
||||
|
||||
default: bpp = 8; break;
|
||||
}
|
||||
|
||||
int bytesPerTile = 64 * bpp / 8;
|
||||
int tileCount = 0x10000 / bytesPerTile;
|
||||
|
||||
uint16_t bgColor = (cgram[1] << 8) | cgram[0];
|
||||
for(int i = 0; i < 512 * 512; i++) {
|
||||
outBuffer[i] = ToArgb(bgColor);
|
||||
}
|
||||
|
||||
int rowCount = tileCount / options.Width;
|
||||
|
||||
for(int row = 0; row < rowCount; row++) {
|
||||
uint32_t baseOffset = row * bytesPerTile * options.Width;
|
||||
|
||||
for(int column = 0; column < options.Width; column++) {
|
||||
uint32_t addr = baseOffset + bytesPerTile * column;
|
||||
|
||||
int baseOutputOffset;
|
||||
if(options.Layout == TileLayout::SingleLine8x16) {
|
||||
int displayColumn = column / 2 + ((row & 0x01) ? options.Width/2 : 0);
|
||||
int displayRow = (row & ~0x01) + ((column & 0x01) ? 1 : 0);
|
||||
baseOutputOffset = displayRow * options.Width * 64 + displayColumn * 8;
|
||||
} else if(options.Layout == TileLayout::SingleLine16x16) {
|
||||
int displayColumn = (column / 2) + (column & 0x01) + ((row & 0x01) ? options.Width/2 : 0) + ((column & 0x02) ? -1 : 0);
|
||||
int displayRow = (row & ~0x01) + ((column & 0x02) ? 1 : 0);
|
||||
baseOutputOffset = displayRow * options.Width * 64 + displayColumn * 8;
|
||||
} else {
|
||||
baseOutputOffset = row * options.Width * 64 + column * 8;
|
||||
}
|
||||
|
||||
if(options.Format == TileFormat::Mode7 || options.Format == TileFormat::Mode7DirectColor) {
|
||||
for(int y = 0; y < 8; y++) {
|
||||
uint32_t pixelStart = addr + y * 16;
|
||||
|
||||
for(int x = 0; x < 8; x++) {
|
||||
uint8_t color = ram[(pixelStart + x * 2 + 1) & ramMask];
|
||||
|
||||
if(color != 0) {
|
||||
uint32_t rgbColor;
|
||||
if(directColor) {
|
||||
rgbColor = ToArgb(((color & 0x07) << 2) | ((color & 0x38) << 4) | ((color & 0xC0) << 7));
|
||||
} else {
|
||||
rgbColor = GetRgbPixelColor(cgram, color, 0, 8, false, 0);
|
||||
}
|
||||
outBuffer[baseOutputOffset + (y*options.Width*8) + x] = rgbColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int y = 0; y < 8; y++) {
|
||||
uint32_t pixelStart = addr + y * 2;
|
||||
for(int x = 0; x < 8; x++) {
|
||||
uint8_t color = GetTilePixelColor(ram, ramMask, bpp, pixelStart, 7 - x);
|
||||
if(color != 0) {
|
||||
outBuffer[baseOutputOffset + (y*options.Width*8) + x] = GetRgbPixelColor(cgram, color, options.Palette, bpp, directColor, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PpuTools::GetTilemap(GetTilemapOptions options, uint8_t* vram, uint8_t* cgram, uint32_t* outBuffer)
|
||||
{
|
||||
static constexpr uint8_t layerBpp[8][4] = {
|
||||
{ 2,2,2,2 }, { 4,4,2,0 }, { 4,4,0,0 }, { 8,4,0,0 }, { 8,2,0,0 }, { 4,2,0,0 }, { 4,0,0,0 }, { 8,0,0,0 }
|
||||
};
|
||||
|
||||
PpuState state = _ppu->GetState();
|
||||
options.BgMode = state.BgMode;
|
||||
|
||||
bool directColor = state.DirectColorMode && (options.BgMode == 3 || options.BgMode == 4 || options.BgMode == 7);
|
||||
|
||||
uint16_t basePaletteOffset = 0;
|
||||
if(options.BgMode == 0) {
|
||||
basePaletteOffset = options.Layer * 64;
|
||||
}
|
||||
|
||||
LayerConfig layer = state.Layers[options.Layer];
|
||||
|
||||
uint32_t bgColor = ToArgb((cgram[1] << 8) | cgram[0]);
|
||||
std::fill(outBuffer, outBuffer + 1024*1024, bgColor);
|
||||
|
||||
uint8_t bpp = layerBpp[options.BgMode][options.Layer];
|
||||
if(bpp == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool largeTileWidth = layer.LargeTiles || options.BgMode == 5 || options.BgMode == 6;
|
||||
bool largeTileHeight = layer.LargeTiles;
|
||||
|
||||
if(options.BgMode == 7) {
|
||||
for(int row = 0; row < 128; row++) {
|
||||
for(int column = 0; column < 128; column++) {
|
||||
uint32_t tileIndex = vram[row * 256 + column * 2];
|
||||
uint32_t tileAddr = tileIndex * 128;
|
||||
|
||||
for(int y = 0; y < 8; y++) {
|
||||
uint32_t pixelStart = tileAddr + y * 16;
|
||||
|
||||
for(int x = 0; x < 8; x++) {
|
||||
uint8_t color = vram[pixelStart + x * 2 + 1];
|
||||
|
||||
if(color != 0) {
|
||||
uint32_t rgbColor;
|
||||
if(directColor) {
|
||||
rgbColor = ToArgb(((color & 0x07) << 2) | ((color & 0x38) << 4) | ((color & 0xC0) << 7));
|
||||
} else {
|
||||
rgbColor = GetRgbPixelColor(cgram, color, 0, 8, false, 0);
|
||||
}
|
||||
outBuffer[((row * 8) + y) * 1024 + column * 8 + x] = rgbColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int tileHeight = largeTileHeight ? 16 : 8;
|
||||
int tileWidth = largeTileWidth ? 16 : 8;
|
||||
for(int row = 0; row < (layer.DoubleHeight ? 64 : 32); row++) {
|
||||
uint16_t addrVerticalScrollingOffset = layer.DoubleHeight ? ((row & 0x20) << (layer.DoubleWidth ? 6 : 5)) : 0;
|
||||
uint16_t baseOffset = layer.TilemapAddress + addrVerticalScrollingOffset + ((row & 0x1F) << 5);
|
||||
|
||||
for(int column = 0; column < (layer.DoubleWidth ? 64 : 32); column++) {
|
||||
uint16_t addr = (baseOffset + (column & 0x1F) + (layer.DoubleWidth ? ((column & 0x20) << 5) : 0)) << 1;
|
||||
|
||||
bool vMirror = (vram[addr + 1] & 0x80) != 0;
|
||||
bool hMirror = (vram[addr + 1] & 0x40) != 0;
|
||||
uint16_t tileIndex = ((vram[addr + 1] & 0x03) << 8) | vram[addr];
|
||||
|
||||
for(int y = 0; y < tileHeight; y++) {
|
||||
uint8_t yOffset = vMirror ? (7 - (y & 0x07)) : (y & 0x07);
|
||||
|
||||
for(int x = 0; x < tileWidth; x++) {
|
||||
uint16_t tileOffset = (
|
||||
(largeTileHeight ? ((y & 0x08) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) : 0) +
|
||||
(largeTileWidth ? ((x & 0x08) ? (hMirror ? 0 : 1) : (hMirror ? 1 : 0)) : 0)
|
||||
);
|
||||
|
||||
uint16_t tileStart = (layer.ChrAddress << 1) + ((tileIndex + tileOffset) & 0x3FF) * 8 * bpp;
|
||||
uint16_t pixelStart = tileStart + yOffset * 2;
|
||||
|
||||
uint8_t shift = hMirror ? (x & 0x07) : (7 - (x & 0x07));
|
||||
uint8_t color = GetTilePixelColor(vram, Ppu::VideoRamSize - 1, bpp, pixelStart, shift);
|
||||
if(color != 0) {
|
||||
uint8_t palette = bpp == 8 ? 0 : (vram[addr + 1] >> 2) & 0x07;
|
||||
outBuffer[((row * tileHeight) + y) * 1024 + column * tileWidth + x] = GetRgbPixelColor(cgram, color, palette, bpp, directColor, basePaletteOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr uint8_t _oamSizes[8][2][2] = {
|
||||
{ { 1, 1 }, { 2, 2 } }, //8x8 + 16x16
|
||||
{ { 1, 1 }, { 4, 4 } }, //8x8 + 32x32
|
||||
{ { 1, 1 }, { 8, 8 } }, //8x8 + 64x64
|
||||
{ { 2, 2 }, { 4, 4 } }, //16x16 + 32x32
|
||||
{ { 2, 2 }, { 8, 8 } }, //16x16 + 64x64
|
||||
{ { 4, 4 }, { 8, 8 } }, //32x32 + 64x64
|
||||
{ { 2, 4 }, { 4, 8 } }, //16x32 + 32x64
|
||||
{ { 2, 4 }, { 4, 4 } } //16x32 + 32x32
|
||||
};
|
||||
|
||||
void PpuTools::GetSpritePreview(GetSpritePreviewOptions options, PpuState state, uint8_t *vram, uint8_t *oamRam, uint8_t *cgram, uint32_t *outBuffer)
|
||||
{
|
||||
//TODO
|
||||
//uint16_t baseAddr = state.EnableOamPriority ? (_internalOamAddress & 0x1FC) : 0;
|
||||
uint16_t baseAddr = 0;
|
||||
|
||||
bool filled[256 * 240] = {};
|
||||
int lastScanline = state.OverscanMode ? 239 : 224;
|
||||
std::fill(outBuffer, outBuffer + 256 * lastScanline, 0xFF888888);
|
||||
std::fill(outBuffer + 256 * lastScanline, outBuffer + 256 * 240, 0xFF000000);
|
||||
|
||||
for(int screenY = 0; screenY < lastScanline; screenY++) {
|
||||
for(int i = 508; i >= 0; i -= 4) {
|
||||
uint16_t addr = (baseAddr + i) & 0x1FF;
|
||||
uint8_t y = oamRam[addr + 1];
|
||||
|
||||
uint8_t highTableOffset = addr >> 4;
|
||||
uint8_t shift = ((addr >> 2) & 0x03) << 1;
|
||||
uint8_t highTableValue = oamRam[0x200 | highTableOffset] >> shift;
|
||||
uint8_t largeSprite = (highTableValue & 0x02) >> 1;
|
||||
uint8_t height = _oamSizes[state.OamMode][largeSprite][1] << 3;
|
||||
|
||||
if(state.ObjInterlace) {
|
||||
height /= 2;
|
||||
}
|
||||
|
||||
uint8_t endY = (y + height) & 0xFF;
|
||||
|
||||
bool visible = (screenY >= y && screenY < endY) || (endY < y && screenY < endY);
|
||||
if(!visible) {
|
||||
//Not visible on this scanline
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t width = _oamSizes[state.OamMode][largeSprite][0] << 3;
|
||||
uint16_t sign = (highTableValue & 0x01) << 8;
|
||||
int16_t x = (int16_t)((sign | oamRam[addr]) << 7) >> 7;
|
||||
|
||||
if(x != -256 && (x + width <= 0 || x > 255)) {
|
||||
//Sprite is not visible (and must be ignored for time/range flag calculations)
|
||||
//Sprites at X=-256 are always used when considering Time/Range flag calculations, but not actually drawn.
|
||||
continue;
|
||||
}
|
||||
|
||||
int tileRow = (oamRam[addr + 2] & 0xF0) >> 4;
|
||||
int tileColumn = oamRam[addr + 2] & 0x0F;
|
||||
|
||||
uint8_t flags = oamRam[addr + 3];
|
||||
bool useSecondTable = (flags & 0x01) != 0;
|
||||
uint8_t palette = (flags >> 1) & 0x07;
|
||||
//uint8_t priority = (flags >> 4) & 0x03;
|
||||
bool horizontalMirror = (flags & 0x40) != 0;
|
||||
bool verticalMirror = (flags & 0x80) != 0;
|
||||
|
||||
uint8_t yOffset;
|
||||
int rowOffset;
|
||||
|
||||
int yGap = (screenY - y);
|
||||
if(state.ObjInterlace) {
|
||||
yGap <<= 1;
|
||||
}
|
||||
if(verticalMirror) {
|
||||
yOffset = (height - 1 - yGap) & 0x07;
|
||||
rowOffset = (height - 1 - yGap) >> 3;
|
||||
} else {
|
||||
yOffset = yGap & 0x07;
|
||||
rowOffset = yGap >> 3;
|
||||
}
|
||||
|
||||
uint8_t row = (tileRow + rowOffset) & 0x0F;
|
||||
|
||||
for(int j = std::max<int16_t>(x, 0); j < x + width && j < 256; j++) {
|
||||
uint32_t outOffset = screenY * 256 + j;
|
||||
if(filled[outOffset]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t xOffset;
|
||||
int columnOffset;
|
||||
if(horizontalMirror) {
|
||||
xOffset = (width - (j - x) - 1) & 0x07;
|
||||
columnOffset = (width - (j - x) - 1) >> 3;
|
||||
} else {
|
||||
xOffset = (j - x) & 0x07;
|
||||
columnOffset = (j - x) >> 3;
|
||||
}
|
||||
|
||||
uint8_t column = (tileColumn + columnOffset) & 0x0F;
|
||||
uint8_t tileIndex = (row << 4) | column;
|
||||
uint16_t tileStart = ((state.OamBaseAddress + (tileIndex << 4) + (useSecondTable ? state.OamAddressOffset : 0)) & 0x7FFF) << 1;
|
||||
|
||||
uint8_t color = GetTilePixelColor(vram, Ppu::VideoRamSize - 1, 4, tileStart + yOffset * 2, 7 - xOffset);
|
||||
if(color != 0) {
|
||||
if(options.SelectedSprite == i / 4) {
|
||||
filled[outOffset] = true;
|
||||
}
|
||||
outBuffer[outOffset] = GetRgbPixelColor(cgram, color, palette, 4, false, 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PpuTools::SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle)
|
||||
{
|
||||
//TODO Thread safety
|
||||
_updateTimings[viewerId] = (scanline << 16) | cycle;
|
||||
}
|
||||
|
||||
void PpuTools::RemoveViewer(uint32_t viewerId)
|
||||
{
|
||||
//TODO Thread safety
|
||||
_updateTimings.erase(viewerId);
|
||||
}
|
||||
|
||||
void PpuTools::UpdateViewers(uint16_t scanline, uint16_t cycle)
|
||||
{
|
||||
uint32_t currentCycle = (scanline << 16) | cycle;
|
||||
for(auto updateTiming : _updateTimings) {
|
||||
if(updateTiming.second == currentCycle) {
|
||||
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ViewerRefresh, (void*)(uint64_t)updateTiming.first);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue