From 8e7c6f68ff1a8e1144105c0b366b14df9dc3dbf3 Mon Sep 17 00:00:00 2001 From: TheRealQuantam <54358053+TheRealQuantam@users.noreply.github.com> Date: Tue, 20 Feb 2024 00:24:01 -0800 Subject: [PATCH 1/5] Work in progress --- src/drivers/Qt/ConsoleUtilities.h | 137 +++++++++ src/drivers/Qt/SymbolicDebug.cpp | 471 ++++++++++++++---------------- src/drivers/Qt/TraceFileWriter.h | 344 ++++++++++++++++++++++ src/drivers/Qt/TraceLogger.cpp | 275 +++++------------ 4 files changed, 778 insertions(+), 449 deletions(-) create mode 100644 src/drivers/Qt/TraceFileWriter.h diff --git a/src/drivers/Qt/ConsoleUtilities.h b/src/drivers/Qt/ConsoleUtilities.h index bbcefc07..93827117 100644 --- a/src/drivers/Qt/ConsoleUtilities.h +++ b/src/drivers/Qt/ConsoleUtilities.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include @@ -93,3 +94,139 @@ class QCheckBoxRO : public QCheckBox QString fceuGetOpcodeToolTip( uint8_t *opcode, int size ); QDialog *fceuCustomToolTipShow( const QPoint &globalPos, QDialog *popup ); + +// Faster replacement functions for sprintf +class StringBuilder +{ +public: + inline StringBuilder(char *str) : start(str), end(str) + { + *end = '\0'; + } + + inline char *str() const + { + return start; + } + + inline size_t size() const + { + return size_t(end - start); + } + + template + inline StringBuilder &operator << (T value) + { + return append(value); + } + + inline StringBuilder &append(char ch) + { + *(end++) = ch; + *end = '\0'; + + return *this; + } + + inline StringBuilder &append(const char *src) + { + size_t len = strlen(src); + memcpy(end, src, len + 1); + + end += len; + + return *this; + } + + template + StringBuilder &appendInt(T x, int minLen = 0, char leadChar = ' ', bool upperCase = false) + { + static_assert(Radix >= 2 && Radix <= 36, "Radix must be between 2 and 36"); + static_assert(std::is_integral::value, "T must be an integral type"); + + static const char *upperCaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + *lowerCaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz"; + const char *digits = upperCase ? upperCaseDigits : lowerCaseDigits; + + const bool isSigned = std::is_signed::value; + bool isNeg = isSigned && x < 0; + if (isSigned && isNeg) + // This convoluted expression is to silence an unnecessary compiler warning when unsigned + x = T(-std::make_signed::type(x)); + + unsigned i = 0; + if (x != 0) + { + do + { + end[i++] = digits[x % Radix]; + x /= Radix; + } while (x != 0); + } + else + end[i++] = '0'; + + if (isNeg) + end[i++] = '-'; + + if (minLen < 0) + { + end[i] = '\0'; + + strrev(end); + + for (; i < (unsigned)-minLen; i++) + end[i] = leadChar; + + end[i] = '\0'; + } + else + { + for (; i < (unsigned)minLen; i++) + end[i] = leadChar; + + end[i] = '\0'; + + strrev(end); + } + + end += i; + + return *this; + } + + template + inline StringBuilder &appendDec(T x, int minLen = 0, char leadChar = ' ') + { + appendInt<10>(x, minLen, leadChar); + + return *this; + } + + template + inline StringBuilder &appendHex(T x, int minLen = 0, bool upperCase = true, char leadChar = '0') + { + appendInt<16>(x, minLen, leadChar, upperCase); + + return *this; + } + + template + inline StringBuilder &appendAddr(T addr, int minLen = 4, bool upperCase = true) + { + *(end++) = '$'; + return appendHex(addr, minLen, upperCase); + } + + template + inline StringBuilder &appendLit(T x, int minLen = 2, bool upperCase = true) + { + *(end++) = '#'; + *(end++) = '$'; + return appendHex(x, minLen, upperCase); + } + +protected: + char *start; + char *end; +}; \ No newline at end of file diff --git a/src/drivers/Qt/SymbolicDebug.cpp b/src/drivers/Qt/SymbolicDebug.cpp index cf9cea88..070fb0ae 100644 --- a/src/drivers/Qt/SymbolicDebug.cpp +++ b/src/drivers/Qt/SymbolicDebug.cpp @@ -42,6 +42,7 @@ debugSymbol_t *replaceSymbols( int flags, int addr, char *str ) { debugSymbol_t *sym; + StringBuilder sb(str); if ( addr >= 0x8000 ) { @@ -59,36 +60,16 @@ debugSymbol_t *replaceSymbols( int flags, int addr, char *str ) } } + if ( !sym || !( flags & ASM_DEBUG_REPLACE ) ) + { + (sb << '$').appendHex( addr, flags & ASM_DEBUG_ADDR_02X ? 2 : 4 ); + + if ( sym ) + sb << ' '; + } + if ( sym ) - { - if ( flags & ASM_DEBUG_REPLACE ) - { - strcpy( str, sym->name().c_str() ); - } - else - { - if ( flags & ASM_DEBUG_ADDR_02X ) - { - sprintf( str, "$%02X ", addr ); - } - else - { - sprintf( str, "$%04X ", addr ); - } - strcat( str, sym->name().c_str() ); - } - } - else - { - if ( flags & ASM_DEBUG_ADDR_02X ) - { - sprintf( str, "$%02X", addr ); - } - else - { - sprintf( str, "$%04X", addr ); - } - } + sb << sym->name().c_str(); return sym; } @@ -97,10 +78,12 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS { debugSymbol_t *sym = NULL; debugSymbol_t *sym2 = NULL; - static char chr[8]={0}; + const char *chr; + char indReg; uint16_t tmp,tmp2; char stmp[128], stmp2[128]; bool symDebugEnable, showTrace; + StringBuilder sb(str); symDebugEnable = (flags & ASM_DEBUG_SYMS ) ? true : false; showTrace = (flags & ASM_DEBUG_TRACES) ? true : false; @@ -170,321 +153,309 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS case 0xF8: strcpy(str,"SED"); break; //(Indirect,X) - case 0x01: strcpy(chr,"ORA"); goto _indirectx; - case 0x21: strcpy(chr,"AND"); goto _indirectx; - case 0x41: strcpy(chr,"EOR"); goto _indirectx; - case 0x61: strcpy(chr,"ADC"); goto _indirectx; - case 0x81: strcpy(chr,"STA"); goto _indirectx; - case 0xA1: strcpy(chr,"LDA"); goto _indirectx; - case 0xC1: strcpy(chr,"CMP"); goto _indirectx; - case 0xE1: strcpy(chr,"SBC"); goto _indirectx; + case 0x01: chr = "ORA"; goto _indirectx; + case 0x21: chr = "AND"; goto _indirectx; + case 0x41: chr = "EOR"; goto _indirectx; + case 0x61: chr = "ADC"; goto _indirectx; + case 0x81: chr = "STA"; goto _indirectx; + case 0xA1: chr = "LDA"; goto _indirectx; + case 0xC1: chr = "CMP"; goto _indirectx; + case 0xE1: chr = "SBC"; goto _indirectx; _indirectx: indirectX(tmp); + indReg = 'X'; + _indirect: if ( symDebugEnable ) - { sym = replaceSymbols( flags, tmp, stmp ); - showTrace - ? sprintf(str,"%s ($%02X,X) @ %s = #$%02X", chr,opcode[1],stmp,GetMem(tmp)) - : sprintf(str,"%s ($%02X,X)", chr,opcode[1]); - } - else + + (sb << chr << " (").appendAddr(opcode[1], 2) << ',' << indReg << ')'; + + if (showTrace) { - showTrace - ? sprintf(str,"%s ($%02X,X) @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)) - : sprintf(str,"%s ($%02X,X)", chr,opcode[1]); + sb << " @ "; + if (symDebugEnable) + sb << stmp; + else + sb.appendAddr(tmp); + + (sb << " = ").appendLit(GetMem(tmp)); } break; //Zero Page - case 0x05: strcpy(chr,"ORA"); goto _zeropage; - case 0x06: strcpy(chr,"ASL"); goto _zeropage; - case 0x24: strcpy(chr,"BIT"); goto _zeropage; - case 0x25: strcpy(chr,"AND"); goto _zeropage; - case 0x26: strcpy(chr,"ROL"); goto _zeropage; - case 0x45: strcpy(chr,"EOR"); goto _zeropage; - case 0x46: strcpy(chr,"LSR"); goto _zeropage; - case 0x65: strcpy(chr,"ADC"); goto _zeropage; - case 0x66: strcpy(chr,"ROR"); goto _zeropage; - case 0x84: strcpy(chr,"STY"); goto _zeropage; - case 0x85: strcpy(chr,"STA"); goto _zeropage; - case 0x86: strcpy(chr,"STX"); goto _zeropage; - case 0xA4: strcpy(chr,"LDY"); goto _zeropage; - case 0xA5: strcpy(chr,"LDA"); goto _zeropage; - case 0xA6: strcpy(chr,"LDX"); goto _zeropage; - case 0xC4: strcpy(chr,"CPY"); goto _zeropage; - case 0xC5: strcpy(chr,"CMP"); goto _zeropage; - case 0xC6: strcpy(chr,"DEC"); goto _zeropage; - case 0xE4: strcpy(chr,"CPX"); goto _zeropage; - case 0xE5: strcpy(chr,"SBC"); goto _zeropage; - case 0xE6: strcpy(chr,"INC"); goto _zeropage; + case 0x05: chr = "ORA"; goto _zeropage; + case 0x06: chr = "ASL"; goto _zeropage; + case 0x24: chr = "BIT"; goto _zeropage; + case 0x25: chr = "AND"; goto _zeropage; + case 0x26: chr = "ROL"; goto _zeropage; + case 0x45: chr = "EOR"; goto _zeropage; + case 0x46: chr = "LSR"; goto _zeropage; + case 0x65: chr = "ADC"; goto _zeropage; + case 0x66: chr = "ROR"; goto _zeropage; + case 0x84: chr = "STY"; goto _zeropage; + case 0x85: chr = "STA"; goto _zeropage; + case 0x86: chr = "STX"; goto _zeropage; + case 0xA4: chr = "LDY"; goto _zeropage; + case 0xA5: chr = "LDA"; goto _zeropage; + case 0xA6: chr = "LDX"; goto _zeropage; + case 0xC4: chr = "CPY"; goto _zeropage; + case 0xC5: chr = "CMP"; goto _zeropage; + case 0xC6: chr = "DEC"; goto _zeropage; + case 0xE4: chr = "CPX"; goto _zeropage; + case 0xE5: chr = "SBC"; goto _zeropage; + case 0xE6: chr = "INC"; goto _zeropage; _zeropage: // ################################## Start of SP CODE ########################### // Change width to %04X // don't! + + sb << chr << ' '; if ( symDebugEnable ) { sym = replaceSymbols( flags | ASM_DEBUG_ADDR_02X, opcode[1], stmp ); - showTrace - ? sprintf(str,"%s %s = #$%02X", chr,stmp,GetMem(opcode[1])) - : sprintf(str,"%s %s", chr,stmp); + sb << stmp; } else - { - showTrace - ? sprintf(str,"%s $%02X = #$%02X", chr,opcode[1],GetMem(opcode[1])) - : sprintf(str,"%s $%02X", chr,opcode[1]); - } + sb.appendAddr(opcode[1], 2); + + if (showTrace) + (sb << " = ").appendLit(GetMem(opcode[1])); + // ################################## End of SP CODE ########################### break; //#Immediate - case 0x09: strcpy(chr,"ORA"); goto _immediate; - case 0x29: strcpy(chr,"AND"); goto _immediate; - case 0x49: strcpy(chr,"EOR"); goto _immediate; - case 0x69: strcpy(chr,"ADC"); goto _immediate; - //case 0x89: strcpy(chr,"STA"); goto _immediate; //baka, no STA #imm!! - case 0xA0: strcpy(chr,"LDY"); goto _immediate; - case 0xA2: strcpy(chr,"LDX"); goto _immediate; - case 0xA9: strcpy(chr,"LDA"); goto _immediate; - case 0xC0: strcpy(chr,"CPY"); goto _immediate; - case 0xC9: strcpy(chr,"CMP"); goto _immediate; - case 0xE0: strcpy(chr,"CPX"); goto _immediate; - case 0xE9: strcpy(chr,"SBC"); goto _immediate; + case 0x09: chr = "ORA"; goto _immediate; + case 0x29: chr = "AND"; goto _immediate; + case 0x49: chr = "EOR"; goto _immediate; + case 0x69: chr = "ADC"; goto _immediate; + //case 0x89: chr = "STA"; goto _immediate; //baka, no STA #imm!! + case 0xA0: chr = "LDY"; goto _immediate; + case 0xA2: chr = "LDX"; goto _immediate; + case 0xA9: chr = "LDA"; goto _immediate; + case 0xC0: chr = "CPY"; goto _immediate; + case 0xC9: chr = "CMP"; goto _immediate; + case 0xE0: chr = "CPX"; goto _immediate; + case 0xE9: chr = "SBC"; goto _immediate; _immediate: - sprintf(str,"%s #$%02X", chr,opcode[1]); + (sb << chr << ' ').appendLit(opcode[1]); break; //Absolute - case 0x0D: strcpy(chr,"ORA"); goto _absolute; - case 0x0E: strcpy(chr,"ASL"); goto _absolute; - case 0x2C: strcpy(chr,"BIT"); goto _absolute; - case 0x2D: strcpy(chr,"AND"); goto _absolute; - case 0x2E: strcpy(chr,"ROL"); goto _absolute; - case 0x4D: strcpy(chr,"EOR"); goto _absolute; - case 0x4E: strcpy(chr,"LSR"); goto _absolute; - case 0x6D: strcpy(chr,"ADC"); goto _absolute; - case 0x6E: strcpy(chr,"ROR"); goto _absolute; - case 0x8C: strcpy(chr,"STY"); goto _absolute; - case 0x8D: strcpy(chr,"STA"); goto _absolute; - case 0x8E: strcpy(chr,"STX"); goto _absolute; - case 0xAC: strcpy(chr,"LDY"); goto _absolute; - case 0xAD: strcpy(chr,"LDA"); goto _absolute; - case 0xAE: strcpy(chr,"LDX"); goto _absolute; - case 0xCC: strcpy(chr,"CPY"); goto _absolute; - case 0xCD: strcpy(chr,"CMP"); goto _absolute; - case 0xCE: strcpy(chr,"DEC"); goto _absolute; - case 0xEC: strcpy(chr,"CPX"); goto _absolute; - case 0xED: strcpy(chr,"SBC"); goto _absolute; - case 0xEE: strcpy(chr,"INC"); goto _absolute; + case 0x0D: chr = "ORA"; goto _absolute; + case 0x0E: chr = "ASL"; goto _absolute; + case 0x2C: chr = "BIT"; goto _absolute; + case 0x2D: chr = "AND"; goto _absolute; + case 0x2E: chr = "ROL"; goto _absolute; + case 0x4D: chr = "EOR"; goto _absolute; + case 0x4E: chr = "LSR"; goto _absolute; + case 0x6D: chr = "ADC"; goto _absolute; + case 0x6E: chr = "ROR"; goto _absolute; + case 0x8C: chr = "STY"; goto _absolute; + case 0x8D: chr = "STA"; goto _absolute; + case 0x8E: chr = "STX"; goto _absolute; + case 0xAC: chr = "LDY"; goto _absolute; + case 0xAD: chr = "LDA"; goto _absolute; + case 0xAE: chr = "LDX"; goto _absolute; + case 0xCC: chr = "CPY"; goto _absolute; + case 0xCD: chr = "CMP"; goto _absolute; + case 0xCE: chr = "DEC"; goto _absolute; + case 0xEC: chr = "CPX"; goto _absolute; + case 0xED: chr = "SBC"; goto _absolute; + case 0xEE: chr = "INC"; goto _absolute; _absolute: absolute(tmp); + sb << chr << ' '; if ( symDebugEnable ) { sym = replaceSymbols( flags, tmp, stmp ); - showTrace - ? sprintf(str,"%s %s = #$%02X", chr,stmp,GetMem(tmp)) - : sprintf(str,"%s %s", chr,stmp); + sb << stmp; } else - { - showTrace - ? sprintf(str,"%s $%04X = #$%02X", chr,tmp,GetMem(tmp)) - : sprintf(str,"%s $%04X", chr,tmp); - } + sb.appendAddr(tmp); + + if (showTrace) + (sb << " = ").appendLit(GetMem(tmp)); + break; //branches - case 0x10: strcpy(chr,"BPL"); goto _branch; - case 0x30: strcpy(chr,"BMI"); goto _branch; - case 0x50: strcpy(chr,"BVC"); goto _branch; - case 0x70: strcpy(chr,"BVS"); goto _branch; - case 0x90: strcpy(chr,"BCC"); goto _branch; - case 0xB0: strcpy(chr,"BCS"); goto _branch; - case 0xD0: strcpy(chr,"BNE"); goto _branch; - case 0xF0: strcpy(chr,"BEQ"); goto _branch; + case 0x10: chr = "BPL"; goto _branch; + case 0x30: chr = "BMI"; goto _branch; + case 0x50: chr = "BVC"; goto _branch; + case 0x70: chr = "BVS"; goto _branch; + case 0x90: chr = "BCC"; goto _branch; + case 0xB0: chr = "BCS"; goto _branch; + case 0xD0: chr = "BNE"; goto _branch; + case 0xF0: chr = "BEQ"; goto _branch; _branch: relative(tmp); + sb << chr << ' '; if ( symDebugEnable ) { sym = replaceSymbols( flags, tmp, stmp ); - sprintf(str,"%s %s", chr,stmp); + sb << stmp; } else - { - sprintf(str,"%s $%04X", chr,tmp); - } + sb.appendAddr(tmp); + break; //(Indirect),Y - case 0x11: strcpy(chr,"ORA"); goto _indirecty; - case 0x31: strcpy(chr,"AND"); goto _indirecty; - case 0x51: strcpy(chr,"EOR"); goto _indirecty; - case 0x71: strcpy(chr,"ADC"); goto _indirecty; - case 0x91: strcpy(chr,"STA"); goto _indirecty; - case 0xB1: strcpy(chr,"LDA"); goto _indirecty; - case 0xD1: strcpy(chr,"CMP"); goto _indirecty; - case 0xF1: strcpy(chr,"SBC"); goto _indirecty; + case 0x11: chr = "ORA"; goto _indirecty; + case 0x31: chr = "AND"; goto _indirecty; + case 0x51: chr = "EOR"; goto _indirecty; + case 0x71: chr = "ADC"; goto _indirecty; + case 0x91: chr = "STA"; goto _indirecty; + case 0xB1: chr = "LDA"; goto _indirecty; + case 0xD1: chr = "CMP"; goto _indirecty; + case 0xF1: chr = "SBC"; goto _indirecty; _indirecty: indirectY(tmp); + indReg = 'Y'; - if ( symDebugEnable ) - { - sym = replaceSymbols( flags, tmp, stmp ); - showTrace - ? sprintf(str,"%s ($%02X),Y @ %s = #$%02X", chr,opcode[1],stmp,GetMem(tmp)) - : sprintf(str,"%s ($%02X),Y", chr,opcode[1]); - } - else - { - showTrace - ? sprintf(str,"%s ($%02X),Y @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)) - : sprintf(str,"%s ($%02X),Y", chr,opcode[1]); - } - break; + goto _indirect; //Zero Page,X - case 0x15: strcpy(chr,"ORA"); goto _zeropagex; - case 0x16: strcpy(chr,"ASL"); goto _zeropagex; - case 0x35: strcpy(chr,"AND"); goto _zeropagex; - case 0x36: strcpy(chr,"ROL"); goto _zeropagex; - case 0x55: strcpy(chr,"EOR"); goto _zeropagex; - case 0x56: strcpy(chr,"LSR"); goto _zeropagex; - case 0x75: strcpy(chr,"ADC"); goto _zeropagex; - case 0x76: strcpy(chr,"ROR"); goto _zeropagex; - case 0x94: strcpy(chr,"STY"); goto _zeropagex; - case 0x95: strcpy(chr,"STA"); goto _zeropagex; - case 0xB4: strcpy(chr,"LDY"); goto _zeropagex; - case 0xB5: strcpy(chr,"LDA"); goto _zeropagex; - case 0xD5: strcpy(chr,"CMP"); goto _zeropagex; - case 0xD6: strcpy(chr,"DEC"); goto _zeropagex; - case 0xF5: strcpy(chr,"SBC"); goto _zeropagex; - case 0xF6: strcpy(chr,"INC"); goto _zeropagex; + case 0x15: chr = "ORA"; goto _zeropagex; + case 0x16: chr = "ASL"; goto _zeropagex; + case 0x35: chr = "AND"; goto _zeropagex; + case 0x36: chr = "ROL"; goto _zeropagex; + case 0x55: chr = "EOR"; goto _zeropagex; + case 0x56: chr = "LSR"; goto _zeropagex; + case 0x75: chr = "ADC"; goto _zeropagex; + case 0x76: chr = "ROR"; goto _zeropagex; + case 0x94: chr = "STY"; goto _zeropagex; + case 0x95: chr = "STA"; goto _zeropagex; + case 0xB4: chr = "LDY"; goto _zeropagex; + case 0xB5: chr = "LDA"; goto _zeropagex; + case 0xD5: chr = "CMP"; goto _zeropagex; + case 0xD6: chr = "DEC"; goto _zeropagex; + case 0xF5: chr = "SBC"; goto _zeropagex; + case 0xF6: chr = "INC"; goto _zeropagex; _zeropagex: zpIndex(tmp,RX); + indReg = 'X'; + + _indexed: // ################################## Start of SP CODE ########################### // Change width to %04X // don't! if ( symDebugEnable ) - { sym = replaceSymbols( flags, tmp, stmp ); - showTrace - ? sprintf(str,"%s $%02X,X @ %s = #$%02X", chr,opcode[1],stmp,GetMem(tmp)) - : sprintf(str,"%s $%02X,X", chr,opcode[1]); - } - else + + (sb << chr << ' ').appendAddr(opcode[1], 2) << ',' << indReg; + if (showTrace) { - showTrace - ? sprintf(str,"%s $%02X,X @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)) - : sprintf(str,"%s $%02X,X", chr,opcode[1]); + sb << " @ "; + if (symDebugEnable) + sb << stmp; + else + sb.appendAddr(tmp); + + (sb << " = ").appendLit(GetMem(tmp)); } // ################################## End of SP CODE ########################### break; //Absolute,Y - case 0x19: strcpy(chr,"ORA"); goto _absolutey; - case 0x39: strcpy(chr,"AND"); goto _absolutey; - case 0x59: strcpy(chr,"EOR"); goto _absolutey; - case 0x79: strcpy(chr,"ADC"); goto _absolutey; - case 0x99: strcpy(chr,"STA"); goto _absolutey; - case 0xB9: strcpy(chr,"LDA"); goto _absolutey; - case 0xBE: strcpy(chr,"LDX"); goto _absolutey; - case 0xD9: strcpy(chr,"CMP"); goto _absolutey; - case 0xF9: strcpy(chr,"SBC"); goto _absolutey; + case 0x19: chr = "ORA"; goto _absolutey; + case 0x39: chr = "AND"; goto _absolutey; + case 0x59: chr = "EOR"; goto _absolutey; + case 0x79: chr = "ADC"; goto _absolutey; + case 0x99: chr = "STA"; goto _absolutey; + case 0xB9: chr = "LDA"; goto _absolutey; + case 0xBE: chr = "LDX"; goto _absolutey; + case 0xD9: chr = "CMP"; goto _absolutey; + case 0xF9: chr = "SBC"; goto _absolutey; _absolutey: absolute(tmp); tmp2=(tmp+RY); + indReg = 'Y'; + + _absindexed: + sb << chr << ' '; if ( symDebugEnable ) { sym = replaceSymbols( flags, tmp , stmp ); sym2 = replaceSymbols( flags, tmp2, stmp2 ); - showTrace - ? sprintf(str,"%s %s,Y @ %s = #$%02X", chr,stmp,stmp2,GetMem(tmp2)) - : sprintf(str,"%s %s,Y", chr,stmp); + sb << stmp; } else + sb.appendAddr(tmp); + + sb << ',' << indReg; + + if (showTrace) { - showTrace - ? sprintf(str,"%s $%04X,Y @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)) - : sprintf(str,"%s $%04X,Y", chr,tmp); + sb << " @ "; + if (symDebugEnable) + sb << stmp2; + else + sb.appendAddr(tmp2); + + (sb << " = ").appendLit(GetMem(tmp2)); } + break; //Absolute,X - case 0x1D: strcpy(chr,"ORA"); goto _absolutex; - case 0x1E: strcpy(chr,"ASL"); goto _absolutex; - case 0x3D: strcpy(chr,"AND"); goto _absolutex; - case 0x3E: strcpy(chr,"ROL"); goto _absolutex; - case 0x5D: strcpy(chr,"EOR"); goto _absolutex; - case 0x5E: strcpy(chr,"LSR"); goto _absolutex; - case 0x7D: strcpy(chr,"ADC"); goto _absolutex; - case 0x7E: strcpy(chr,"ROR"); goto _absolutex; - case 0x9D: strcpy(chr,"STA"); goto _absolutex; - case 0xBC: strcpy(chr,"LDY"); goto _absolutex; - case 0xBD: strcpy(chr,"LDA"); goto _absolutex; - case 0xDD: strcpy(chr,"CMP"); goto _absolutex; - case 0xDE: strcpy(chr,"DEC"); goto _absolutex; - case 0xFD: strcpy(chr,"SBC"); goto _absolutex; - case 0xFE: strcpy(chr,"INC"); goto _absolutex; + case 0x1D: chr = "ORA"; goto _absolutex; + case 0x1E: chr = "ASL"; goto _absolutex; + case 0x3D: chr = "AND"; goto _absolutex; + case 0x3E: chr = "ROL"; goto _absolutex; + case 0x5D: chr = "EOR"; goto _absolutex; + case 0x5E: chr = "LSR"; goto _absolutex; + case 0x7D: chr = "ADC"; goto _absolutex; + case 0x7E: chr = "ROR"; goto _absolutex; + case 0x9D: chr = "STA"; goto _absolutex; + case 0xBC: chr = "LDY"; goto _absolutex; + case 0xBD: chr = "LDA"; goto _absolutex; + case 0xDD: chr = "CMP"; goto _absolutex; + case 0xDE: chr = "DEC"; goto _absolutex; + case 0xFD: chr = "SBC"; goto _absolutex; + case 0xFE: chr = "INC"; goto _absolutex; _absolutex: absolute(tmp); tmp2=(tmp+RX); - if ( symDebugEnable ) - { - sym = replaceSymbols( flags, tmp , stmp ); - sym2 = replaceSymbols( flags, tmp2, stmp2 ); - showTrace - ? sprintf(str,"%s %s,X @ %s = #$%02X", chr,stmp,stmp2,GetMem(tmp2)) - : sprintf(str,"%s %s,X", chr,stmp); - } - else - { - showTrace - ? sprintf(str,"%s $%04X,X @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)) - : sprintf(str,"%s $%04X,X", chr,tmp); - } - break; + indReg = 'X'; + + goto _absindexed; //jumps - case 0x20: strcpy(chr,"JSR"); goto _jump; - case 0x4C: strcpy(chr,"JMP"); goto _jump; - case 0x6C: absolute(tmp); sprintf(str,"JMP ($%04X) = $%04X", tmp,GetMem(tmp)|GetMem(tmp+1)<<8); break; + case 0x20: chr = "JSR"; goto _jump; + case 0x4C: chr = "JMP"; goto _jump; _jump: absolute(tmp); - if ( symDebugEnable ) + sb << chr << ' '; + if (symDebugEnable) { - sym = replaceSymbols( flags, tmp, stmp ); - sprintf(str,"%s %s", chr,stmp); + sym = replaceSymbols(flags, tmp, stmp); + sb << stmp; } else - { - sprintf(str,"%s $%04X", chr,tmp); - } + sb.appendAddr(tmp); + + break; + + case 0x6C: + absolute(tmp); + + (sb << "JMP (").appendAddr(tmp); + (sb << ") = ").appendAddr(GetMem(tmp) | GetMem(tmp + 1) << 8); + break; //Zero Page,Y - case 0x96: strcpy(chr,"STX"); goto _zeropagey; - case 0xB6: strcpy(chr,"LDX"); goto _zeropagey; + case 0x96: chr = "STX"; goto _zeropagey; + case 0xB6: chr = "LDX"; goto _zeropagey; _zeropagey: zpIndex(tmp,RY); - // ################################## Start of SP CODE ########################### - // Change width to %04X // don't! - if ( symDebugEnable ) - { - sym = replaceSymbols( flags, tmp, stmp ); - showTrace - ? sprintf(str,"%s $%02X,Y @ %s = #$%02X", chr,opcode[1],stmp,GetMem(tmp)) - : sprintf(str,"%s $%02X,Y", chr,opcode[1]); - } - else - { - showTrace - ? sprintf(str,"%s $%02X,Y @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)) - : sprintf(str,"%s $%02X,Y", chr,opcode[1]); - } - // ################################## End of SP CODE ########################### - break; + indReg = 'Y'; + + goto _indexed; //UNDEFINED default: strcpy(str,"ERROR"); break; diff --git a/src/drivers/Qt/TraceFileWriter.h b/src/drivers/Qt/TraceFileWriter.h new file mode 100644 index 00000000..91a86185 --- /dev/null +++ b/src/drivers/Qt/TraceFileWriter.h @@ -0,0 +1,344 @@ +#pragma once + +#include +#include +#include + +class TraceFileWriter +{ +public: + static const size_t BlockSize = 4 << 10; + static const size_t BuffSize = BlockSize * 257; + static const size_t FlushSize = BlockSize * 256; + + inline TraceFileWriter() + { + HMODULE kernel32 = GetModuleHandleA("kernel32"); + SetFileInformationByHandle = kernel32 ? (SetFileInformationByHandlePtr)GetProcAddress(kernel32, "SetFileInformationByHandle") : nullptr; + + initialize(); + } + + inline ~TraceFileWriter() + { + if (isOpen) + close(); + } + + inline bool getOpen() const + { + return isOpen; + } + + inline DWORD getLastError() const + { + return lastErr; + } + + bool open(const char *fileName, bool isPaused = false) + { + HANDLE file = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + if (file == INVALID_HANDLE_VALUE) + { + lastErr = GetLastError(); + return false; + } + + initialize(false, isPaused, fileName, file); + + do + { + for (unsigned i = 0; i < 2; i++) + { + buffers[i] = (char *)VirtualAlloc(NULL, BuffSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (buffers[i] == nullptr) + { + lastErr = GetLastError(); + break; + } + + ovls[i].hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (ovls[i].hEvent == nullptr) + { + lastErr = GetLastError(); + break; + } + } + + if (!lastErr) + isOpen = true; + } while (false); + + if (!isOpen) + cleanup(); + + return isOpen; + } + + void close() + { + if (!isOpen) + { + lastErr = ERROR_FILE_NOT_FOUND; + return; + } + + for (unsigned i = 0; i < 2; i++) + waitForBuffer(i); + + writeTail(); + + cleanup(); + } + + bool setPause(bool isPaused) + { + if (isPaused && !this->isPaused) + { + for (unsigned i = 0; i < 2; i++) + { + if (!waitForBuffer(i)) + return false; + } + + if (!writeTail()) + return false; + } + + this->isPaused = isPaused; + lastErr = ERROR_SUCCESS; + + return true; + } + + bool writeLine(const char *line) + { + if (!isOpen) + { + lastErr = ERROR_FILE_NOT_FOUND; + return false; + } + + char *buff = buffers[buffIdx]; + OVERLAPPED *ovl = &ovls[buffIdx]; + + size_t lineLen = strlen(line); + if (buffOffs + lineLen + 1 > BuffSize) + { + lastErr = ERROR_INTERNAL_ERROR; + return false; + } + + memcpy(buff + buffOffs, line, lineLen); + buffOffs += lineLen; + buff[buffOffs++] = '\n'; + + unsigned prevBuff = (buffIdx + 1) % 2; + if (!waitForBuffer(prevBuff, 0) && lastErr != ERROR_TIMEOUT) + return false; + + lastErr = ERROR_SUCCESS; + + if (buffOffs < FlushSize) + return true; + + DWORD writeSize = buffOffs - (buffOffs % BlockSize); + if (!beginWrite(writeSize)) + return false; + + bool isAsync = bytesToWrite[buffIdx] != INVALID_FILE_SIZE; + if (isAsync) + { + if (!waitForBuffer(prevBuff)) + return false; + + // Switch to next buffer + + memcpy(buffers[prevBuff], buff + writeSize, buffOffs - writeSize); + buffOffs -= writeSize; + + fileOffs += writeSize; + + buffIdx = prevBuff; + } + else + { + // Stick with same buffer + memmove(buff, buff + writeSize, buffOffs - writeSize); + + buffOffs -= writeSize; + } + + bytesToWrite[buffIdx] = INVALID_FILE_SIZE; + + return true; + } + +protected: + typedef BOOL (*SetFileInformationByHandlePtr)( + HANDLE hFile, + FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + LPVOID lpFileInformation, + DWORD dwBufferSize + ); + + SetFileInformationByHandlePtr SetFileInformationByHandle; + + DWORD lastErr; + + bool isOpen; + bool isPaused; + + std::string fileName; + HANDLE file; + + char *buffers[2]; + OVERLAPPED ovls[2]; + size_t bytesToWrite[2]; + + unsigned buffIdx; + size_t buffOffs; + uint64_t fileOffs; + + void initialize(bool isOpen = false, bool isPaused = false, const char *fileName = "", HANDLE file = INVALID_HANDLE_VALUE) + { + lastErr = ERROR_SUCCESS; + + this->isOpen = isOpen; + this->isPaused = isPaused; + + this->fileName = fileName; + this->file = file; + + for (unsigned i = 0; i < 2; i++) + { + buffers[i] = nullptr; + bytesToWrite[i] = INVALID_FILE_SIZE; + } + + memset(ovls, 0, sizeof(ovls)); + + buffIdx = 0; + buffOffs = 0; + fileOffs = 0; + } + + void cleanup() + { + isOpen = false; + + if (file != INVALID_HANDLE_VALUE) + { + CloseHandle(file); + file = INVALID_HANDLE_VALUE; + } + + for (unsigned i = 0; i < 2; i++) + { + if (buffers[i] != nullptr) + VirtualFree(buffers[i], 0, MEM_RELEASE); + if (ovls[i].hEvent != nullptr) + CloseHandle(ovls[i].hEvent); + + buffers[i] = nullptr; + ovls[i].hEvent = nullptr; + } + } + + bool beginWrite(size_t writeSize) + { + bytesToWrite[buffIdx] = INVALID_FILE_SIZE; + + if (writeSize % BlockSize != 0) + { + lastErr = ERROR_INTERNAL_ERROR; + return false; + } + + OVERLAPPED *ovl = &ovls[buffIdx]; + ovl->Offset = (DWORD)fileOffs; + ovl->OffsetHigh = DWORD(fileOffs >> 32); + + bool success = false; + DWORD bytesWritten; + if (WriteFile(file, buffers[buffIdx], writeSize, &bytesWritten, ovl)) + { + // Completed synchronously + lastErr = (bytesWritten == writeSize) + ? ERROR_SUCCESS + : ERROR_WRITE_FAULT; // "Close enough" + } + else + { + DWORD lastErr = GetLastError(); + if (lastErr == ERROR_IO_PENDING) + { + bytesToWrite[buffIdx] = writeSize; + lastErr = ERROR_SUCCESS; + } + } + + return !lastErr; + } + + bool writeTail() + { + char *buff = buffers[buffIdx]; + if (buffOffs != 0) + { + size_t writeSize = (buffOffs + BlockSize - 1) & ~(BlockSize - 1); + memset(buff + buffOffs, ' ', writeSize - buffOffs); + + if (!beginWrite(writeSize) + || !waitForBuffer(buffIdx)) + return false; + } + + if (SetFileInformationByHandle != nullptr) + { + FILE_END_OF_FILE_INFO eofInfo; + eofInfo.EndOfFile.QuadPart = int64_t(fileOffs + buffOffs); + + if (!SetFileInformationByHandle(file, FileEndOfFileInfo, &eofInfo, sizeof(eofInfo))) + { + lastErr = GetLastError(); + return false; + } + } + + return true; + } + + bool waitForBuffer(unsigned buffIdx, DWORD timeout = INFINITE) + { + if (buffIdx >= 2) + lastErr = ERROR_INTERNAL_ERROR; + else if (bytesToWrite[buffIdx] == INVALID_FILE_SIZE) + lastErr = ERROR_SUCCESS; + else + { + DWORD waitRes = WaitForSingleObject(ovls[buffIdx].hEvent, timeout); + if (waitRes == WAIT_TIMEOUT) + lastErr = ERROR_TIMEOUT; + else if (waitRes != WAIT_OBJECT_0) + { + lastErr = GetLastError(); + bytesToWrite[buffIdx] = INVALID_FILE_SIZE; + } + else + { + DWORD prevBytesWritten; + if (!GetOverlappedResult(file, &ovls[buffIdx], &prevBytesWritten, FALSE)) + lastErr = GetLastError(); + else if (prevBytesWritten != bytesToWrite[buffIdx]) + lastErr = ERROR_WRITE_FAULT; + else + lastErr = ERROR_SUCCESS; + + bytesToWrite[buffIdx] = INVALID_FILE_SIZE; + } + } + + return !lastErr; + } +}; \ No newline at end of file diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp index 95df40f2..a0720922 100644 --- a/src/drivers/Qt/TraceLogger.cpp +++ b/src/drivers/Qt/TraceLogger.cpp @@ -25,6 +25,7 @@ #ifdef WIN32 #include +#include #else #include #include @@ -945,7 +946,6 @@ static int convToXchar(int i) int traceRecord_t::convToText(char *txt, int *len) { int i = 0, j = 0; - char stmp[128]; char str_axystate[32], str_procstatus[32]; str_axystate[0] = 0; @@ -954,112 +954,56 @@ int traceRecord_t::convToText(char *txt, int *len) txt[0] = 0; if (opSize == 0) { - j = 0; - while (asmTxt[j] != 0) - { - txt[i] = asmTxt[j]; - i++; - j++; - } - txt[i] = 0; + strcpy(txt, asmTxt); return -1; } + StringBuilder sb(txt + i); if (skippedLines > 0) - { - sprintf(stmp, "(%d lines skipped) ", skippedLines); - - j = 0; - while (stmp[j] != 0) - { - txt[i] = stmp[j]; - i++; - j++; - } - } + (sb << '(').appendDec(skippedLines) << " lines skipped) "; // Start filling the str_temp line: Frame count, Cycles count, Instructions count, AXYS state, Processor status, Tabs, Address, Data, Disassembly if (logging_options & LOG_FRAMES_COUNT) - { - sprintf(stmp, "f%-6llu ", (long long unsigned int)frameCount); - - j = 0; - while (stmp[j] != 0) - { - txt[i] = stmp[j]; - i++; - j++; - } - } + (sb << 'f').appendDec(frameCount, -6); if (logging_options & LOG_CYCLES_COUNT) - { - sprintf(stmp, "c%-11llu ", (long long unsigned int)cycleCount); - - j = 0; - while (stmp[j] != 0) - { - txt[i] = stmp[j]; - i++; - j++; - } - } + (sb << 'c').appendDec(cycleCount, -11); if (logging_options & LOG_INSTRUCTIONS_COUNT) - { - sprintf(stmp, "i%-11llu ", (long long unsigned int)instrCount); - - j = 0; - while (stmp[j] != 0) - { - txt[i] = stmp[j]; - i++; - j++; - } - } + (sb << 'i').appendDec(instrCount, -11); if (logging_options & LOG_REGISTERS) { - sprintf(str_axystate, "A:%02X X:%02X Y:%02X S:%02X ", (cpu.A), (cpu.X), (cpu.Y), (cpu.S)); + StringBuilder sb(str_axystate); + (sb << "A:").appendHex(cpu.A, 2); + (sb << " X:").appendHex(cpu.X, 2); + (sb << " Y:").appendHex(cpu.Y, 2); + (sb << " S:").appendHex(cpu.S, 2); + sb << ' '; } if (logging_options & LOG_PROCESSOR_STATUS) { - int tmp = cpu.P ^ 0xFF; - sprintf(str_procstatus, "P:%c%c%c%c%c%c%c%c ", - 'N' | (tmp & 0x80) >> 2, - 'V' | (tmp & 0x40) >> 1, - 'U' | (tmp & 0x20), - 'B' | (tmp & 0x10) << 1, - 'D' | (tmp & 0x08) << 2, - 'I' | (tmp & 0x04) << 3, - 'Z' | (tmp & 0x02) << 4, - 'C' | (tmp & 0x01) << 5); + char *s = str_procstatus; + *(s++) = cpu.P & 0x80 ? 'N' : 'n'; + *(s++) = cpu.P & 0x40 ? 'V' : 'v'; + *(s++) = cpu.P & 0x20 ? 'U' : 'u'; + *(s++) = cpu.P & 0x10 ? 'B' : 'b'; + *(s++) = cpu.P & 0x08 ? 'D' : 'd'; + *(s++) = cpu.P & 0x04 ? 'I' : 'i'; + *(s++) = cpu.P & 0x02 ? 'Z' : 'z'; + *(s++) = cpu.P & 0x01 ? 'C' : 'c'; + *(s++) = ' '; + *(s++) = '\0'; } if (logging_options & LOG_TO_THE_LEFT) { if (logging_options & LOG_REGISTERS) - { - j = 0; - while (str_axystate[j] != 0) - { - txt[i] = str_axystate[j]; - i++; - j++; - } - } + sb << str_axystate; if (logging_options & LOG_PROCESSOR_STATUS) - { - j = 0; - while (str_procstatus[j] != 0) - { - txt[i] = str_procstatus[j]; - i++; - j++; - } - } + sb << str_procstatus; } if (logging_options & LOG_CODE_TABBING) @@ -1067,106 +1011,44 @@ int traceRecord_t::convToText(char *txt, int *len) // add spaces at the beginning of the line according to stack pointer int spaces = (0xFF - cpu.S) & LOG_TABS_MASK; - while (spaces > 0) - { - txt[i] = ' '; - i++; - spaces--; - } + for (; spaces > 0; spaces--) + sb << ' '; } else if (logging_options & LOG_TO_THE_LEFT) - { - txt[i] = ' '; - i++; - } + sb << ' '; if (logging_options & LOG_BANK_NUMBER) { if (cpu.PC >= 0x8000) - { - sprintf(stmp, "$%02X:%04X: ", bank, cpu.PC); - } + sb.appendAddr((uint8_t)bank, 2) << ':'; else - { - sprintf(stmp, " $%04X: ", cpu.PC); - } + sb << " $"; } else - { - sprintf(stmp, "$%04X: ", cpu.PC); - } - j = 0; - while (stmp[j] != 0) - { - txt[i] = stmp[j]; - i++; - j++; - } + sb << '$'; + + sb.appendHex(cpu.PC, 4) << ": "; for (j = 0; j < opSize; j++) - { - txt[i] = convToXchar((opCode[j] >> 4) & 0x0F); - i++; - txt[i] = convToXchar(opCode[j] & 0x0F); - i++; - txt[i] = ' '; - i++; - } - while (j < 3) - { - txt[i] = ' '; - i++; - txt[i] = ' '; - i++; - txt[i] = ' '; - i++; - j++; - } - j = 0; - while (asmTxt[j] != 0) - { - txt[i] = asmTxt[j]; - i++; - j++; - } - if (callAddr >= 0) - { - sprintf(stmp, " (from $%04X)", callAddr); + sb.appendHex(opCode[j], 2) << ' '; + for (; j < 3; j++) + sb << " "; - j = 0; - while (stmp[j] != 0) - { - txt[i] = stmp[j]; - i++; - j++; - } - } + sb << asmTxt; + + if (callAddr >= 0) + (sb << " (from ").appendAddr((uint16_t)callAddr) << ')'; if (!(logging_options & LOG_TO_THE_LEFT)) { if (logging_options & LOG_REGISTERS) - { - j = 0; - while (str_axystate[j] != 0) - { - txt[i] = str_axystate[j]; - i++; - j++; - } - } + sb << str_axystate; if (logging_options & LOG_PROCESSOR_STATUS) - { - j = 0; - while (str_procstatus[j] != 0) - { - txt[i] = str_procstatus[j]; - i++; - j++; - } - } + sb << str_procstatus; } - txt[i] = 0; + i = int(sb.str() + sb.size() - txt); + txt[i] = '\0'; if (len) { @@ -1249,12 +1131,15 @@ static void pushToLogBuffer(traceRecord_t &rec) break; } } + + bool overrun = nextHead == logBufTail; + logBufHead = nextHead; if ( overrunWarningArmed ) { // Don't spam with buffer overrun warning messages, // we will print once if this happens. - if (logBufHead == logBufTail) + if (overrun) { if ( traceLogWindow ) { @@ -2536,9 +2421,8 @@ TraceLogDiskThread_t::~TraceLogDiskThread_t(void) void TraceLogDiskThread_t::run(void) { char line[256]; - char buf[8192]; - int i,idx=0; - int blockSize = 4 * 1024; + unsigned idx = 0; + const unsigned blockSize = 4 * 1024; bool dataNeedsFlush = true; bool isPaused = false; @@ -2547,10 +2431,8 @@ void TraceLogDiskThread_t::run(void) setPriority( QThread::HighestPriority ); #ifdef WIN32 - logFile = CreateFileA( logFilePath.c_str(), GENERIC_WRITE, - 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL ); - - if ( logFile == INVALID_HANDLE_VALUE ) + TraceFileWriter tracer; + if (!tracer.open(logFilePath.c_str(), (bool)FCEUI_EmulationPaused())) { char stmp[1024]; sprintf( stmp, "Error: Failed to open log file for writing: %s", logFilePath.c_str() ); @@ -2558,6 +2440,11 @@ void TraceLogDiskThread_t::run(void) return; } #else + const unsigned bufSize = blockSize * 2; + const unsigned flushSize = blockSize; + char buf[bufSize]; + int i, idx=0; + logFile = open( logFilePath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ); @@ -2588,7 +2475,13 @@ void TraceLogDiskThread_t::run(void) while (logBufHead != logBufTail) { logBuf[logBufTail].convToText(line); + logBufTail = (logBufTail + 1) % logBufMax; + #ifdef WIN32 + bool success = tracer.writeLine(line); + + /// TODO: Do something on error + #else i=0; while ( line[i] != 0 ) { @@ -2596,24 +2489,23 @@ void TraceLogDiskThread_t::run(void) } buf[idx] = '\n'; idx++; - logBufTail = (logBufTail + 1) % logBufMax; - - if ( idx >= blockSize ) + if ( idx >= flushSize ) { - #ifdef WIN32 - DWORD bytesWritten; - WriteFile( logFile, buf, idx, &bytesWritten, NULL ); idx = 0; - #else if ( write( logFile, buf, idx ) < 0 ) { // HANDLE ERROR TODO } idx = 0; - #endif dataNeedsFlush = true; } + #endif } + #ifdef WIN32 + bool success = tracer.setPause(isPaused); + + /// TODO: Do something on error + #else if (isPaused) { // If paused, the user might be at a breakpoint or doing some @@ -2621,55 +2513,40 @@ void TraceLogDiskThread_t::run(void) // Only flush data when paused, to keep write efficiency up. if ( idx > 0 ) { - #ifdef WIN32 - DWORD bytesWritten; - WriteFile( logFile, buf, idx, &bytesWritten, NULL ); idx = 0; - #else if ( write( logFile, buf, idx ) < 0 ) { // HANDLE ERROR TODO } idx = 0; - #endif dataNeedsFlush = true; } if (dataNeedsFlush) { //printf("Flushing Trace Log Disk Buffers\n"); - #ifdef WIN32 - FlushFileBuffers( logFile ); - #else if ( fsync( logFile ) ) { printf("Trace Log fsync error\n"); } - #endif dataNeedsFlush = false; } } + #endif + SDL_Delay(1); } + #ifdef WIN32 + tracer.close(); + #else if ( idx > 0 ) { - #ifdef WIN32 - DWORD bytesWritten; - WriteFile( logFile, buf, idx, &bytesWritten, NULL ); idx = 0; - #else if ( write( logFile, buf, idx ) < 0 ) { // HANDLE ERROR TODO } idx = 0; - #endif } - #ifdef WIN32 - if ( logFile != INVALID_HANDLE_VALUE ) - { - CloseHandle( logFile ); logFile = INVALID_HANDLE_VALUE; - } - #else if ( logFile != -1 ) { close(logFile); logFile = -1; From 2b418e765ec8e201cba6ec075ea9bee266e300ed Mon Sep 17 00:00:00 2001 From: TheRealQuantam <54358053+TheRealQuantam@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:38:00 -0800 Subject: [PATCH 2/5] Finish up --- src/drivers/Qt/ConsoleUtilities.h | 109 +++++++++------ src/drivers/Qt/SymbolicDebug.cpp | 38 +++--- src/drivers/Qt/TraceFileWriter.h | 218 +++++++++++++++++++++++------- src/drivers/Qt/TraceLogger.cpp | 37 +++-- 4 files changed, 273 insertions(+), 129 deletions(-) diff --git a/src/drivers/Qt/ConsoleUtilities.h b/src/drivers/Qt/ConsoleUtilities.h index 93827117..d867ff47 100644 --- a/src/drivers/Qt/ConsoleUtilities.h +++ b/src/drivers/Qt/ConsoleUtilities.h @@ -95,10 +95,21 @@ QString fceuGetOpcodeToolTip( uint8_t *opcode, int size ); QDialog *fceuCustomToolTipShow( const QPoint &globalPos, QDialog *popup ); -// Faster replacement functions for sprintf +// Faster replacement functions for sprintf. It's uglier, but much faster. class StringBuilder { public: + // Helper struct. Do not use directly. + template + struct IntInfo + { + T x; + int minLen; + char leadChar; + bool upperCase; + Prefix prefix; + }; + inline StringBuilder(char *str) : start(str), end(str) { *end = '\0'; @@ -114,13 +125,7 @@ public: return size_t(end - start); } - template - inline StringBuilder &operator << (T value) - { - return append(value); - } - - inline StringBuilder &append(char ch) + inline StringBuilder &operator <<(char ch) { *(end++) = ch; *end = '\0'; @@ -128,7 +133,7 @@ public: return *this; } - inline StringBuilder &append(const char *src) + inline StringBuilder &operator <<(const char *src) { size_t len = strlen(src); memcpy(end, src, len + 1); @@ -138,7 +143,20 @@ public: return *this; } - template + template + inline StringBuilder &operator <<(const IntInfo intInfo) + { + *this << intInfo.prefix; + return appendInt(intInfo.x, intInfo.minLen, intInfo.leadChar, intInfo.upperCase); + } + +protected: + inline StringBuilder &operator <<(nullptr_t) + { + return *this; + } + + template StringBuilder &appendInt(T x, int minLen = 0, char leadChar = ' ', bool upperCase = false) { static_assert(Radix >= 2 && Radix <= 36, "Radix must be between 2 and 36"); @@ -195,38 +213,43 @@ public: return *this; } - template - inline StringBuilder &appendDec(T x, int minLen = 0, char leadChar = ' ') - { - appendInt<10>(x, minLen, leadChar); - - return *this; - } - - template - inline StringBuilder &appendHex(T x, int minLen = 0, bool upperCase = true, char leadChar = '0') - { - appendInt<16>(x, minLen, leadChar, upperCase); - - return *this; - } - - template - inline StringBuilder &appendAddr(T addr, int minLen = 4, bool upperCase = true) - { - *(end++) = '$'; - return appendHex(addr, minLen, upperCase); - } - - template - inline StringBuilder &appendLit(T x, int minLen = 2, bool upperCase = true) - { - *(end++) = '#'; - *(end++) = '$'; - return appendHex(x, minLen, upperCase); - } - -protected: char *start; char *end; -}; \ No newline at end of file +}; + +// Formatters for numbers + +// Generic integer with any radius +template +inline StringBuilder::IntInfo sb_int(T x, int minLen = 0, char leadChar = ' ', bool upperCase = false) +{ + return { x, minLen, leadChar, upperCase, nullptr }; +} + +// A decimal number +template +inline StringBuilder::IntInfo<10, T> sb_dec(T x, int minLen = 0, char leadChar = ' ') +{ + return { x, minLen, leadChar, false, nullptr }; +} + +// A hex number +template +inline StringBuilder::IntInfo<16, T> sb_hex(T x, int minLen = 0, bool upperCase = true, char leadChar = '0') +{ + return { x, minLen, leadChar, upperCase, nullptr }; +} + +// An address of the basic form $%x +template +inline StringBuilder::IntInfo<16, T, char> sb_addr(T x, int minLen = 4, bool upperCase = true) +{ + return { x, minLen, '0', upperCase, '$' }; +} + +// A literal value of the form #$%x +template +inline StringBuilder::IntInfo<16, T, const char *> sb_lit(T x, int minLen = 2, bool upperCase = true) +{ + return { x, minLen, '0', upperCase, "#$" }; +} diff --git a/src/drivers/Qt/SymbolicDebug.cpp b/src/drivers/Qt/SymbolicDebug.cpp index 070fb0ae..2d735d6f 100644 --- a/src/drivers/Qt/SymbolicDebug.cpp +++ b/src/drivers/Qt/SymbolicDebug.cpp @@ -62,7 +62,7 @@ debugSymbol_t *replaceSymbols( int flags, int addr, char *str ) if ( !sym || !( flags & ASM_DEBUG_REPLACE ) ) { - (sb << '$').appendHex( addr, flags & ASM_DEBUG_ADDR_02X ? 2 : 4 ); + sb << sb_addr( addr, flags & ASM_DEBUG_ADDR_02X ? 2 : 4 ); if ( sym ) sb << ' '; @@ -169,7 +169,7 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS if ( symDebugEnable ) sym = replaceSymbols( flags, tmp, stmp ); - (sb << chr << " (").appendAddr(opcode[1], 2) << ',' << indReg << ')'; + sb << chr << " (" << sb_addr(opcode[1], 2) << ',' << indReg << ')'; if (showTrace) { @@ -177,9 +177,9 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS if (symDebugEnable) sb << stmp; else - sb.appendAddr(tmp); + sb << sb_addr(tmp); - (sb << " = ").appendLit(GetMem(tmp)); + sb << " = " << sb_lit(GetMem(tmp)); } break; @@ -216,10 +216,10 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS sb << stmp; } else - sb.appendAddr(opcode[1], 2); + sb << sb_addr(opcode[1], 2); if (showTrace) - (sb << " = ").appendLit(GetMem(opcode[1])); + sb << " = " << sb_lit(GetMem(opcode[1])); // ################################## End of SP CODE ########################### break; @@ -238,7 +238,7 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS case 0xE0: chr = "CPX"; goto _immediate; case 0xE9: chr = "SBC"; goto _immediate; _immediate: - (sb << chr << ' ').appendLit(opcode[1]); + sb << chr << ' ' << sb_lit(opcode[1]); break; //Absolute @@ -273,10 +273,10 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS sb << stmp; } else - sb.appendAddr(tmp); + sb << sb_addr(tmp); if (showTrace) - (sb << " = ").appendLit(GetMem(tmp)); + sb << " = " << sb_lit(GetMem(tmp)); break; @@ -299,7 +299,7 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS sb << stmp; } else - sb.appendAddr(tmp); + sb << sb_addr(tmp); break; @@ -345,16 +345,16 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS if ( symDebugEnable ) sym = replaceSymbols( flags, tmp, stmp ); - (sb << chr << ' ').appendAddr(opcode[1], 2) << ',' << indReg; + sb << chr << ' ' << sb_addr(opcode[1], 2) << ',' << indReg; if (showTrace) { sb << " @ "; if (symDebugEnable) sb << stmp; else - sb.appendAddr(tmp); + sb << sb_addr(tmp); - (sb << " = ").appendLit(GetMem(tmp)); + sb << " = " << sb_lit(GetMem(tmp)); } // ################################## End of SP CODE ########################### break; @@ -383,7 +383,7 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS sb << stmp; } else - sb.appendAddr(tmp); + sb << sb_addr(tmp); sb << ',' << indReg; @@ -393,9 +393,9 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS if (symDebugEnable) sb << stmp2; else - sb.appendAddr(tmp2); + sb << sb_addr(tmp2); - (sb << " = ").appendLit(GetMem(tmp2)); + sb << " = " << sb_lit(GetMem(tmp2)); } break; @@ -436,15 +436,15 @@ int DisassembleWithDebug(int addr, uint8_t *opcode, int flags, char *str, debugS sb << stmp; } else - sb.appendAddr(tmp); + sb << sb_addr(tmp); break; case 0x6C: absolute(tmp); - (sb << "JMP (").appendAddr(tmp); - (sb << ") = ").appendAddr(GetMem(tmp) | GetMem(tmp + 1) << 8); + sb << "JMP (" << sb_addr(tmp); + sb << ") = " << sb_addr(GetMem(tmp) | GetMem(tmp + 1) << 8); break; diff --git a/src/drivers/Qt/TraceFileWriter.h b/src/drivers/Qt/TraceFileWriter.h index 91a86185..907baf42 100644 --- a/src/drivers/Qt/TraceFileWriter.h +++ b/src/drivers/Qt/TraceFileWriter.h @@ -4,6 +4,9 @@ #include #include +// High-performance class for writing a series of text lines to a file, using overlapped, unbuffered I/O +// Works on Windows builds both SDL/Qt and non-SQL/Qt +// Apart from getLastError, the entire API can be adapted to other OS with no client changes class TraceFileWriter { public: @@ -13,6 +16,7 @@ public: inline TraceFileWriter() { + // Windows Vista API that allows setting the end of file without closing and reopening it HMODULE kernel32 = GetModuleHandleA("kernel32"); SetFileInformationByHandle = kernel32 ? (SetFileInformationByHandlePtr)GetProcAddress(kernel32, "SetFileInformationByHandle") : nullptr; @@ -30,14 +34,16 @@ public: return isOpen; } + // SUPPOSED to always be valid (ERROR_SUCCESS if no error), but bugs may make it only valid after a failure inline DWORD getLastError() const { return lastErr; } + // Open the file and allocate all necessary resources bool open(const char *fileName, bool isPaused = false) { - HANDLE file = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + HANDLE file = CreateFileA(fileName, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, nullptr); if (file == INVALID_HANDLE_VALUE) { lastErr = GetLastError(); @@ -46,6 +52,7 @@ public: initialize(false, isPaused, fileName, file); + // Allocate resources do { for (unsigned i = 0; i < 2; i++) @@ -70,11 +77,13 @@ public: } while (false); if (!isOpen) + // Free resources on failure cleanup(); return isOpen; } + // Close the file and free resources void close() { if (!isOpen) @@ -91,16 +100,19 @@ public: cleanup(); } + // When going from unpaused to paused, flush file and set end of file so it can be accessed externally bool setPause(bool isPaused) { if (isPaused && !this->isPaused) { + // Wait for any outstanding writes to complete for (unsigned i = 0; i < 2; i++) { if (!waitForBuffer(i)) return false; } + // Write out anything still in the buffer if (!writeTail()) return false; } @@ -111,6 +123,8 @@ public: return true; } + // Add a line to the buffer and write it out when the buffer is filled + // Under most failure cirumstances the line is added to the buffer bool writeLine(const char *line) { if (!isOpen) @@ -119,20 +133,23 @@ public: return false; } + // Add to buffer + static const char eol[] = "\r\n"; + size_t eolSize = strlen(eol); char *buff = buffers[buffIdx]; - OVERLAPPED *ovl = &ovls[buffIdx]; - size_t lineLen = strlen(line); - if (buffOffs + lineLen + 1 > BuffSize) + if (buffOffs + lineLen + eolSize > BuffSize) { + // Buffer is full. This shouldn't ever happen. lastErr = ERROR_INTERNAL_ERROR; return false; } memcpy(buff + buffOffs, line, lineLen); - buffOffs += lineLen; - buff[buffOffs++] = '\n'; + memcpy(buff + buffOffs + lineLen, eol, eolSize); + buffOffs += lineLen + eolSize; + // Check if the previous write is done, to detect it as early as possible unsigned prevBuff = (buffIdx + 1) % 2; if (!waitForBuffer(prevBuff, 0) && lastErr != ERROR_TIMEOUT) return false; @@ -142,35 +159,45 @@ public: if (buffOffs < FlushSize) return true; - DWORD writeSize = buffOffs - (buffOffs % BlockSize); - if (!beginWrite(writeSize)) + return writeBlocks(); + } + + // Flush buffer contents. Writes partial blocks, but does NOT set end of file + // Do NOT call frequently, as writes may be significantly undersized and cause poor performance + bool flush() + { + if (!isOpen) + { + lastErr = ERROR_FILE_NOT_FOUND; + return false; + } + + // Write full blocks, if any + if (!writeBlocks()) return false; - bool isAsync = bytesToWrite[buffIdx] != INVALID_FILE_SIZE; - if (isAsync) + char *buff = buffers[buffIdx]; + if (buffOffs != 0) { - if (!waitForBuffer(prevBuff)) + // Write out partial block at the tail + size_t writeSize = (buffOffs + BlockSize - 1) & ~(BlockSize - 1); + memset(buff + buffOffs, ' ', writeSize - buffOffs); + + if (!beginWrite(writeSize) + || !waitForBuffer(buffIdx)) return false; - // Switch to next buffer - - memcpy(buffers[prevBuff], buff + writeSize, buffOffs - writeSize); - buffOffs -= writeSize; - - fileOffs += writeSize; - - buffIdx = prevBuff; + // Do NOT update buffIdx, buffOffs, or fileOffs, as the partial block must be overwritten later } - else + + // Wait for all writes to complete + for (unsigned i = 0; i < 2; i++) { - // Stick with same buffer - memmove(buff, buff + writeSize, buffOffs - writeSize); - - buffOffs -= writeSize; + if (!waitForBuffer(i)) + return false; } - bytesToWrite[buffIdx] = INVALID_FILE_SIZE; - + lastErr = ERROR_SUCCESS; return true; } @@ -184,6 +211,8 @@ protected: SetFileInformationByHandlePtr SetFileInformationByHandle; + const size_t NO_WRITE = size_t(-1); + DWORD lastErr; bool isOpen; @@ -192,14 +221,16 @@ protected: std::string fileName; HANDLE file; + // Double-buffers char *buffers[2]; OVERLAPPED ovls[2]; - size_t bytesToWrite[2]; + size_t bytesToWrite[2]; // Write in progress size or size_t(-1) if none unsigned buffIdx; size_t buffOffs; uint64_t fileOffs; + // Put the class into a defined state, but does NOT allocate resources void initialize(bool isOpen = false, bool isPaused = false, const char *fileName = "", HANDLE file = INVALID_HANDLE_VALUE) { lastErr = ERROR_SUCCESS; @@ -213,7 +244,7 @@ protected: for (unsigned i = 0; i < 2; i++) { buffers[i] = nullptr; - bytesToWrite[i] = INVALID_FILE_SIZE; + bytesToWrite[i] = NO_WRITE; } memset(ovls, 0, sizeof(ovls)); @@ -223,6 +254,7 @@ protected: fileOffs = 0; } + // Close file and release resources. Does NOT wait for writes to complete. void cleanup() { isOpen = false; @@ -245,9 +277,52 @@ protected: } } + // Write out as many blocks as present in the buffer + bool writeBlocks() + { + if (buffOffs < BlockSize) + return true; + + char *buff = buffers[buffIdx]; + unsigned prevBuff = (buffIdx + 1) % 2; + OVERLAPPED *ovl = &ovls[buffIdx]; + + DWORD writeSize = buffOffs - (buffOffs % BlockSize); + if (!beginWrite(writeSize)) + return false; + + bool isAsync = bytesToWrite[buffIdx] != NO_WRITE; + if (isAsync) + { + if (!waitForBuffer(prevBuff)) + return false; // Catastrophic failure case + + // Switch to next buffer + + memcpy(buffers[prevBuff], buff + writeSize, buffOffs - writeSize); + buffOffs -= writeSize; + + fileOffs += writeSize; + + buffIdx = prevBuff; + } + else + { + // Stick with same buffer + memmove(buff, buff + writeSize, buffOffs - writeSize); + + buffOffs -= writeSize; + } + + bytesToWrite[buffIdx] = NO_WRITE; + + return true; + } + + // Begin a write and handle errors without updating class state bool beginWrite(size_t writeSize) { - bytesToWrite[buffIdx] = INVALID_FILE_SIZE; + bytesToWrite[buffIdx] = NO_WRITE; if (writeSize % BlockSize != 0) { @@ -281,21 +356,12 @@ protected: return !lastErr; } - bool writeTail() + // Set the end of file so it can be accessed + bool setEndOfFile() { - char *buff = buffers[buffIdx]; - if (buffOffs != 0) - { - size_t writeSize = (buffOffs + BlockSize - 1) & ~(BlockSize - 1); - memset(buff + buffOffs, ' ', writeSize - buffOffs); - - if (!beginWrite(writeSize) - || !waitForBuffer(buffIdx)) - return false; - } - if (SetFileInformationByHandle != nullptr) { + // Easy case: Vista or better FILE_END_OF_FILE_INFO eofInfo; eofInfo.EndOfFile.QuadPart = int64_t(fileOffs + buffOffs); @@ -304,29 +370,89 @@ protected: lastErr = GetLastError(); return false; } - } - return true; + lastErr = ERROR_SUCCESS; + return true; + } + else + { + // Hard case: XP + // If set EOF fails, make a desperate attempt to reopen the file and keep running + lastErr = ERROR_SUCCESS; + do + { + // Set EOF to fileOffs rounded up to the next block + LARGE_INTEGER tgtOffs; + tgtOffs.QuadPart = (fileOffs + buffOffs + BlockSize - 1) & ~(BlockSize - 1); + LARGE_INTEGER newOffs; + + if (!SetFilePointerEx(file, tgtOffs, &newOffs, FILE_BEGIN) + || !SetEndOfFile(file)) + break; + + CloseHandle(file); + + // Open file with buffering so the exact byte size can be set + tgtOffs.QuadPart = fileOffs + buffOffs; + + file = CreateFile(fileName.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + if (file != INVALID_HANDLE_VALUE) + { + if (!SetFilePointerEx(file, tgtOffs, &newOffs, FILE_BEGIN) + || !SetEndOfFile(file)) + lastErr = GetLastError(); + + CloseHandle(file); + } + else + lastErr = GetLastError(); + + // Finally, reopen the file in original mode + file = CreateFile(fileName.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, nullptr); + if (file == INVALID_HANDLE_VALUE || lastErr) + break; + + lastErr = ERROR_SUCCESS; + return true; + } while (false); + + // Failed + if (!lastErr) + lastErr = GetLastError(); + + return false; + } } + // Write out everything in the buffer and set the file end for pausing + inline bool writeTail() + { + return flush() && setEndOfFile(); + } + + // Wait for an a buffer to become available, waiting for write completion if necessary bool waitForBuffer(unsigned buffIdx, DWORD timeout = INFINITE) { if (buffIdx >= 2) lastErr = ERROR_INTERNAL_ERROR; - else if (bytesToWrite[buffIdx] == INVALID_FILE_SIZE) + else if (bytesToWrite[buffIdx] == NO_WRITE) + // No write in progress lastErr = ERROR_SUCCESS; else { + // Wait for the operation to complete DWORD waitRes = WaitForSingleObject(ovls[buffIdx].hEvent, timeout); if (waitRes == WAIT_TIMEOUT) lastErr = ERROR_TIMEOUT; else if (waitRes != WAIT_OBJECT_0) { lastErr = GetLastError(); - bytesToWrite[buffIdx] = INVALID_FILE_SIZE; + bytesToWrite[buffIdx] = NO_WRITE; } else { + // Verify it succeeded DWORD prevBytesWritten; if (!GetOverlappedResult(file, &ovls[buffIdx], &prevBytesWritten, FALSE)) lastErr = GetLastError(); @@ -335,7 +461,7 @@ protected: else lastErr = ERROR_SUCCESS; - bytesToWrite[buffIdx] = INVALID_FILE_SIZE; + bytesToWrite[buffIdx] = NO_WRITE; } } diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp index a0720922..7455beac 100644 --- a/src/drivers/Qt/TraceLogger.cpp +++ b/src/drivers/Qt/TraceLogger.cpp @@ -915,15 +915,10 @@ traceRecord_t::traceRecord_t(void) //---------------------------------------------------- int traceRecord_t::appendAsmText(const char *txt) { - int i = 0; + size_t len = strlen(txt); + memcpy(asmTxt + asmTxtSize, txt, len + 1); - while (txt[i] != 0) - { - asmTxt[asmTxtSize] = txt[i]; - i++; - asmTxtSize++; - } - asmTxt[asmTxtSize] = 0; + asmTxtSize += len; return 0; } @@ -961,26 +956,26 @@ int traceRecord_t::convToText(char *txt, int *len) StringBuilder sb(txt + i); if (skippedLines > 0) - (sb << '(').appendDec(skippedLines) << " lines skipped) "; + sb << '(' << sb_dec(skippedLines) << " lines skipped) "; // Start filling the str_temp line: Frame count, Cycles count, Instructions count, AXYS state, Processor status, Tabs, Address, Data, Disassembly if (logging_options & LOG_FRAMES_COUNT) - (sb << 'f').appendDec(frameCount, -6); + sb << 'f' << sb_dec(frameCount, -6); if (logging_options & LOG_CYCLES_COUNT) - (sb << 'c').appendDec(cycleCount, -11); + sb << 'c' << sb_dec(cycleCount, -11); if (logging_options & LOG_INSTRUCTIONS_COUNT) - (sb << 'i').appendDec(instrCount, -11); + sb << 'i' << sb_dec(instrCount, -11); if (logging_options & LOG_REGISTERS) { StringBuilder sb(str_axystate); - (sb << "A:").appendHex(cpu.A, 2); - (sb << " X:").appendHex(cpu.X, 2); - (sb << " Y:").appendHex(cpu.Y, 2); - (sb << " S:").appendHex(cpu.S, 2); - sb << ' '; + sb << "A:" << sb_hex(cpu.A, 2) + << " X:" << sb_hex(cpu.X, 2) + << " Y:" << sb_hex(cpu.Y, 2) + << " S:" << sb_hex(cpu.S, 2) + << ' '; } if (logging_options & LOG_PROCESSOR_STATUS) @@ -1020,24 +1015,24 @@ int traceRecord_t::convToText(char *txt, int *len) if (logging_options & LOG_BANK_NUMBER) { if (cpu.PC >= 0x8000) - sb.appendAddr((uint8_t)bank, 2) << ':'; + sb << sb_addr((uint8_t)bank, 2) << ':'; else sb << " $"; } else sb << '$'; - sb.appendHex(cpu.PC, 4) << ": "; + sb << sb_hex(cpu.PC, 4) << ": "; for (j = 0; j < opSize; j++) - sb.appendHex(opCode[j], 2) << ' '; + sb << sb_hex(opCode[j], 2) << ' '; for (; j < 3; j++) sb << " "; sb << asmTxt; if (callAddr >= 0) - (sb << " (from ").appendAddr((uint16_t)callAddr) << ')'; + sb << " (from " << sb_addr((uint16_t)callAddr) << ')'; if (!(logging_options & LOG_TO_THE_LEFT)) { From f6493b1e30e40c39d8b1c083ba00d3cdf1ec773f Mon Sep 17 00:00:00 2001 From: TheRealQuantam <54358053+TheRealQuantam@users.noreply.github.com> Date: Wed, 21 Feb 2024 19:55:53 -0800 Subject: [PATCH 3/5] Move StringBuilder to its own header and fix Linux build failures --- src/drivers/Qt/ConsoleUtilities.h | 160 ----------------------------- src/drivers/Qt/SymbolicDebug.cpp | 1 + src/drivers/Qt/TraceLogger.cpp | 1 + src/utils/StringBuilder.h | 164 ++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 160 deletions(-) create mode 100644 src/utils/StringBuilder.h diff --git a/src/drivers/Qt/ConsoleUtilities.h b/src/drivers/Qt/ConsoleUtilities.h index d867ff47..bbcefc07 100644 --- a/src/drivers/Qt/ConsoleUtilities.h +++ b/src/drivers/Qt/ConsoleUtilities.h @@ -3,7 +3,6 @@ #pragma once #include -#include #include #include @@ -94,162 +93,3 @@ class QCheckBoxRO : public QCheckBox QString fceuGetOpcodeToolTip( uint8_t *opcode, int size ); QDialog *fceuCustomToolTipShow( const QPoint &globalPos, QDialog *popup ); - -// Faster replacement functions for sprintf. It's uglier, but much faster. -class StringBuilder -{ -public: - // Helper struct. Do not use directly. - template - struct IntInfo - { - T x; - int minLen; - char leadChar; - bool upperCase; - Prefix prefix; - }; - - inline StringBuilder(char *str) : start(str), end(str) - { - *end = '\0'; - } - - inline char *str() const - { - return start; - } - - inline size_t size() const - { - return size_t(end - start); - } - - inline StringBuilder &operator <<(char ch) - { - *(end++) = ch; - *end = '\0'; - - return *this; - } - - inline StringBuilder &operator <<(const char *src) - { - size_t len = strlen(src); - memcpy(end, src, len + 1); - - end += len; - - return *this; - } - - template - inline StringBuilder &operator <<(const IntInfo intInfo) - { - *this << intInfo.prefix; - return appendInt(intInfo.x, intInfo.minLen, intInfo.leadChar, intInfo.upperCase); - } - -protected: - inline StringBuilder &operator <<(nullptr_t) - { - return *this; - } - - template - StringBuilder &appendInt(T x, int minLen = 0, char leadChar = ' ', bool upperCase = false) - { - static_assert(Radix >= 2 && Radix <= 36, "Radix must be between 2 and 36"); - static_assert(std::is_integral::value, "T must be an integral type"); - - static const char *upperCaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", - *lowerCaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz"; - const char *digits = upperCase ? upperCaseDigits : lowerCaseDigits; - - const bool isSigned = std::is_signed::value; - bool isNeg = isSigned && x < 0; - if (isSigned && isNeg) - // This convoluted expression is to silence an unnecessary compiler warning when unsigned - x = T(-std::make_signed::type(x)); - - unsigned i = 0; - if (x != 0) - { - do - { - end[i++] = digits[x % Radix]; - x /= Radix; - } while (x != 0); - } - else - end[i++] = '0'; - - if (isNeg) - end[i++] = '-'; - - if (minLen < 0) - { - end[i] = '\0'; - - strrev(end); - - for (; i < (unsigned)-minLen; i++) - end[i] = leadChar; - - end[i] = '\0'; - } - else - { - for (; i < (unsigned)minLen; i++) - end[i] = leadChar; - - end[i] = '\0'; - - strrev(end); - } - - end += i; - - return *this; - } - - char *start; - char *end; -}; - -// Formatters for numbers - -// Generic integer with any radius -template -inline StringBuilder::IntInfo sb_int(T x, int minLen = 0, char leadChar = ' ', bool upperCase = false) -{ - return { x, minLen, leadChar, upperCase, nullptr }; -} - -// A decimal number -template -inline StringBuilder::IntInfo<10, T> sb_dec(T x, int minLen = 0, char leadChar = ' ') -{ - return { x, minLen, leadChar, false, nullptr }; -} - -// A hex number -template -inline StringBuilder::IntInfo<16, T> sb_hex(T x, int minLen = 0, bool upperCase = true, char leadChar = '0') -{ - return { x, minLen, leadChar, upperCase, nullptr }; -} - -// An address of the basic form $%x -template -inline StringBuilder::IntInfo<16, T, char> sb_addr(T x, int minLen = 4, bool upperCase = true) -{ - return { x, minLen, '0', upperCase, '$' }; -} - -// A literal value of the form #$%x -template -inline StringBuilder::IntInfo<16, T, const char *> sb_lit(T x, int minLen = 2, bool upperCase = true) -{ - return { x, minLen, '0', upperCase, "#$" }; -} diff --git a/src/drivers/Qt/SymbolicDebug.cpp b/src/drivers/Qt/SymbolicDebug.cpp index 2d735d6f..bbba95e3 100644 --- a/src/drivers/Qt/SymbolicDebug.cpp +++ b/src/drivers/Qt/SymbolicDebug.cpp @@ -31,6 +31,7 @@ #include "../../ines.h" #include "../../asm.h" #include "../../x6502.h" +#include "utils/StringBuilder.h" #include "Qt/fceuWrapper.h" #include "Qt/SymbolicDebug.h" diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp index 7455beac..8cad1663 100644 --- a/src/drivers/Qt/TraceLogger.cpp +++ b/src/drivers/Qt/TraceLogger.cpp @@ -57,6 +57,7 @@ #include "../../movie.h" #include "common/os_utils.h" +#include "utils/StringBuilder.h" #include "Qt/ConsoleDebugger.h" #include "Qt/ConsoleWindow.h" diff --git a/src/utils/StringBuilder.h b/src/utils/StringBuilder.h new file mode 100644 index 00000000..2c95e992 --- /dev/null +++ b/src/utils/StringBuilder.h @@ -0,0 +1,164 @@ +#pragma once + +#include +#include +#include + +// Faster replacement functions for sprintf. It's uglier, but much faster. +class StringBuilder +{ +public: + // Helper struct. Do not use directly. + template + struct IntInfo + { + T x; + int minLen; + char leadChar; + bool upperCase; + Prefix prefix; + }; + + inline StringBuilder(char *str) : start(str), end(str) + { + *end = '\0'; + } + + inline char *str() const + { + return start; + } + + inline size_t size() const + { + return size_t(end - start); + } + + inline StringBuilder &operator <<(char ch) + { + *(end++) = ch; + *end = '\0'; + + return *this; + } + + inline StringBuilder &operator <<(const char *src) + { + size_t len = std::strlen(src); + std::memcpy(end, src, len + 1); + + end += len; + + return *this; + } + + template + inline StringBuilder &operator <<(const IntInfo intInfo) + { + *this << intInfo.prefix; + return appendInt(intInfo.x, intInfo.minLen, intInfo.leadChar, intInfo.upperCase); + } + +protected: + inline StringBuilder &operator <<(nullptr_t) + { + return *this; + } + + template + StringBuilder &appendInt(T x, int minLen = 0, char leadChar = ' ', bool upperCase = false) + { + static_assert(Radix >= 2 && Radix <= 36, "Radix must be between 2 and 36"); + static_assert(std::is_integral::value, "T must be an integral type"); + + static const char *upperCaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + *lowerCaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz"; + const char *digits = upperCase ? upperCaseDigits : lowerCaseDigits; + + const bool isSigned = std::is_signed::value; + bool isNeg = isSigned && x < 0; + if (isSigned && isNeg) + // This convoluted expression is to silence an unnecessary compiler warning when unsigned + x = T(-((typename std::make_signed::type)(x))); + + unsigned i = 0; + if (x != 0) + { + do + { + end[i++] = digits[x % Radix]; + x /= Radix; + } while (x != 0); + } + else + end[i++] = '0'; + + if (isNeg) + end[i++] = '-'; + + if (minLen < 0) + { + end[i] = '\0'; + + std::reverse(end, end + i); + + for (; i < (unsigned)-minLen; i++) + end[i] = leadChar; + + end[i] = '\0'; + } + else + { + for (; i < (unsigned)minLen; i++) + end[i] = leadChar; + + end[i] = '\0'; + + std::reverse(end, end + i); + } + + end += i; + + return *this; + } + + char *start; + char *end; +}; + +// Formatters for numbers + +// Generic integer with any radius +template +inline StringBuilder::IntInfo sb_int(T x, int minLen = 0, char leadChar = ' ', bool upperCase = false) +{ + return { x, minLen, leadChar, upperCase, nullptr }; +} + +// A decimal number +template +inline StringBuilder::IntInfo<10, T> sb_dec(T x, int minLen = 0, char leadChar = ' ') +{ + return { x, minLen, leadChar, false, nullptr }; +} + +// A hex number +template +inline StringBuilder::IntInfo<16, T> sb_hex(T x, int minLen = 0, bool upperCase = true, char leadChar = '0') +{ + return { x, minLen, leadChar, upperCase, nullptr }; +} + +// An address of the basic form $%x +template +inline StringBuilder::IntInfo<16, T, char> sb_addr(T x, int minLen = 4, bool upperCase = true) +{ + return { x, minLen, '0', upperCase, '$' }; +} + +// A literal value of the form #$%x +template +inline StringBuilder::IntInfo<16, T, const char *> sb_lit(T x, int minLen = 2, bool upperCase = true) +{ + return { x, minLen, '0', upperCase, "#$" }; +} From c33ba818d263bde102fe23571cf444f9a2e367e0 Mon Sep 17 00:00:00 2001 From: TheRealQuantam <54358053+TheRealQuantam@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:20:02 -0800 Subject: [PATCH 4/5] Another Linux build fix --- src/drivers/Qt/TraceLogger.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp index 8cad1663..3711755d 100644 --- a/src/drivers/Qt/TraceLogger.cpp +++ b/src/drivers/Qt/TraceLogger.cpp @@ -2417,7 +2417,6 @@ TraceLogDiskThread_t::~TraceLogDiskThread_t(void) void TraceLogDiskThread_t::run(void) { char line[256]; - unsigned idx = 0; const unsigned blockSize = 4 * 1024; bool dataNeedsFlush = true; bool isPaused = false; @@ -2462,7 +2461,6 @@ void TraceLogDiskThread_t::run(void) logBuf = (traceRecord_t *)malloc(size); } - idx = 0; while ( !isInterruptionRequested() ) { From db92e1bc8be17c6541cee83c474aa0cc8e25bbc8 Mon Sep 17 00:00:00 2001 From: TheRealQuantam <54358053+TheRealQuantam@users.noreply.github.com> Date: Fri, 23 Feb 2024 01:38:05 -0800 Subject: [PATCH 5/5] Non-SDL/Qt Windows optimizations --- src/asm.cpp | 323 ++++++++++++---------- src/drivers/Qt/TraceLogger.cpp | 2 +- src/drivers/{Qt => win}/TraceFileWriter.h | 8 +- src/drivers/win/debuggersp.cpp | 53 +++- src/drivers/win/tracer.cpp | 127 ++++----- src/utils/StringBuilder.h | 51 ++++ 6 files changed, 331 insertions(+), 233 deletions(-) rename src/drivers/{Qt => win}/TraceFileWriter.h (97%) diff --git a/src/asm.cpp b/src/asm.cpp index 0aa2a61e..533bb0b1 100644 --- a/src/asm.cpp +++ b/src/asm.cpp @@ -3,6 +3,7 @@ #include "types.h" #include "utils/xstring.h" +#include "utils/StringBuilder.h" #include "debug.h" #include "asm.h" #include "x6502.h" @@ -256,8 +257,11 @@ int Assemble(unsigned char *output, int addr, char *str) { ///disassembles the opcodes in the buffer assuming the provided address. Uses GetMem() and 6502 current registers to query referenced values. returns a static string buffer. char *Disassemble(int addr, uint8 *opcode) { - static char str[64]={0},chr[5]={0}; + static char str[64]={0}; + const char *chr; + char indReg; uint16 tmp,tmp2; + StringBuilder sb(str); //these may be replaced later with passed-in values to make a lighter-weight disassembly mode that may not query the referenced values #define RX (X.X) @@ -286,7 +290,7 @@ char *Disassemble(int addr, uint8 *opcode) { #ifdef BRK_3BYTE_HACK case 0x00: - sprintf(str,"BRK %02X %02X", opcode[1], opcode[2]); + sb << "BRK " << sb_hex(opcode[1], 2) << ' ' << sb_hex(opcode[2], 2); break; #else case 0x00: strcpy(str,"BRK"); break; @@ -323,207 +327,228 @@ char *Disassemble(int addr, uint8 *opcode) { case 0xF8: strcpy(str,"SED"); break; //(Indirect,X) - case 0x01: strcpy(chr,"ORA"); goto _indirectx; - case 0x21: strcpy(chr,"AND"); goto _indirectx; - case 0x41: strcpy(chr,"EOR"); goto _indirectx; - case 0x61: strcpy(chr,"ADC"); goto _indirectx; - case 0x81: strcpy(chr,"STA"); goto _indirectx; - case 0xA1: strcpy(chr,"LDA"); goto _indirectx; - case 0xC1: strcpy(chr,"CMP"); goto _indirectx; - case 0xE1: strcpy(chr,"SBC"); goto _indirectx; + case 0x01: chr = "ORA"; goto _indirectx; + case 0x21: chr = "AND"; goto _indirectx; + case 0x41: chr = "EOR"; goto _indirectx; + case 0x61: chr = "ADC"; goto _indirectx; + case 0x81: chr = "STA"; goto _indirectx; + case 0xA1: chr = "LDA"; goto _indirectx; + case 0xC1: chr = "CMP"; goto _indirectx; + case 0xE1: chr = "SBC"; goto _indirectx; _indirectx: indirectX(tmp); - sprintf(str,"%s ($%02X,X) @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); + indReg = 'X'; + + _indirect: + sb << chr << " (" << sb_addr(opcode[1], 2) << ',' << indReg << ") @ " << sb_addr(tmp) << " = " << sb_lit(GetMem(tmp)); break; //Zero Page - case 0x05: strcpy(chr,"ORA"); goto _zeropage; - case 0x06: strcpy(chr,"ASL"); goto _zeropage; - case 0x24: strcpy(chr,"BIT"); goto _zeropage; - case 0x25: strcpy(chr,"AND"); goto _zeropage; - case 0x26: strcpy(chr,"ROL"); goto _zeropage; - case 0x45: strcpy(chr,"EOR"); goto _zeropage; - case 0x46: strcpy(chr,"LSR"); goto _zeropage; - case 0x65: strcpy(chr,"ADC"); goto _zeropage; - case 0x66: strcpy(chr,"ROR"); goto _zeropage; - case 0x84: strcpy(chr,"STY"); goto _zeropage; - case 0x85: strcpy(chr,"STA"); goto _zeropage; - case 0x86: strcpy(chr,"STX"); goto _zeropage; - case 0xA4: strcpy(chr,"LDY"); goto _zeropage; - case 0xA5: strcpy(chr,"LDA"); goto _zeropage; - case 0xA6: strcpy(chr,"LDX"); goto _zeropage; - case 0xC4: strcpy(chr,"CPY"); goto _zeropage; - case 0xC5: strcpy(chr,"CMP"); goto _zeropage; - case 0xC6: strcpy(chr,"DEC"); goto _zeropage; - case 0xE4: strcpy(chr,"CPX"); goto _zeropage; - case 0xE5: strcpy(chr,"SBC"); goto _zeropage; - case 0xE6: strcpy(chr,"INC"); goto _zeropage; + case 0x05: chr = "ORA"; goto _zeropage; + case 0x06: chr = "ASL"; goto _zeropage; + case 0x24: chr = "BIT"; goto _zeropage; + case 0x25: chr = "AND"; goto _zeropage; + case 0x26: chr = "ROL"; goto _zeropage; + case 0x45: chr = "EOR"; goto _zeropage; + case 0x46: chr = "LSR"; goto _zeropage; + case 0x65: chr = "ADC"; goto _zeropage; + case 0x66: chr = "ROR"; goto _zeropage; + case 0x84: chr = "STY"; goto _zeropage; + case 0x85: chr = "STA"; goto _zeropage; + case 0x86: chr = "STX"; goto _zeropage; + case 0xA4: chr = "LDY"; goto _zeropage; + case 0xA5: chr = "LDA"; goto _zeropage; + case 0xA6: chr = "LDX"; goto _zeropage; + case 0xC4: chr = "CPY"; goto _zeropage; + case 0xC5: chr = "CMP"; goto _zeropage; + case 0xC6: chr = "DEC"; goto _zeropage; + case 0xE4: chr = "CPX"; goto _zeropage; + case 0xE5: chr = "SBC"; goto _zeropage; + case 0xE6: chr = "INC"; goto _zeropage; _zeropage: // ################################## Start of SP CODE ########################### // Change width to %04X // don't! - sprintf(str,"%s $%02X = #$%02X", chr,opcode[1],GetMem(opcode[1])); + sb << chr << ' ' << sb_addr(opcode[1], 2) << " = " << sb_lit(GetMem(opcode[1])); // ################################## End of SP CODE ########################### break; //#Immediate - case 0x09: strcpy(chr,"ORA"); goto _immediate; - case 0x29: strcpy(chr,"AND"); goto _immediate; - case 0x49: strcpy(chr,"EOR"); goto _immediate; - case 0x69: strcpy(chr,"ADC"); goto _immediate; - //case 0x89: strcpy(chr,"STA"); goto _immediate; //baka, no STA #imm!! - case 0xA0: strcpy(chr,"LDY"); goto _immediate; - case 0xA2: strcpy(chr,"LDX"); goto _immediate; - case 0xA9: strcpy(chr,"LDA"); goto _immediate; - case 0xC0: strcpy(chr,"CPY"); goto _immediate; - case 0xC9: strcpy(chr,"CMP"); goto _immediate; - case 0xE0: strcpy(chr,"CPX"); goto _immediate; - case 0xE9: strcpy(chr,"SBC"); goto _immediate; + case 0x09: chr = "ORA"; goto _immediate; + case 0x29: chr = "AND"; goto _immediate; + case 0x49: chr = "EOR"; goto _immediate; + case 0x69: chr = "ADC"; goto _immediate; + //case 0x89: chr = "STA"; goto _immediate; //baka, no STA #imm!! + case 0xA0: chr = "LDY"; goto _immediate; + case 0xA2: chr = "LDX"; goto _immediate; + case 0xA9: chr = "LDA"; goto _immediate; + case 0xC0: chr = "CPY"; goto _immediate; + case 0xC9: chr = "CMP"; goto _immediate; + case 0xE0: chr = "CPX"; goto _immediate; + case 0xE9: chr = "SBC"; goto _immediate; _immediate: - sprintf(str,"%s #$%02X", chr,opcode[1]); + sb << chr << ' ' << sb_lit(opcode[1]); break; //Absolute - case 0x0D: strcpy(chr,"ORA"); goto _absolute; - case 0x0E: strcpy(chr,"ASL"); goto _absolute; - case 0x2C: strcpy(chr,"BIT"); goto _absolute; - case 0x2D: strcpy(chr,"AND"); goto _absolute; - case 0x2E: strcpy(chr,"ROL"); goto _absolute; - case 0x4D: strcpy(chr,"EOR"); goto _absolute; - case 0x4E: strcpy(chr,"LSR"); goto _absolute; - case 0x6D: strcpy(chr,"ADC"); goto _absolute; - case 0x6E: strcpy(chr,"ROR"); goto _absolute; - case 0x8C: strcpy(chr,"STY"); goto _absolute; - case 0x8D: strcpy(chr,"STA"); goto _absolute; - case 0x8E: strcpy(chr,"STX"); goto _absolute; - case 0xAC: strcpy(chr,"LDY"); goto _absolute; - case 0xAD: strcpy(chr,"LDA"); goto _absolute; - case 0xAE: strcpy(chr,"LDX"); goto _absolute; - case 0xCC: strcpy(chr,"CPY"); goto _absolute; - case 0xCD: strcpy(chr,"CMP"); goto _absolute; - case 0xCE: strcpy(chr,"DEC"); goto _absolute; - case 0xEC: strcpy(chr,"CPX"); goto _absolute; - case 0xED: strcpy(chr,"SBC"); goto _absolute; - case 0xEE: strcpy(chr,"INC"); goto _absolute; + case 0x0D: chr = "ORA"; goto _absolute; + case 0x0E: chr = "ASL"; goto _absolute; + case 0x2C: chr = "BIT"; goto _absolute; + case 0x2D: chr = "AND"; goto _absolute; + case 0x2E: chr = "ROL"; goto _absolute; + case 0x4D: chr = "EOR"; goto _absolute; + case 0x4E: chr = "LSR"; goto _absolute; + case 0x6D: chr = "ADC"; goto _absolute; + case 0x6E: chr = "ROR"; goto _absolute; + case 0x8C: chr = "STY"; goto _absolute; + case 0x8D: chr = "STA"; goto _absolute; + case 0x8E: chr = "STX"; goto _absolute; + case 0xAC: chr = "LDY"; goto _absolute; + case 0xAD: chr = "LDA"; goto _absolute; + case 0xAE: chr = "LDX"; goto _absolute; + case 0xCC: chr = "CPY"; goto _absolute; + case 0xCD: chr = "CMP"; goto _absolute; + case 0xCE: chr = "DEC"; goto _absolute; + case 0xEC: chr = "CPX"; goto _absolute; + case 0xED: chr = "SBC"; goto _absolute; + case 0xEE: chr = "INC"; goto _absolute; _absolute: absolute(tmp); - sprintf(str,"%s $%04X = #$%02X", chr,tmp,GetMem(tmp)); + + sb << chr << ' ' << sb_addr(tmp) << " = " << sb_lit(GetMem(tmp)); + break; //branches - case 0x10: strcpy(chr,"BPL"); goto _branch; - case 0x30: strcpy(chr,"BMI"); goto _branch; - case 0x50: strcpy(chr,"BVC"); goto _branch; - case 0x70: strcpy(chr,"BVS"); goto _branch; - case 0x90: strcpy(chr,"BCC"); goto _branch; - case 0xB0: strcpy(chr,"BCS"); goto _branch; - case 0xD0: strcpy(chr,"BNE"); goto _branch; - case 0xF0: strcpy(chr,"BEQ"); goto _branch; + case 0x10: chr = "BPL"; goto _branch; + case 0x30: chr = "BMI"; goto _branch; + case 0x50: chr = "BVC"; goto _branch; + case 0x70: chr = "BVS"; goto _branch; + case 0x90: chr = "BCC"; goto _branch; + case 0xB0: chr = "BCS"; goto _branch; + case 0xD0: chr = "BNE"; goto _branch; + case 0xF0: chr = "BEQ"; goto _branch; _branch: relative(tmp); - sprintf(str,"%s $%04X", chr,tmp); + + sb << chr << ' ' << sb_addr(tmp); + break; //(Indirect),Y - case 0x11: strcpy(chr,"ORA"); goto _indirecty; - case 0x31: strcpy(chr,"AND"); goto _indirecty; - case 0x51: strcpy(chr,"EOR"); goto _indirecty; - case 0x71: strcpy(chr,"ADC"); goto _indirecty; - case 0x91: strcpy(chr,"STA"); goto _indirecty; - case 0xB1: strcpy(chr,"LDA"); goto _indirecty; - case 0xD1: strcpy(chr,"CMP"); goto _indirecty; - case 0xF1: strcpy(chr,"SBC"); goto _indirecty; + case 0x11: chr = "ORA"; goto _indirecty; + case 0x31: chr = "AND"; goto _indirecty; + case 0x51: chr = "EOR"; goto _indirecty; + case 0x71: chr = "ADC"; goto _indirecty; + case 0x91: chr = "STA"; goto _indirecty; + case 0xB1: chr = "LDA"; goto _indirecty; + case 0xD1: chr = "CMP"; goto _indirecty; + case 0xF1: chr = "SBC"; goto _indirecty; _indirecty: indirectY(tmp); - sprintf(str,"%s ($%02X),Y @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); - break; + indReg = 'Y'; + + goto _indirect; //Zero Page,X - case 0x15: strcpy(chr,"ORA"); goto _zeropagex; - case 0x16: strcpy(chr,"ASL"); goto _zeropagex; - case 0x35: strcpy(chr,"AND"); goto _zeropagex; - case 0x36: strcpy(chr,"ROL"); goto _zeropagex; - case 0x55: strcpy(chr,"EOR"); goto _zeropagex; - case 0x56: strcpy(chr,"LSR"); goto _zeropagex; - case 0x75: strcpy(chr,"ADC"); goto _zeropagex; - case 0x76: strcpy(chr,"ROR"); goto _zeropagex; - case 0x94: strcpy(chr,"STY"); goto _zeropagex; - case 0x95: strcpy(chr,"STA"); goto _zeropagex; - case 0xB4: strcpy(chr,"LDY"); goto _zeropagex; - case 0xB5: strcpy(chr,"LDA"); goto _zeropagex; - case 0xD5: strcpy(chr,"CMP"); goto _zeropagex; - case 0xD6: strcpy(chr,"DEC"); goto _zeropagex; - case 0xF5: strcpy(chr,"SBC"); goto _zeropagex; - case 0xF6: strcpy(chr,"INC"); goto _zeropagex; + case 0x15: chr = "ORA"; goto _zeropagex; + case 0x16: chr = "ASL"; goto _zeropagex; + case 0x35: chr = "AND"; goto _zeropagex; + case 0x36: chr = "ROL"; goto _zeropagex; + case 0x55: chr = "EOR"; goto _zeropagex; + case 0x56: chr = "LSR"; goto _zeropagex; + case 0x75: chr = "ADC"; goto _zeropagex; + case 0x76: chr = "ROR"; goto _zeropagex; + case 0x94: chr = "STY"; goto _zeropagex; + case 0x95: chr = "STA"; goto _zeropagex; + case 0xB4: chr = "LDY"; goto _zeropagex; + case 0xB5: chr = "LDA"; goto _zeropagex; + case 0xD5: chr = "CMP"; goto _zeropagex; + case 0xD6: chr = "DEC"; goto _zeropagex; + case 0xF5: chr = "SBC"; goto _zeropagex; + case 0xF6: chr = "INC"; goto _zeropagex; _zeropagex: - zpIndex(tmp,RX); + zpIndex(tmp, RX); + indReg = 'X'; + + _indexed: // ################################## Start of SP CODE ########################### // Change width to %04X // don't! - sprintf(str,"%s $%02X,X @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); + sb << chr << ' ' << sb_addr(opcode[1], 2) << ',' << indReg << " @ " << sb_addr(tmp) << " = " << sb_lit(GetMem(tmp)); // ################################## End of SP CODE ########################### break; //Absolute,Y - case 0x19: strcpy(chr,"ORA"); goto _absolutey; - case 0x39: strcpy(chr,"AND"); goto _absolutey; - case 0x59: strcpy(chr,"EOR"); goto _absolutey; - case 0x79: strcpy(chr,"ADC"); goto _absolutey; - case 0x99: strcpy(chr,"STA"); goto _absolutey; - case 0xB9: strcpy(chr,"LDA"); goto _absolutey; - case 0xBE: strcpy(chr,"LDX"); goto _absolutey; - case 0xD9: strcpy(chr,"CMP"); goto _absolutey; - case 0xF9: strcpy(chr,"SBC"); goto _absolutey; + case 0x19: chr = "ORA"; goto _absolutey; + case 0x39: chr = "AND"; goto _absolutey; + case 0x59: chr = "EOR"; goto _absolutey; + case 0x79: chr = "ADC"; goto _absolutey; + case 0x99: chr = "STA"; goto _absolutey; + case 0xB9: chr = "LDA"; goto _absolutey; + case 0xBE: chr = "LDX"; goto _absolutey; + case 0xD9: chr = "CMP"; goto _absolutey; + case 0xF9: chr = "SBC"; goto _absolutey; _absolutey: absolute(tmp); - tmp2=(tmp+RY); - sprintf(str,"%s $%04X,Y @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)); + tmp2 = (tmp + RY); + indReg = 'Y'; + + _absindexed: + sb << chr << ' ' << sb_addr(tmp) << ',' << indReg << " @ " << sb_addr(tmp2) << " = " << sb_lit(GetMem(tmp2)); + break; //Absolute,X - case 0x1D: strcpy(chr,"ORA"); goto _absolutex; - case 0x1E: strcpy(chr,"ASL"); goto _absolutex; - case 0x3D: strcpy(chr,"AND"); goto _absolutex; - case 0x3E: strcpy(chr,"ROL"); goto _absolutex; - case 0x5D: strcpy(chr,"EOR"); goto _absolutex; - case 0x5E: strcpy(chr,"LSR"); goto _absolutex; - case 0x7D: strcpy(chr,"ADC"); goto _absolutex; - case 0x7E: strcpy(chr,"ROR"); goto _absolutex; - case 0x9D: strcpy(chr,"STA"); goto _absolutex; - case 0xBC: strcpy(chr,"LDY"); goto _absolutex; - case 0xBD: strcpy(chr,"LDA"); goto _absolutex; - case 0xDD: strcpy(chr,"CMP"); goto _absolutex; - case 0xDE: strcpy(chr,"DEC"); goto _absolutex; - case 0xFD: strcpy(chr,"SBC"); goto _absolutex; - case 0xFE: strcpy(chr,"INC"); goto _absolutex; + case 0x1D: chr = "ORA"; goto _absolutex; + case 0x1E: chr = "ASL"; goto _absolutex; + case 0x3D: chr = "AND"; goto _absolutex; + case 0x3E: chr = "ROL"; goto _absolutex; + case 0x5D: chr = "EOR"; goto _absolutex; + case 0x5E: chr = "LSR"; goto _absolutex; + case 0x7D: chr = "ADC"; goto _absolutex; + case 0x7E: chr = "ROR"; goto _absolutex; + case 0x9D: chr = "STA"; goto _absolutex; + case 0xBC: chr = "LDY"; goto _absolutex; + case 0xBD: chr = "LDA"; goto _absolutex; + case 0xDD: chr = "CMP"; goto _absolutex; + case 0xDE: chr = "DEC"; goto _absolutex; + case 0xFD: chr = "SBC"; goto _absolutex; + case 0xFE: chr = "INC"; goto _absolutex; _absolutex: absolute(tmp); - tmp2=(tmp+RX); - sprintf(str,"%s $%04X,X @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)); - break; + tmp2 = (tmp + RX); + indReg = 'X'; + + goto _absindexed; //jumps - case 0x20: strcpy(chr,"JSR"); goto _jump; - case 0x4C: strcpy(chr,"JMP"); goto _jump; - case 0x6C: absolute(tmp); sprintf(str,"JMP ($%04X) = $%04X", tmp,GetMem(tmp)|GetMem(tmp+1)<<8); break; + case 0x20: chr = "JSR"; goto _jump; + case 0x4C: chr = "JMP"; goto _jump; _jump: absolute(tmp); - sprintf(str,"%s $%04X", chr,tmp); + + sb << chr << ' ' << sb_addr(tmp); + + break; + + case 0x6C: + absolute(tmp); + + sb << "JMP (" << sb_addr(tmp); + sb << ") = " << sb_addr(GetMem(tmp) | GetMem(tmp + 1) << 8); + break; //Zero Page,Y - case 0x96: strcpy(chr,"STX"); goto _zeropagey; - case 0xB6: strcpy(chr,"LDX"); goto _zeropagey; + case 0x96: chr = "STX"; goto _zeropagey; + case 0xB6: chr = "LDX"; goto _zeropagey; _zeropagey: - zpIndex(tmp,RY); - // ################################## Start of SP CODE ########################### - // Change width to %04X // don't! - sprintf(str,"%s $%02X,Y @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); - // ################################## End of SP CODE ########################### - break; + zpIndex(tmp, RY); + indReg = 'Y'; + + goto _indexed; //UNDEFINED - default: strcpy(str,"ERROR"); break; - + default: strcpy(str, "ERROR"); break; } - return str; } diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp index 3711755d..bc5cee81 100644 --- a/src/drivers/Qt/TraceLogger.cpp +++ b/src/drivers/Qt/TraceLogger.cpp @@ -25,7 +25,7 @@ #ifdef WIN32 #include -#include +#include "../win/TraceFileWriter.h" #else #include #include diff --git a/src/drivers/Qt/TraceFileWriter.h b/src/drivers/win/TraceFileWriter.h similarity index 97% rename from src/drivers/Qt/TraceFileWriter.h rename to src/drivers/win/TraceFileWriter.h index 907baf42..983885f5 100644 --- a/src/drivers/Qt/TraceFileWriter.h +++ b/src/drivers/win/TraceFileWriter.h @@ -125,7 +125,7 @@ public: // Add a line to the buffer and write it out when the buffer is filled // Under most failure cirumstances the line is added to the buffer - bool writeLine(const char *line) + bool writeLine(const char *line, bool addEol = true) { if (!isOpen) { @@ -138,6 +138,7 @@ public: size_t eolSize = strlen(eol); char *buff = buffers[buffIdx]; size_t lineLen = strlen(line); + size_t copyLen = lineLen + (addEol ? eolSize : 0); if (buffOffs + lineLen + eolSize > BuffSize) { // Buffer is full. This shouldn't ever happen. @@ -146,7 +147,8 @@ public: } memcpy(buff + buffOffs, line, lineLen); - memcpy(buff + buffOffs + lineLen, eol, eolSize); + if (addEol) + memcpy(buff + buffOffs + lineLen, eol, eolSize); buffOffs += lineLen + eolSize; // Check if the previous write is done, to detect it as early as possible @@ -202,7 +204,7 @@ public: } protected: - typedef BOOL (*SetFileInformationByHandlePtr)( + typedef BOOL (WINAPI *SetFileInformationByHandlePtr)( HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, diff --git a/src/drivers/win/debuggersp.cpp b/src/drivers/win/debuggersp.cpp index d40b5d06..eecfe493 100644 --- a/src/drivers/win/debuggersp.cpp +++ b/src/drivers/win/debuggersp.cpp @@ -27,10 +27,12 @@ #include "../../debug.h" #include "../../debugsymboltable.h" #include "../../conddebug.h" +#include "utils/StringBuilder.h" #include #include #include +#include int GetNesFileAddress(int A); @@ -62,7 +64,7 @@ bool symbRegNames = true; int debuggerWasActive = 0; char temp_chr[40] = {0}; char delimiterChar[2] = "#"; - + INT_PTR CALLBACK nameDebuggerBookmarkCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); extern WNDPROC DefaultEditCtrlProc; @@ -103,8 +105,14 @@ MemoryMappedRegister RegNames[] = { {"$4017", "JOY2_FRAME"} }; +const char minRegFirstDigit = '2', maxRegFirstDigit = '4'; + int RegNameCount = sizeof(RegNames)/sizeof(MemoryMappedRegister); +auto cmpRegNames = [](const void *a, const void *b) { + return std::strncmp(((const MemoryMappedRegister *)a)->offset + 1, ((const MemoryMappedRegister *)b)->offset + 1, 4); +}; + /** * Parses a line from a NL file. * @param line The line to parse @@ -603,20 +611,39 @@ void replaceNames(Name* list, char* str, std::vector* addressesLog) list = list->next; } - for (int i = 0; i < RegNameCount; i++) { - if (!symbRegNames) break; - // copypaste, because Name* is too complex to abstract - *buff = 0; - src = str; + if (symbRegNames) { + StringBuilder sb(buff); - while (pos = strstr(src, RegNames[i].offset)) { - *pos = 0; - strcat(buff, src); - strcat(buff, RegNames[i].name); - src = pos + 5; + pos = src = str; + while (pos = std::strchr(pos, '$')) + { + char first = pos[1]; + if (first < minRegFirstDigit || first > maxRegFirstDigit) + { + pos += 1; + continue; + } + + MemoryMappedRegister key = { pos, "" }; + const MemoryMappedRegister *reg = (const MemoryMappedRegister *)std::bsearch(&key, RegNames, RegNameCount, sizeof(MemoryMappedRegister), cmpRegNames); + if (reg != nullptr) + { + *pos = '\0'; + sb << src << reg->name; + + pos += 5; + src = pos; + } + else + pos += 1; } - if (*buff) { - strcat(buff, src); + + // If no registers were found then the string is unmodified and no copies are necessary + if (src != str) + { + if (*src) + sb << src; + strcpy(str, buff); } } diff --git a/src/drivers/win/tracer.cpp b/src/drivers/win/tracer.cpp index 35f97eda..3ce622f1 100644 --- a/src/drivers/win/tracer.cpp +++ b/src/drivers/win/tracer.cpp @@ -32,8 +32,10 @@ #include "cdlogger.h" #include "tracer.h" #include "memview.h" +#include "TraceFileWriter.h" #include "main.h" //for GetRomName() #include "utils/xstring.h" +#include "utils/StringBuilder.h" //Used to determine the current hotkey mapping for the pause key in order to display on the dialog #include "mapinput.h" @@ -132,7 +134,7 @@ bool log_old_emu_paused = true; // thanks to this flag the window only updates extern bool JustFrameAdvanced; extern int currFrameCounter; -FILE *LOG_FP; +TraceFileWriter fileWriter; char trace_str[35000] = {0}; WNDPROC IDC_TRACER_LOG_oldWndProc = 0; @@ -686,14 +688,13 @@ void BeginLoggingSequence(void) { if(logfilename == NULL) ShowLogDirDialog(); if (!logfilename) return; - LOG_FP = fopen(logfilename,"w"); - if (LOG_FP == NULL) + if (!fileWriter.open(logfilename, false)) { sprintf(trace_str, "Error Opening File %s", logfilename); MessageBox(hTracer, trace_str, "File Error", MB_OK); return; } - fprintf(LOG_FP,FCEU_NAME_AND_VERSION" - Trace Log File\n"); //mbg merge 7/19/06 changed string + fileWriter.writeLine(FCEU_NAME_AND_VERSION" - Trace Log File"); } else { ClearTraceLogBuf(); @@ -734,8 +735,8 @@ void BeginLoggingSequence(void) void FCEUD_FlushTrace() { - if(LOG_FP) - fflush(LOG_FP); + if(fileWriter.getOpen()) + fileWriter.flush(); } //todo: really speed this up @@ -745,8 +746,8 @@ void FCEUD_TraceInstruction(uint8 *opcode, int size) return; unsigned int addr = X.PC; - uint8 tmp; static int unloggedlines; + StringBuilder dataSb(str_data), dasmSb(str_disassembly), resSb(str_result); // if instruction executed from the RAM, skip this, log all instead // TODO: loops folding mame-lyke style @@ -760,7 +761,7 @@ void FCEUD_TraceInstruction(uint8 *opcode, int size) olddatacount = datacount; if(unloggedlines > 0) { - sprintf(str_result, "(%d lines skipped)", unloggedlines); + resSb << '(' << sb_dec(unloggedlines) << " lines skipped)"; OutputLogLine(str_result); unloggedlines = 0; } @@ -778,20 +779,22 @@ void FCEUD_TraceInstruction(uint8 *opcode, int size) if ((addr + size) > 0xFFFF) { - sprintf(str_data, "%02X ", opcode[0]); - sprintf(str_disassembly, "OVERFLOW"); + dataSb << sb_hex(opcode[0], 2) << " "; + dasmSb << "OVERFLOW"; } else { char* a = 0; switch (size) { case 0: - sprintf(str_data, "%02X ", opcode[0]); - sprintf(str_disassembly,"UNDEFINED"); + dataSb << sb_hex(opcode[0], 2) << " "; + dasmSb << "UNDEFINED"; break; case 1: { - sprintf(str_data, "%02X ", opcode[0]); + StringBuilder decSb(str_decoration); + + dataSb << sb_hex(opcode[0], 2) << " "; a = Disassemble(addr + 1, opcode); // special case: an RTS opcode if (opcode[0] == 0x60) @@ -802,18 +805,18 @@ void FCEUD_TraceInstruction(uint8 *opcode, int size) { // this was a JSR instruction - take the subroutine address from it unsigned int call_addr = GetMem(caller_addr + 1) + (GetMem(caller_addr + 2) << 8); - sprintf(str_decoration, " (from $%04X)", call_addr); + decSb << " (from " << sb_addr(call_addr) << ')'; strcat(a, str_decoration); } } break; } case 2: - sprintf(str_data, "%02X %02X ", opcode[0],opcode[1]); + dataSb << sb_hex(opcode[0], 2) << ' ' << sb_hex(opcode[1], 2) << " "; a = Disassemble(addr + 2, opcode); break; case 3: - sprintf(str_data, "%02X %02X %02X ", opcode[0],opcode[1],opcode[2]); + dataSb << sb_hex(opcode[0], 2) << ' ' << sb_hex(opcode[1], 2) << ' ' << sb_hex(opcode[2], 2) << " "; a = Disassemble(addr + 3, opcode); break; } @@ -822,6 +825,8 @@ void FCEUD_TraceInstruction(uint8 *opcode, int size) { if (logging_options & LOG_SYMBOLIC) { + StringBuilder decSb(str_decoration); + loadNameFiles(); tempAddressesLog.resize(0); // Insert Name and Comment lines if needed @@ -830,24 +835,22 @@ void FCEUD_TraceInstruction(uint8 *opcode, int size) { if (node->name) { - strcpy(str_decoration, node->name); - strcat(str_decoration, ":"); + decSb << node->name << ':'; tempAddressesLog.push_back(addr); OutputLogLine(str_decoration, &tempAddressesLog); } if (node->comment) { // make a copy - strcpy(str_decoration_comment, node->comment); - strcat(str_decoration_comment, "\r\n"); + StringBuilder cmtSb(str_decoration_comment); + cmtSb << node->comment << "\r\n"; tracer_decoration_comment = str_decoration_comment; // divide the str_decoration_comment into strings (Comment1, Comment2, ...) char* tracer_decoration_comment_end_pos = strstr(tracer_decoration_comment, "\r\n"); while (tracer_decoration_comment_end_pos) { tracer_decoration_comment_end_pos[0] = 0; // set \0 instead of \r - strcpy(str_decoration, "; "); - strcat(str_decoration, tracer_decoration_comment); + decSb << "; " << tracer_decoration_comment; OutputLogLine(str_decoration, &tempAddressesLog); tracer_decoration_comment_end_pos += 2; tracer_decoration_comment = tracer_decoration_comment_end_pos; @@ -860,8 +863,7 @@ void FCEUD_TraceInstruction(uint8 *opcode, int size) for(int i=0;i>2, - 'V'|(tmp&0x40)>>1, - 'U'|(tmp&0x20), - 'B'|(tmp&0x10)<<1, - 'D'|(tmp&0x08)<<2, - 'I'|(tmp&0x04)<<3, - 'Z'|(tmp&0x02)<<4, - 'C'|(tmp&0x01)<<5 - ); + char *s = str_procstatus; + *(s++) = X.P & 0x80 ? 'N' : 'n'; + *(s++) = X.P & 0x40 ? 'V' : 'v'; + *(s++) = X.P & 0x20 ? 'U' : 'u'; + *(s++) = X.P & 0x10 ? 'B' : 'b'; + *(s++) = X.P & 0x08 ? 'D' : 'd'; + *(s++) = X.P & 0x04 ? 'I' : 'i'; + *(s++) = X.P & 0x02 ? 'Z' : 'z'; + *(s++) = X.P & 0x01 ? 'C' : 'c'; + *(s++) = ' '; + *(s++) = '\0'; } if (logging_options & LOG_TO_THE_LEFT) { if (logging_options & LOG_REGISTERS) - strcat(str_result, str_axystate); + resSb << str_axystate; if (logging_options & LOG_PROCESSOR_STATUS) - strcat(str_result, str_procstatus); + resSb << str_procstatus; } if (logging_options & LOG_CODE_TABBING) { // add spaces at the beginning of the line according to stack pointer int spaces = (0xFF - X.S) & LOG_TABS_MASK; - for (int i = 0; i < spaces; i++) - str_tabs[i] = ' '; + std::memset(str_tabs, ' ', spaces); str_tabs[spaces] = 0; - strcat(str_result, str_tabs); + resSb << str_tabs; } else if (logging_options & LOG_TO_THE_LEFT) { - strcat(str_result, " "); + resSb << ' '; } if (logging_options & LOG_BANK_NUMBER) { if (addr >= 0x8000) - sprintf(str_address, "$%02X:%04X: ", getBank(addr), addr); + resSb << sb_addr(getBank(addr), 2) << ':' << sb_hex(addr, 4); else - sprintf(str_address, " $%04X: ", addr); + resSb << " " << sb_addr(addr); } else { - sprintf(str_address, "$%04X: ", addr); + resSb << sb_addr(addr); } - - strcat(str_result, str_address); - strcat(str_result, str_data); - strcat(str_result, str_disassembly); + + resSb << ": " << str_data; + resSb << str_disassembly; if (!(logging_options & LOG_TO_THE_LEFT)) { if (logging_options & LOG_REGISTERS) - strcat(str_result, str_axystate); + resSb << str_axystate; if (logging_options & LOG_PROCESSOR_STATUS) - strcat(str_result, str_procstatus); + resSb << str_procstatus; } OutputLogLine(str_result, &tempAddressesLog); @@ -978,9 +973,7 @@ void OutputLogLine(const char *str, std::vector* addressesLog, bool add_ { if (logtofile) { - fputs(str, LOG_FP); - if (add_newline) - fputs("\n", LOG_FP); + fileWriter.writeLine(str, add_newline); } else { if (add_newline) @@ -1025,7 +1018,7 @@ void EndLoggingSequence() { if (logtofile) { - fclose(LOG_FP); + fileWriter.close(); } else { strcpy(str_result, "Logging finished."); diff --git a/src/utils/StringBuilder.h b/src/utils/StringBuilder.h index 2c95e992..12fac0d0 100644 --- a/src/utils/StringBuilder.h +++ b/src/utils/StringBuilder.h @@ -59,6 +59,57 @@ public: return appendInt(intInfo.x, intInfo.minLen, intInfo.leadChar, intInfo.upperCase); } + // Append an array of characters that may or may not be null-terminated + StringBuilder &appendSubstr(const char *str, size_t maxLen) + { + size_t len = std::strlen(str), + copyLen = len <= maxLen ? len : maxLen; + + std::memcpy(end, str, copyLen); + end += len; + *end = '\0'; + + return *this; + } + + // Substitute for strncpy/strncat + // maxLen is the TOTAL size, including what's already built + // If alwaysAppendNull the last character will always be null + StringBuilder &appendStr(const char *str, size_t maxLen, bool alwaysAddNull = true) + { + size_t len = std::strlen(str), + curLen = size(), + lenLeft = maxLen - curLen; + + if (len < lenLeft) + { + std::memcpy(end, str, len + 1); + end += len; + } + else if (!alwaysAddNull) + { + // strncpy/strncat case: no terminating null + std::memcpy(end, str, lenLeft); + end += lenLeft; + } + else + { + // strncpy_s/strncat_s case: always null-terminate + if (lenLeft != 0) + { + size_t copyLen = lenLeft - 1; + std::memcpy(end, str, copyLen); + + end += copyLen; + *end = '\0'; + } + else + end[-1] = '\0'; + } + + return *this; + } + protected: inline StringBuilder &operator <<(nullptr_t) {