/*****************************************************************************/ /* */ /* 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); }