diff --git a/src/sim65.vcxproj b/src/sim65.vcxproj index 07a9f7fb5..f1fe6bdd2 100644 --- a/src/sim65.vcxproj +++ b/src/sim65.vcxproj @@ -87,6 +87,7 @@ + @@ -95,6 +96,7 @@ + diff --git a/src/sim65/6502.c b/src/sim65/6502.c index be5afc036..a9aa299ff 100644 --- a/src/sim65/6502.c +++ b/src/sim65/6502.c @@ -42,14 +42,16 @@ * the WAI ($CB) and STP ($DB) instructions are unsupported. */ -#include #include +#include #include "memory.h" #include "peripherals.h" #include "error.h" -#include "6502.h" #include "paravirt.h" +#include "trace.h" + +#include "6502.h" /* @@ -4485,7 +4487,7 @@ static const OPFunc OP65C02Table[256] = { OPC_6502_41, OPC_65C02_NOP22, // $42 OPC_65C02_NOP11, // $43 - OPC_6502X_44, // $44 + OPC_6502X_44, // $44 OPC_6502_45, OPC_6502_46, OPC_65C02_47, @@ -4730,6 +4732,10 @@ unsigned ExecuteInsn (void) /* If we have an NMI request, handle it */ if (HaveNMIRequest) { + if (TraceMode != TRACE_DISABLED) { + PrintTraceNMI (); + } + HaveNMIRequest = false; Peripherals.Counter.NmiEvents += 1; @@ -4746,6 +4752,10 @@ unsigned ExecuteInsn (void) } else if (HaveIRQRequest && GET_IF () == 0) { + if (TraceMode != TRACE_DISABLED) { + PrintTraceIRQ (); + } + HaveIRQRequest = false; Peripherals.Counter.IrqEvents += 1; @@ -4765,11 +4775,16 @@ unsigned ExecuteInsn (void) /* Normal instruction - read the next opcode */ uint8_t OPC = MemReadByte (Regs.PC); - /* Execute it */ - Handlers[CPU][OPC] (); + /* Print a trace line, if trace mode is enabled. */ + if (TraceMode != TRACE_DISABLED) { + PrintTraceInstruction (); + } - /* Increment the instruction counter by one.NMIs and IRQs are counted separately. */ + /* Increment the instruction counter by one. */ Peripherals.Counter.CpuInstructions += 1; + + /* Execute the instruction. The handler sets the 'Cycles' variable. */ + Handlers[CPU][OPC] (); } /* Increment the 64-bit clock cycle counter with the cycle count for the instruction that we just executed. */ diff --git a/src/sim65/6502.h b/src/sim65/6502.h index 0f4d066d0..d715f3288 100644 --- a/src/sim65/6502.h +++ b/src/sim65/6502.h @@ -48,9 +48,9 @@ /* Supported CPUs */ typedef enum CPUType { - CPU_6502, - CPU_65C02, - CPU_6502X + CPU_6502 = 0, + CPU_65C02 = 1, + CPU_6502X = 2 } CPUType; /* Current CPU */ diff --git a/src/sim65/main.c b/src/sim65/main.c index 8b41fcc0f..828ea498e 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -35,6 +35,7 @@ #include #include +#include #include /* common */ @@ -49,6 +50,7 @@ #include "memory.h" #include "peripherals.h" #include "paravirt.h" +#include "trace.h" @@ -61,6 +63,9 @@ /* Name of program file */ const char* ProgramFile; +/* Set to True if CPU mode override is in effect. If set, the CPU is not read from the program file. */ +static bool CPUOverrideActive = false; + /* exit simulator after MaxCycles Cccles */ unsigned long long MaxCycles = 0; @@ -95,6 +100,8 @@ static void Usage (void) "Long options:\n" " --help\t\tHelp (this text)\n" " --cycles\t\tPrint amount of executed CPU cycles\n" + " --cpu \t\tOverride CPU type (6502, 65C02, 6502X)\n" + " --trace\t\tEnable CPU trace\n" " --verbose\t\tIncrease verbosity\n" " --version\t\tPrint the simulator version number\n", ProgName); @@ -112,6 +119,35 @@ static void OptHelp (const char* Opt attribute ((unused)), +static void OptCPU (const char* Opt, const char* Arg) +/* Set CPU type */ +{ + /* Don't use FindCPU here. Enum constants would clash. */ + if (strcmp(Arg, "6502") == 0) { + CPU = CPU_6502; + CPUOverrideActive = true; + } else if (strcmp(Arg, "65C02") == 0 || strcmp(Arg, "65c02") == 0) { + CPU = CPU_65C02; + CPUOverrideActive = true; + } else if (strcmp(Arg, "6502X") == 0 || strcmp(Arg, "6502x") == 0) { + CPU = CPU_6502X; + CPUOverrideActive = true; + } else { + AbEnd ("Invalid argument for %s: '%s'", Opt, Arg); + } +} + + + +static void OptTrace (const char* Opt attribute ((unused)), + const char* Arg attribute ((unused))) +/* Enable trace mode */ +{ + TraceMode = TRACE_ENABLE_FULL; /* Enable full trace mode. */ +} + + + static void OptVerbose (const char* Opt attribute ((unused)), const char* Arg attribute ((unused))) /* Increase verbosity */ @@ -135,16 +171,20 @@ static void OptVersion (const char* Opt attribute ((unused)), /* Print the simulator version */ { fprintf (stderr, "%s V%s\n", ProgName, GetVersionAsString ()); - exit(EXIT_SUCCESS); + exit (EXIT_SUCCESS); } + + static void OptQuitXIns (const char* Opt attribute ((unused)), - const char* Arg attribute ((unused))) -/* quit after MaxCycles cycles */ + const char* Arg) +/* Quit after MaxCycles cycles */ { MaxCycles = strtoull(Arg, NULL, 0); } + + static unsigned char ReadProgramFile (void) /* Load program into memory */ { @@ -173,17 +213,20 @@ static unsigned char ReadProgramFile (void) Error ("'%s': Invalid header version.", ProgramFile); } - /* Get the CPU type from the file header */ + /* Get the CPU type from the file header. + * Use it to set the CPU type, unless CPUOverrideActive is set. + */ if ((Val = fgetc(F)) != EOF) { - switch (Val) { - case CPU_6502: - case CPU_65C02: - case CPU_6502X: - CPU = Val; - break; - - default: - Error ("'%s': Invalid CPU type", ProgramFile); + if (!CPUOverrideActive) { + switch (Val) { + case CPU_6502: + case CPU_65C02: + case CPU_6502X: + CPU = Val; + break; + default: + Error ("'%s': Invalid CPU type", ProgramFile); + } } } @@ -238,16 +281,22 @@ int main (int argc, char* argv[]) { /* Program long options */ static const LongOpt OptTab[] = { - { "--help", 0, OptHelp }, - { "--cycles", 0, OptCycles }, - { "--verbose", 0, OptVerbose }, - { "--version", 0, OptVersion }, + { "--help", 0, OptHelp }, + { "--cycles", 0, OptCycles }, + { "--cpu", 1, OptCPU }, + { "--trace", 0, OptTrace }, + { "--verbose", 0, OptVerbose }, + { "--version", 0, OptVersion }, }; unsigned I; unsigned char SPAddr; unsigned int Cycles; + /* Set reasonable defaults. */ + CPU = CPU_6502; + TraceMode = TRACE_DISABLED; /* Disabled by default */ + /* Initialize the cmdline module */ InitCmdLine (&argc, &argv, "sim65"); @@ -302,16 +351,29 @@ int main (int argc, char* argv[]) } /* Do we have a program file? */ - if (ProgramFile == 0) { + if (ProgramFile == NULL) { AbEnd ("No program file"); } + /* Reset memory */ MemInit (); + + /* Reset peripherals. */ PeripheralsInit (); + /* Read program file into memory. + * This also sets the CPU type, unless a CPU override is in effect. + */ SPAddr = ReadProgramFile (); + + /* Initialize the paravirtualization subsystem. It requires the stack pointer address, to be able to + * simulate 6502 subroutine calls. + */ + + TraceInit(SPAddr); ParaVirtInit (I, SPAddr); + /* Reset the CPU */ Reset (); RemainCycles = MaxCycles; diff --git a/src/sim65/paravirt.h b/src/sim65/paravirt.h index f3281705e..f5d74acc8 100644 --- a/src/sim65/paravirt.h +++ b/src/sim65/paravirt.h @@ -32,11 +32,12 @@ /*****************************************************************************/ - #ifndef PARAVIRT_H #define PARAVIRT_H +#include "6502.h" + /*****************************************************************************/ /* Data */ diff --git a/src/sim65/peripherals.c b/src/sim65/peripherals.c index 4f105d87a..1d7ff6bdb 100644 --- a/src/sim65/peripherals.c +++ b/src/sim65/peripherals.c @@ -37,6 +37,8 @@ #include "peripherals.h" +#include "trace.h" +#include "6502.h" /*****************************************************************************/ @@ -146,6 +148,20 @@ void PeripheralsWriteByte (uint8_t Addr, uint8_t Val) break; } + /* Handle writes to the SimControl peripheral. */ + + case PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_CPUMODE: { + if (Val == CPU_6502 || Val == CPU_65C02 || Val == CPU_6502X) { + CPU = Val; + } + break; + } + + case PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_TRACEMODE: { + TraceMode = Val; + break; + } + /* Handle writes to unused and read-only peripheral addresses. */ default: { @@ -192,6 +208,16 @@ uint8_t PeripheralsReadByte (uint8_t Addr) return (uint8_t)(Value >> (SelectedByteIndex * 8)); } + /* Handle reads from the SimControl peripheral. */ + + case PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_CPUMODE: { + return CPU; + } + + case PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_TRACEMODE: { + return TraceMode; + } + /* Handle reads from unused peripheral and write-only addresses. */ default: { diff --git a/src/sim65/peripherals.h b/src/sim65/peripherals.h index 16ea83782..dc86e6c6e 100644 --- a/src/sim65/peripherals.h +++ b/src/sim65/peripherals.h @@ -38,9 +38,9 @@ /* The memory range where the memory-mapped peripherals can be accessed. */ #define PERIPHERALS_APERTURE_BASE_ADDRESS 0xffc0 -#define PERIPHERALS_APERTURE_LAST_ADDRESS 0xffc9 +#define PERIPHERALS_APERTURE_LAST_ADDRESS 0xffcb -/* Declarations for the COUNTER peripheral (currently the only peripheral). */ +/* Declarations for the COUNTER peripheral */ #define PERIPHERALS_COUNTER_ADDRESS_OFFSET_LATCH 0x00 #define PERIPHERALS_COUNTER_ADDRESS_OFFSET_SELECT 0x01 @@ -84,13 +84,18 @@ typedef struct { uint8_t LatchedValueSelected; } CounterPeripheral; +/* Declarations for the SIMCONTROL peripheral. */ +#define PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_CPUMODE 0x0A +#define PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_TRACEMODE 0x0B + +#define PERIPHERALS_SIMCONTROL_CPUMODE (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_CPUMODE) +#define PERIPHERALS_SIMCONTROL_TRACEMODE (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_SIMCONTROL_ADDRESS_OFFSET_TRACEMODE) /* Declare the 'Sim65Peripherals' type and its single instance 'Peripherals'. */ typedef struct { - /* State of the peripherals available in sim65. - * Currently, there is only one peripheral: the Counter. */ + /* State of the peripherals available in sim65. */ CounterPeripheral Counter; } Sim65Peripherals; diff --git a/src/sim65/trace.c b/src/sim65/trace.c new file mode 100644 index 000000000..ef1edd0bf --- /dev/null +++ b/src/sim65/trace.c @@ -0,0 +1,1157 @@ +/*****************************************************************************/ +/* */ +/* trace.c */ +/* */ +/* Instruction tracing functionality sim65 6502 simulator */ +/* */ +/* */ +/* */ +/* (C) 2025, Sidney Cadot */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + +#include +#include +#include +#include + +#include "6502.h" +#include "memory.h" +#include "trace.h" +#include "peripherals.h" + +/* Current Trace Mode. Tracing is off by default, and needs to be explicitly enabled. */ +uint8_t TraceMode = TRACE_DISABLED; + +/* CC65 stack pointer */ +uint8_t StackPointerZPageAddress; + +/* 6502, 65C02 addressing modes. */ +typedef enum { + ILLEGAL, + IMPLIED, + ACCUMULATOR, + IMMEDIATE, + REL, + ZP, + ZP_X, + ZP_Y, + ZP_IND, + ZP_X_IND, + ZP_IND_Y, + ZP_REL, + ABS, + ABS_X, + ABS_Y, + ABS_IND, + ABS_X_IND +} AddressingMode; + +/* Info for a specific opcode and addressing mode, for a specific CPU type. */ +typedef struct { + const char * mnemonic; + AddressingMode adrmode; +} InstructionInfo; + +/* Information for standard 6502 opcodes. */ +static InstructionInfo II_6502[256] = { + { "brk" , IMPLIED }, /* 0x00 to 0x0f */ + { "ora" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ora" , ZP }, + { "asl" , ZP }, + { "???" , ILLEGAL }, + { "php" , IMPLIED }, + { "ora" , IMMEDIATE }, + { "asl" , ACCUMULATOR }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ora" , ABS }, + { "asl" , ABS }, + { "???" , ILLEGAL }, + + { "bpl" , REL }, /* 0x10 to 0x1f */ + { "ora" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ora" , ZP_X }, + { "asl" , ZP_X }, + { "???" , ILLEGAL }, + { "clc" , IMPLIED }, + { "ora" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ora" , ABS_X }, + { "asl" , ABS_X }, + { "???" , ILLEGAL }, + + { "jsr" , ABS }, /* 0x20 to 0x2f */ + { "and" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "bit" , ZP }, + { "and" , ZP }, + { "rol" , ZP }, + { "???" , ILLEGAL }, + { "plp" , IMPLIED }, + { "and" , IMMEDIATE }, + { "rol" , ACCUMULATOR }, + { "???" , ILLEGAL }, + { "bit" , ABS }, + { "and" , ABS }, + { "rol" , ABS }, + { "???" , ILLEGAL }, + + { "bmi" , REL }, /* 0x30 to 0x3f */ + { "and" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "and" , ZP_X }, + { "rol" , ZP_X }, + { "???" , ILLEGAL }, + { "sec" , IMPLIED }, + { "and" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "and" , ABS_X }, + { "rol" , ABS_X }, + { "???" , ILLEGAL }, + + { "rti" , IMPLIED }, /* 0x40 to 0x4f */ + { "eor" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "eor" , ZP }, + { "lsr" , ZP }, + { "???" , ILLEGAL }, + { "pha" , IMPLIED }, + { "eor" , IMMEDIATE }, + { "lsr" , ACCUMULATOR }, + { "???" , ILLEGAL }, + { "jmp" , ABS }, + { "eor" , ABS }, + { "lsr" , ABS }, + { "???" , ILLEGAL }, + + { "bvc" , REL }, /* 0x50 to 0x5f */ + { "eor" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "eor" , ZP_X }, + { "lsr" , ZP_X }, + { "???" , ILLEGAL }, + { "cli" , IMPLIED }, + { "eor" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "eor" , ABS_X }, + { "lsr" , ABS_X }, + { "???" , ILLEGAL }, + + { "rts" , IMPLIED }, /* 0x60 to 0x6f */ + { "adc" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "adc" , ZP }, + { "ror" , ZP }, + { "???" , ILLEGAL }, + { "pla" , IMPLIED }, + { "adc" , IMMEDIATE }, + { "ror" , ACCUMULATOR }, + { "???" , ILLEGAL }, + { "jmp" , ABS_IND }, + { "adc" , ABS }, + { "ror" , ABS }, + { "???" , ILLEGAL }, + + { "bvs" , REL }, /* 0x70 to 0x7f */ + { "adc" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "adc" , ZP_X }, + { "ror" , ZP_X }, + { "???" , ILLEGAL }, + { "sei" , IMPLIED }, + { "adc" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "adc" , ABS_X }, + { "ror" , ABS_X }, + { "???" , ILLEGAL }, + + { "???" , ILLEGAL }, /* 0x80 to 0x8f */ + { "sta" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sty" , ZP }, + { "sta" , ZP }, + { "stx" , ZP }, + { "???" , ILLEGAL }, + { "dey" , IMPLIED }, + { "???" , ILLEGAL }, + { "txa" , IMPLIED }, + { "???" , ILLEGAL }, + { "sty" , ABS }, + { "sta" , ABS }, + { "stx" , ABS }, + { "???" , ILLEGAL }, + + { "bcc" , REL }, /* 0x90 to 0x9f */ + { "sta" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sty" , ZP_X }, + { "sta" , ZP_X }, + { "stx" , ZP_Y }, + { "???" , ILLEGAL }, + { "tya" , IMPLIED }, + { "sta" , ABS_Y }, + { "txs" , IMPLIED }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sta" , ABS_X }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + + { "ldy" , IMMEDIATE }, /* 0xa0 to 0xaf */ + { "lda" , ZP_X_IND }, + { "ldx" , IMMEDIATE }, + { "???" , ILLEGAL }, + { "ldy" , ZP }, + { "lda" , ZP }, + { "ldx" , ZP }, + { "???" , ILLEGAL }, + { "tay" , IMPLIED }, + { "lda" , IMMEDIATE }, + { "tax" , IMPLIED }, + { "???" , ILLEGAL }, + { "ldy" , ABS }, + { "lda" , ABS }, + { "ldx" , ABS }, + { "???" , ILLEGAL }, + + { "bcs" , REL }, /* 0xb0 to 0xbf */ + { "lda" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "ldy" , ZP_X }, + { "lda" , ZP_X }, + { "ldx" , ZP_Y }, + { "???" , ILLEGAL }, + { "clv" , IMPLIED }, + { "lda" , ABS_Y }, + { "tsx" , IMPLIED }, + { "???" , ILLEGAL }, + { "ldy" , ABS_X }, + { "lda" , ABS_X }, + { "ldx" , ABS_Y }, + { "???" , ILLEGAL }, + + { "cpy" , IMMEDIATE }, /* 0xc0 to 0xcf */ + { "cmp" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "cpy" , ZP }, + { "cmp" , ZP }, + { "dec" , ZP }, + { "???" , ILLEGAL }, + { "iny" , IMPLIED }, + { "cmp" , IMMEDIATE }, + { "dex" , IMPLIED }, + { "???" , ILLEGAL }, + { "cpy" , ABS }, + { "cmp" , ABS }, + { "dec" , ABS }, + { "???" , ILLEGAL }, + + { "bne" , REL }, /* 0xd0 to 0xdf */ + { "cmp" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "cmp" , ZP_X }, + { "dec" , ZP_X }, + { "???" , ILLEGAL }, + { "cld" , IMPLIED }, + { "cmp" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "cmp" , ABS_X }, + { "dec" , ABS_X }, + { "???" , ILLEGAL }, + + { "cpx" , IMMEDIATE }, /* 0xe0 to 0xef */ + { "sbc" , ZP_X_IND }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "cpx" , ZP }, + { "sbc" , ZP }, + { "inc" , ZP }, + { "???" , ILLEGAL }, + { "inx" , IMPLIED }, + { "sbc" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "???" , ILLEGAL }, + { "cpx" , ABS }, + { "sbc" , ABS }, + { "inc" , ABS }, + { "???" , ILLEGAL }, + + { "beq" , REL }, /* 0xf0 to 0xff */ + { "sbc" , ZP_IND_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sbc" , ZP_X }, + { "inc" , ZP_X }, + { "???" , ILLEGAL }, + { "sed" , IMPLIED }, + { "sbc" , ABS_Y }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "???" , ILLEGAL }, + { "sbc" , ABS_X }, + { "inc" , ABS_X }, + { "???" , ILLEGAL } +}; + +/* Information for 65C02 opcodes. */ +static InstructionInfo II_65C02[256] = { + { "brk" , IMPLIED }, /* 0x00 to 0x0f */ + { "ora" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "tsb" , ZP }, + { "ora" , ZP }, + { "asl" , ZP }, + { "rmb0" , ZP }, + { "php" , IMPLIED }, + { "ora" , IMMEDIATE }, + { "asl" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "tsb" , ABS }, + { "ora" , ABS }, + { "asl" , ABS }, + { "bbr0" , ZP_REL }, + + { "bpl" , REL }, /* 0x10 to 0x1f */ + { "ora" , ZP_IND_Y }, + { "ora" , ZP_IND }, + { "nop" , IMPLIED }, + { "trb" , ZP }, + { "ora" , ZP_X }, + { "asl" , ZP_X }, + { "rmb1" , ZP }, + { "clc" , IMPLIED }, + { "ora" , ABS_Y }, + { "inc" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "trb" , ABS }, + { "ora" , ABS_X }, + { "asl" , ABS_X }, + { "bbr1" , ZP_REL }, + + { "jsr" , ABS }, /* 0x20 to 0x2f */ + { "and" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "bit" , ZP }, + { "and" , ZP }, + { "rol" , ZP }, + { "rmb2" , ZP }, + { "plp" , IMPLIED }, + { "and" , IMMEDIATE }, + { "rol" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "bit" , ABS }, + { "and" , ABS }, + { "rol" , ABS }, + { "bbr2" , ZP_REL }, + + { "bmi" , REL }, /* 0x30 to 0x3f */ + { "and" , ZP_IND_Y }, + { "and" , ZP_IND }, + { "nop" , IMPLIED }, + { "bit" , ZP_X }, + { "and" , ZP_X }, + { "rol" , ZP_X }, + { "rmb3" , ZP }, + { "sec" , IMPLIED }, + { "and" , ABS_Y }, + { "dec" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "bit" , ABS_X }, + { "and" , ABS_X }, + { "rol" , ABS_X }, + { "bbr3" , ZP_REL }, + + { "rti" , IMPLIED }, /* 0x40 to 0x4f */ + { "eor" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , ILLEGAL }, + { "nop" , ZP }, + { "eor" , ZP }, + { "lsr" , ZP }, + { "rmb4" , ZP }, + { "pha" , IMPLIED }, + { "eor" , IMMEDIATE }, + { "lsr" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "jmp" , ABS }, + { "eor" , ABS }, + { "lsr" , ABS }, + { "bbr4" , ZP_REL }, + + { "bvc" , REL }, /* 0x50 to 0x5f */ + { "eor" , ZP_IND_Y }, + { "eor" , ZP_IND }, + { "nop" , IMPLIED }, + { "nop" , ZP_X }, + { "eor" , ZP_X }, + { "lsr" , ZP_X }, + { "rmb5" , ZP }, + { "cli" , IMPLIED }, + { "eor" , ABS_Y }, + { "phy" , IMPLIED }, + { "nop" , IMPLIED }, + { "nop" , ABS }, + { "eor" , ABS_X }, + { "lsr" , ABS_X }, + { "bbr5" , ZP_REL }, + + { "rts" , IMPLIED }, /* 0x60 to 0x6f */ + { "adc" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "stz" , ZP }, + { "adc" , ZP }, + { "ror" , ZP }, + { "rmb6" , ZP }, + { "pla" , IMPLIED }, + { "adc" , IMMEDIATE }, + { "ror" , ACCUMULATOR }, + { "nop" , IMPLIED }, + { "jmp" , ABS_IND }, + { "adc" , ABS }, + { "ror" , ABS }, + { "bbr6" , ZP_REL }, + + { "bvs" , REL }, /* 0x70 to 0x7f */ + { "adc" , ZP_IND_Y }, + { "adc" , ZP_IND }, + { "nop" , IMPLIED }, + { "stz" , ZP_X }, + { "adc" , ZP_X }, + { "ror" , ZP_X }, + { "rmb7" , ZP }, + { "sei" , IMPLIED }, + { "adc" , ABS_Y }, + { "ply" , IMPLIED }, + { "nop" , IMPLIED }, + { "jmp" , ABS_X_IND }, + { "adc" , ABS_X }, + { "ror" , ABS_X }, + { "bbr7" , ZP_REL }, + + { "bra" , REL }, /* 0x80 to 0x8f */ + { "sta" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "sty" , ZP }, + { "sta" , ZP }, + { "stx" , ZP }, + { "smb0" , ZP }, + { "dey" , IMPLIED }, + { "bit" , IMMEDIATE }, + { "txa" , IMPLIED }, + { "nop" , IMPLIED }, + { "sty" , ABS }, + { "sta" , ABS }, + { "stx" , ABS }, + { "bbs0" , ZP_REL }, + + { "bcc" , REL }, /* 0x90 to 0x9f */ + { "sta" , ZP_IND_Y }, + { "sta" , ZP_IND }, + { "nop" , IMPLIED }, + { "sty" , ZP_X }, + { "sta" , ZP_X }, + { "stx" , ZP_Y }, + { "smb1" , ZP }, + { "tya" , IMPLIED }, + { "sta" , ABS_Y }, + { "txs" , IMPLIED }, + { "nop" , IMPLIED }, + { "stz" , ABS }, + { "sta" , ABS_X }, + { "stz" , ABS_X }, + { "bbs1" , ZP_REL }, + + { "ldy" , IMMEDIATE }, /* 0xa0 to 0xaf */ + { "lda" , ZP_X_IND }, + { "ldx" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "ldy" , ZP }, + { "lda" , ZP }, + { "ldx" , ZP }, + { "smb2" , ZP }, + { "tay" , IMPLIED }, + { "lda" , IMMEDIATE }, + { "tax" , IMPLIED }, + { "nop" , IMPLIED }, + { "ldy" , ABS }, + { "lda" , ABS }, + { "ldx" , ABS }, + { "bbs2" , ZP_REL }, + + { "bcs" , REL }, /* 0xb0 to 0xbf */ + { "lda" , ZP_IND_Y }, + { "lda" , ZP_IND }, + { "nop" , IMPLIED }, + { "ldy" , ZP_X }, + { "lda" , ZP_X }, + { "ldx" , ZP_Y }, + { "smb3" , ZP }, + { "clv" , IMPLIED }, + { "lda" , ABS_Y }, + { "tsx" , IMPLIED }, + { "nop" , IMPLIED }, + { "ldy" , ABS_X }, + { "lda" , ABS_X }, + { "ldx" , ABS_Y }, + { "bbs3" , ZP_REL }, + + { "cpy" , IMMEDIATE }, /* 0xc0 to 0xcf */ + { "cmp" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "cpy" , ZP }, + { "cmp" , ZP }, + { "dec" , ZP }, + { "smb4" , ZP }, + { "iny" , IMPLIED }, + { "cmp" , IMMEDIATE }, + { "dex" , IMPLIED }, + { "wai" , IMPLIED }, + { "cpy" , ABS }, + { "cmp" , ABS }, + { "dec" , ABS }, + { "bbs4" , ZP_REL }, + + { "bne" , REL }, /* 0xd0 to 0xdf */ + { "cmp" , ZP_IND_Y }, + { "cmp" , ZP_IND }, + { "nop" , IMPLIED }, + { "nop" , ZP_X }, + { "cmp" , ZP_X }, + { "dec" , ZP_X }, + { "smb5" , ZP }, + { "cld" , IMPLIED }, + { "cmp" , ABS_Y }, + { "phx" , IMPLIED }, + { "stp" , IMPLIED }, + { "nop" , ABS }, + { "cmp" , ABS_X }, + { "dec" , ABS_X }, + { "bbs5" , ZP_REL }, + + { "cpx" , IMMEDIATE }, /* 0xe0 to 0xef */ + { "sbc" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "cpx" , ZP }, + { "sbc" , ZP }, + { "inc" , ZP }, + { "smb6" , ZP }, + { "inx" , IMPLIED }, + { "sbc" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "nop" , IMPLIED }, + { "cpx" , ABS }, + { "sbc" , ABS }, + { "inc" , ABS }, + { "bbs6" , ZP_REL }, + + { "beq" , REL }, /* 0xf0 to 0xff */ + { "sbc" , ZP_IND_Y }, + { "sbc" , ZP_IND }, + { "nop" , IMPLIED }, + { "nop" , ZP_X }, + { "sbc" , ZP_X }, + { "inc" , ZP_X }, + { "smb7" , ZP }, + { "sed" , IMPLIED }, + { "sbc" , ABS_Y }, + { "plx" , IMPLIED }, + { "nop" , IMPLIED }, + { "nop" , ABS }, + { "sbc" , ABS_X }, + { "inc" , ABS_X }, + { "bbs7" , ZP_REL } +}; + +/* Information for 6502X (6502 with undocumented instructions) opcodes. */ +static InstructionInfo II_6502X[256] = { + { "brk" , IMPLIED }, /* 0x00 to 0x0f */ + { "ora" , ZP_X_IND }, + { "jam" , IMPLIED }, + { "slo" , ZP_X_IND }, + { "nop" , ZP }, + { "ora" , ZP }, + { "asl" , ZP }, + { "slo" , ZP }, + { "php" , IMPLIED }, + { "ora" , IMMEDIATE }, + { "asl" , ACCUMULATOR }, + { "anc" , IMMEDIATE }, + { "nop" , ABS }, + { "ora" , ABS }, + { "asl" , ABS }, + { "slo" , ABS }, + + { "bpl" , REL }, /* 0x10 to 0x1f */ + { "ora" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "slo" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "ora" , ZP_X }, + { "asl" , ZP_X }, + { "slo" , ZP }, + { "clc" , IMPLIED }, + { "ora" , ABS_Y }, + { "nop" , IMPLIED }, + { "slo" , ABS_Y }, + { "*nop" , ABS_X }, + { "ora" , ABS_X }, + { "asl" , ABS_X }, + { "slo" , ABS_X }, + + { "jsr" , ABS }, /* 0x20 to 0x2f */ + { "and" , ZP_X_IND }, + { "jam" , IMPLIED }, + { "rla" , ZP_X_IND }, + { "bit" , ZP }, + { "and" , ZP }, + { "rol" , ZP }, + { "rla" , ZP }, + { "plp" , IMPLIED }, + { "and" , IMMEDIATE }, + { "rol" , ACCUMULATOR }, + { "anc" , IMMEDIATE }, + { "bit" , ABS }, + { "and" , ABS }, + { "rol" , ABS }, + { "rla" , ABS }, + + { "bmi" , REL }, /* 0x30 to 0x3f */ + { "and" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "rla" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "and" , ZP_X }, + { "rol" , ZP_X }, + { "rla" , ZP_X }, + { "sec" , IMPLIED }, + { "and" , ABS_Y }, + { "nop" , IMPLIED }, + { "rla" , ABS_Y }, + { "nop" , ABS_X }, + { "and" , ABS_X }, + { "rol" , ABS_X }, + { "rla" , ABS_X }, + + { "rti" , IMPLIED }, /* 0x40 to 0x4f */ + { "eor" , ZP_X_IND }, + { "jam" , IMPLIED }, + { "sre" , ZP_X_IND }, + { "nop" , ZP }, + { "eor" , ZP }, + { "lsr" , ZP }, + { "sre" , ZP }, + { "pha" , IMPLIED }, + { "eor" , IMMEDIATE }, + { "lsr" , ACCUMULATOR }, + { "alr" , IMMEDIATE }, + { "jmp" , ABS }, + { "eor" , ABS }, + { "lsr" , ABS }, + { "sre" , ABS }, + + { "bvc" , REL }, /* 0x50 to 0x5f */ + { "eor" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "sre" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "eor" , ZP_X }, + { "lsr" , ZP_X }, + { "sre" , ZP_X }, + { "cli" , IMPLIED }, + { "eor" , ABS_Y }, + { "nop" , IMPLIED }, + { "sre" , ABS_Y }, + { "nop" , ABS_X }, + { "eor" , ABS_X }, + { "lsr" , ABS_X }, + { "sre" , ABS_X }, + + { "rts" , IMPLIED }, /* 0x60 to 0x6f */ + { "adc" , ZP_X_IND }, + { "jam" , IMPLIED }, + { "rra" , ZP_X_IND }, + { "nop" , ZP }, + { "adc" , ZP }, + { "ror" , ZP }, + { "rra" , ZP }, + { "pla" , IMPLIED }, + { "adc" , IMMEDIATE }, + { "ror" , ACCUMULATOR }, + { "arr" , IMMEDIATE }, + { "jmp" , ABS_IND }, + { "adc" , ABS }, + { "ror" , ABS }, + { "rra" , ABS }, + + { "bvs" , REL }, /* 0x70 to 0x7f */ + { "adc" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "sre" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "adc" , ZP_X }, + { "ror" , ZP_X }, + { "rra" , ZP_X }, + { "sei" , IMPLIED }, + { "adc" , ABS_Y }, + { "nop" , IMPLIED }, + { "rra" , ABS_Y }, + { "nop" , ABS_X }, + { "adc" , ABS_X }, + { "ror" , ABS_X }, + { "rra" , ABS_X }, + + { "nop" , IMMEDIATE }, /* 0x80 to 0x8f */ + { "sta" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "sax" , ZP_X_IND }, + { "sty" , ZP }, + { "sta" , ZP }, + { "stx" , ZP }, + { "sax" , ZP }, + { "dey" , IMPLIED }, + { "nop" , IMMEDIATE }, + { "txa" , IMPLIED }, + { "ane" , IMMEDIATE }, + { "sty" , ABS }, + { "sta" , ABS }, + { "stx" , ABS }, + { "sax" , ABS }, + + { "bcc" , REL }, /* 0x90 to 0x9f */ + { "sta" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "sha" , ZP_IND_Y }, + { "sty" , ZP_X }, + { "sta" , ZP_X }, + { "stx" , ZP_Y }, + { "sax" , ZP_Y }, + { "tya" , IMPLIED }, + { "sta" , ABS_Y }, + { "txs" , IMPLIED }, + { "tas" , ABS_Y }, + { "shy" , ABS_X }, + { "sta" , ABS_X }, + { "shx" , ABS_Y }, + { "sha" , ABS_Y }, + + { "ldy" , IMMEDIATE }, /* 0xa0 to 0xaf */ + { "lda" , ZP_X_IND }, + { "ldx" , IMMEDIATE }, + { "lax" , ZP_X_IND }, + { "ldy" , ZP }, + { "lda" , ZP }, + { "ldx" , ZP }, + { "lax" , ZP }, + { "tay" , IMPLIED }, + { "lda" , IMMEDIATE }, + { "tax" , IMPLIED }, + { "lax" , IMMEDIATE }, + { "ldy" , ABS }, + { "lda" , ABS }, + { "ldx" , ABS }, + { "lax" , ABS }, + + { "bcs" , REL }, /* 0xb0 to 0xbf */ + { "lda" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "lax" , ZP_IND_Y }, + { "ldy" , ZP_X }, + { "lda" , ZP_X }, + { "ldx" , ZP_Y }, + { "lax" , ZP_Y }, + { "clv" , IMPLIED }, + { "lda" , ABS_Y }, + { "tsx" , IMPLIED }, + { "las" , ABS_Y }, + { "ldy" , ABS_X }, + { "lda" , ABS_X }, + { "ldx" , ABS_Y }, + { "lax" , ABS_Y }, + + { "cpy" , IMMEDIATE }, /* 0xc0 to 0xcf */ + { "cmp" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "dcp" , ZP_X_IND }, + { "cpy" , ZP }, + { "cmp" , ZP }, + { "dec" , ZP }, + { "dcp" , ZP }, + { "iny" , IMPLIED }, + { "cmp" , IMMEDIATE }, + { "dex" , IMPLIED }, + { "sbx" , IMMEDIATE }, + { "cpy" , ABS }, + { "cmp" , ABS }, + { "dec" , ABS }, + { "dcp" , ABS }, + + { "bne" , REL }, /* 0xd0 to 0xdf */ + { "cmp" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "dcp" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "cmp" , ZP_X }, + { "dec" , ZP_X }, + { "dcp" , ZP_X }, + { "cld" , IMPLIED }, + { "cmp" , ABS_Y }, + { "nop" , IMPLIED }, + { "dcp" , ABS_Y }, + { "nop" , ABS_X }, + { "cmp" , ABS_X }, + { "dec" , ABS_X }, + { "dcp" , ABS_X }, + + { "cpx" , IMMEDIATE }, /* 0xe0 to 0xef */ + { "sbc" , ZP_X_IND }, + { "nop" , IMMEDIATE }, + { "isc" , ZP_X_IND }, + { "cpx" , ZP }, + { "sbc" , ZP }, + { "inc" , ZP }, + { "isc" , ZP }, + { "inx" , IMPLIED }, + { "sbc" , IMMEDIATE }, + { "nop" , IMPLIED }, + { "sbc" , IMMEDIATE }, + { "cpx" , ABS }, + { "sbc" , ABS }, + { "inc" , ABS }, + { "isc" , ABS }, + + { "beq" , REL }, /* 0xf0 to 0xff */ + { "sbc" , ZP_IND_Y }, + { "jam" , IMPLIED }, + { "isc" , ZP_IND_Y }, + { "nop" , ZP_X }, + { "sbc" , ZP_X }, + { "inc" , ZP_X }, + { "isc" , ZP_X }, + { "sed" , IMPLIED }, + { "sbc" , ABS_Y }, + { "nop" , IMPLIED }, + { "isc" , ABS_Y }, + { "nop" , ABS_X }, + { "sbc" , ABS_X }, + { "inc" , ABS_X }, + { "isc" , ABS_X } +}; + +static InstructionInfo * II[3] = { II_6502, II_65C02, II_6502X }; + +static unsigned GetInstructionLength (uint8_t opcode) +/* Get the number of bytes in the full instruction. Depends on the addressing mode. */ +{ + switch (II[CPU][opcode].adrmode) { + case ILLEGAL: + case IMPLIED: + case ACCUMULATOR: + return 1; + case IMMEDIATE: + case REL: + case ZP: + case ZP_X: + case ZP_Y: + case ZP_IND: + case ZP_X_IND: + case ZP_IND_Y: + return 2; + case ZP_REL: + case ABS: + case ABS_X: + case ABS_Y: + case ABS_IND: + case ABS_X_IND: + return 3; + } + + /* We should never get here. */ + return -1; +} + + + +static char * PrintAssemblyInstruction (char * ptr) +/* Print assembly instruction: mnemonic and addres-mode specific operand(s). */ +{ + uint8_t opcode; + + /* Print the instruction starting at the current program counter. */ + + opcode = MemReadByte (Regs.PC); + + ptr += sprintf (ptr, "%-4s ", II[CPU][opcode].mnemonic); + + switch (II[CPU][opcode].adrmode) { + case IMPLIED: + case ILLEGAL: + break; + case ACCUMULATOR: + ptr += sprintf (ptr, "A"); + break; + case IMMEDIATE: + ptr += sprintf (ptr, "#$%02X", MemReadByte (Regs.PC + 1)); + break; + case REL: + ptr += sprintf (ptr, "$%04X", Regs.PC + 2 + (int8_t)MemReadByte (Regs.PC + 1)); + break; + case ZP: + ptr += sprintf (ptr, "$%02X", MemReadByte (Regs.PC + 1)); + break; + case ZP_X: + ptr += sprintf (ptr, "$%02X,X", MemReadByte (Regs.PC + 1)); + break; + case ZP_Y: + ptr += sprintf (ptr, "$%02X,Y", MemReadByte (Regs.PC + 1)); + break; + case ZP_IND: + ptr += sprintf (ptr, "($%02X)", MemReadByte (Regs.PC + 1)); + break; + case ZP_X_IND: + ptr += sprintf (ptr, "($%02X,X)", MemReadByte (Regs.PC + 1)); + break; + case ZP_IND_Y: + ptr += sprintf (ptr, "($%02X),Y", MemReadByte (Regs.PC + 1)); + break; + case ZP_REL: + ptr += sprintf (ptr, "$%02X,$%04X", MemReadByte (Regs.PC + 1), Regs.PC + 3 + (int8_t)MemReadByte (Regs.PC + 2)); + break; + case ABS: + ptr += sprintf (ptr, "$%04X", MemReadWord (Regs.PC + 1)); + break; + case ABS_IND: + ptr += sprintf (ptr, "($%04X)", MemReadWord (Regs.PC + 1)); + break; + case ABS_X: + ptr += sprintf (ptr, "$%04X,X", MemReadWord (Regs.PC + 1)); + break; + case ABS_X_IND: + ptr += sprintf (ptr, "($%04X,X)", MemReadWord (Regs.PC + 1)); + break; + case ABS_Y: + ptr += sprintf (ptr, "$%04X,Y", MemReadWord (Regs.PC + 1)); + break; + } + + return ptr; +} + + + +static void PrintTraceInstructionOrInterrupt (const char * InterruptType) +{ + char traceline[200]; + char * traceline_ptr = traceline; + uint8_t opcode; + unsigned k, num_bytes; + + if (TraceMode & TRACE_FIELD_INSTR_COUNTER) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, "%12" PRIu64, Peripherals.Counter.CpuInstructions); + } + + if (TraceMode & TRACE_FIELD_CLOCK_COUNTER) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, "%12" PRIu64, Peripherals.Counter.ClockCycles); + } + + if (TraceMode & TRACE_FIELD_PC) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, "%04X", Regs.PC); + } + + if (TraceMode & TRACE_FIELD_INSTR_BYTES) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + if (InterruptType == NULL) + { + /* Get the opcode */ + opcode = MemReadByte (Regs.PC); + + /* How many bytes are in the full instruction? 1, 2 or 3. */ + num_bytes = GetInstructionLength (opcode); + } else { + num_bytes = 0; /* Consider interrupts as instructions that are inserted into the instruction stream. */ + } + + /* Print 0 to 3 bytes for the interrupt/instruction. */ + for (k = 0; k < 3; ++k) { + if (k != 0) { + *traceline_ptr++ = ' '; + } + if (k < num_bytes) { + traceline_ptr += sprintf (traceline_ptr, "%02X", MemReadByte (Regs.PC + k)); + } else { + traceline_ptr += sprintf (traceline_ptr, " "); + } + } + } + + if (TraceMode & TRACE_FIELD_INSTR_ASSEMBLY) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + char * save_ptr = traceline_ptr; + + if (InterruptType == NULL) { + traceline_ptr = PrintAssemblyInstruction (traceline_ptr); + } else { + /* Print interrupt message. */ + traceline_ptr += sprintf (traceline_ptr, "*** %s ***", InterruptType); + } + + /* Fill out the field to 16 characters */ + num_bytes = (unsigned)(traceline_ptr - save_ptr); + if (num_bytes < 16) { + traceline_ptr += sprintf (traceline_ptr, "%*s", 16 - num_bytes, ""); + } + } + + if (TraceMode & TRACE_FIELD_CPU_REGISTERS) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, + "A=%02X X=%02X Y=%02X S=%02X Flags=%c%c%c%c%c%c", + Regs.AC, + Regs.XR, + Regs.YR, + Regs.SP, + (Regs.SR & SF) ? 'N' : 'n', + (Regs.SR & OF) ? 'V' : 'v', + (Regs.SR & DF) ? 'D' : 'd', + (Regs.SR & IF) ? 'I' : 'i', + (Regs.SR & ZF) ? 'Z' : 'z', + (Regs.SR & CF) ? 'C' : 'c' + ); + } + + if (TraceMode & TRACE_FIELD_CC65_SP) { + + if (traceline_ptr != traceline) { + /* Print field separator. */ + traceline_ptr += sprintf (traceline_ptr, " "); + } + + traceline_ptr += sprintf (traceline_ptr, + " SP=%04X", + MemReadZPWord (StackPointerZPageAddress) + ); + } + + if (traceline_ptr != traceline) { + puts (traceline); + } +} + + + +void TraceInit (uint8_t SPAddr) +{ + StackPointerZPageAddress = SPAddr; +} + + + +void PrintTraceNMI (void) +{ + PrintTraceInstructionOrInterrupt("NMI"); +} + + + +void PrintTraceIRQ (void) +{ + PrintTraceInstructionOrInterrupt("IRQ"); +} + + + +void PrintTraceInstruction (void) +{ + PrintTraceInstructionOrInterrupt(NULL); +} diff --git a/src/sim65/trace.h b/src/sim65/trace.h new file mode 100644 index 000000000..f982460d2 --- /dev/null +++ b/src/sim65/trace.h @@ -0,0 +1,89 @@ +/*****************************************************************************/ +/* */ +/* trace.h */ +/* */ +/* Instruction tracing functionality sim65 6502 simulator */ +/* */ +/* */ +/* */ +/* (C) 2025, Sidney Cadot */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + +#ifndef TRACE_H +#define TRACE_H + + +#include + + +#include "6502.h" + +/* The trace mode is a bitfield that determines how trace lines are displayed. + * + * The value zero indicates that tracing is disabled (the default). + * + * In case TraceMode is not equal to zero, the value is interpreted as a bitfield: + * + * Bit Bit value Enables + * --- ----------- ------------------------------- + * 6 0x40 ( 64) Print the instruction counter. + * 5 0x20 ( 32) Print the clock cycle counter. + * 4 0x10 ( 16) Print the PC (program counter). + * 3 0x08 ( 8) Print the instruction bytes. + * 2 0x04 ( 4) Print the instruction assembly. + * 1 0x02 ( 2) Print the CPU registers. + * 0 0x01 ( 1) Print the CC65 stack pointer. + * + */ + +#define TRACE_FIELD_INSTR_COUNTER 0x40 +#define TRACE_FIELD_CLOCK_COUNTER 0x20 +#define TRACE_FIELD_PC 0x10 +#define TRACE_FIELD_INSTR_BYTES 0x08 +#define TRACE_FIELD_INSTR_ASSEMBLY 0x04 +#define TRACE_FIELD_CPU_REGISTERS 0x02 +#define TRACE_FIELD_CC65_SP 0x01 + +#define TRACE_DISABLED 0x00 +#define TRACE_ENABLE_FULL 0x7f + +/* Currently active tracing mode. */ +extern uint8_t TraceMode; + +void TraceInit (uint8_t SPAddr); +/* Initialize the trace subsystem. */ + +void PrintTraceNMI(void); +/* Print trace line for an NMI interrupt. */ + +void PrintTraceIRQ(void); +/* Print trace line for an IRQ interrupt. */ + +void PrintTraceInstruction (void); +/* Print trace line for the instruction at the currrent program counter. */ + + + +/* End of trace.h */ + +#endif