Unfortunately sim65 doesn't really have any debugging features. After some experimentation I decided the fastest way to get some visibility into what the program is doing is to just dump the registers out at various points in the program. To do that I created the module below.
The install_debug_handler function points the BRK vector to debug_handler, which dumps out the registers and flags via cc65's standard library function fprintf. You probably know that BRK is actually a two-byte instruction where the second byte is ignored. The debug handler takes advantage of this by printing the byte following the BRK along with the registers and flags; I use the second byte to identify where the program is. I defined a debug macro that just accepts an argument and adds it after the BRK:
Code: Select all
.macro debug value
brk
.byte value
.endmacroHere's the module. Let me know if you find any issues or have suggestions for improvement.
Code: Select all
; MIT License
;
; Copyright (c) 2022 Willis Blackburn
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE
; cc65 runtime
.import pushax, pusha0
; C standard library
.import _fprintf, _stderr
; Point the BRK handler to the debug_handler function.
install_debug_handler:
lda #<debug_handler
sta $FFFE ; BRK vector low byte
lda #>debug_handler
sta $FFFF ; BRK vector high byte
rts
.export install_debug_handler
.zeropage
save_pc: .res 2
.bss
save_a: .res 1
save_x: .res 1
save_y: .res 1
save_sp: .res 1
save_flags: .res 1
flag_indicators: .res 8
.code
format: .byte "$%02X: A=%02X X=%02X Y=%02X SP=%02X %.8s", $0A, $00
flag_names: .byte "NV-BDIZC"
; Prints the register values to stderr.
; Although calling into the C library from an interrupt handler is normally asking for trouble, since sim65
; doesn't generate interrupts, this will only be called by a BRK statement.
debug_handler:
cld ; Clear decimal flag (just in case)
sta save_a ; Save 6502 registers
stx save_x
sty save_y
tsx ; Get stack pointer into X
stx save_sp ; Save it so we can print it
ldy $102,x ; PC low byte
sty save_pc
ldy $103,x ; PC high byte
dey ; Subtract 256 from PC; we will index with Y = 255 to get PC-1
sty save_pc+1
lda $101,x ; Flags
sta save_flags
ldy #0
@next_flag:
lda flag_names,y ; Store name in indicator string if flag on
rol save_flags
bcs @on
lda #'-' ; Store '-' indicator string if flag off
@on:
sta flag_indicators,y
iny
cpy #8
bne @next_flag
lda _stderr ; fprintf(stderr, ...
ldx _stderr+1
jsr pushax
lda #<format ; format, ...
ldx #>format
jsr pushax
ldy #$FF
lda (save_pc),y ; id, ...
jsr pusha0
lda save_a ; A, ...
jsr pusha0
lda save_x ; X, ...
jsr pusha0
lda save_y ; Y, ...
jsr pusha0
lda save_sp ; SP, ...
jsr pusha0
lda #<flag_indicators ; flag_indicators)
ldx #>flag_indicators
jsr pushax
ldy #16 ; 16 bytes on the C stack
jsr _fprintf
lda save_a ; Restore 6502 registers
ldx save_x
ldy save_y
rti