-
Notifications
You must be signed in to change notification settings - Fork 1
/
debug.asm
137 lines (128 loc) · 4.99 KB
/
debug.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
; contains code for generating debug messages and breakpoints within the
; code itself. BGB (an emulator) displays these messages and stops at the
; specified breakpoints generated by these macros. However, that is not bgb's
; default behavior. You'll want to edit bgb.ini (in the same folder as
; bgb.exe) to set these options:
; DebugSrcBrk=1 ; enable breakpoints embedded in source-code
; -- Use bug_break "msg". Breakpoints stop execution when it is reached.
; - The breakpoint message is also output to debugmsg.txt
; DebugMsgFile=1 ; output debug messages in source to "debugmsg.txt"
; -- Use bug_message "msg". Messages passed by this way are sent to
; - debugmsg.txt, which you can monitor with "tail -f" in linux
;
; One Last Thing: Expressions can be evaluated within debug messages
; "%boolean expression%text if true;text if false"
; or a simple expression can just be used, such as:
; "health > max! It's at %A%" <- which'll then print (if A contains 15):
; "heatlh > max! It's at 0F"
; "%A==0F%;squares greater than allowed;squares good"
; --
; list of expression variables that can be used:
; CPU registers: AF, BC, DE, HL, SP, PC, B, C, D, E, H, L, A
; CPU flags: ZERO, ZF, Z, CARRY, CY, IME, ALLREGS
; other state values: ROMBANK, XRAMBANK, SRAMBANK, WRAMBANK, VRAMBANK,
; TOTALCLKS, LASTCLKS, CLKS2VBLANK
IF !DEF(debug_asm)
debug_asm set 1
; this is a macro that stores in memory a debug message. This message will
; be skipped by the actual hardware, but in BGB or No$GMB it will display
; in their "debug messages" window
; ( I have confirmed it works in BGB )
bug_message: MACRO
ld d, d
jr .end\@
DW $6464
DW $0000
DB \1
.end\@
ENDM
; call this macro to automatically break / halt execution when this instruction
; is reached. This macro uses the bug_message macro, so you must pass a
; message into this macro as well
; ( I have confirmed it works in BGB )
bug_break: MACRO
bug_message \1
ld b, b
; I'm not sure if the below instructions are required, but I figure
; it's important to try
jr .end\@
DW $6464
DW $0000
.end\@
ENDM
; debug file is meant as a way to debug (LIVE!) running programs
; pass bug_ macros the text you wish to display, and it'll be written
; (in serial fashion) to rDIV, aka the address $FF04. Since most people don't
; ever write to this register, I figure it's safe to use for debugging purposes
; take a snapshot of the system registers
; in order: af bc de hl (yes, we also write the flags reg.)
; finally, SP and PC
; all of these register are written (one at a time, MSB first) to rDIV.
; if you use hardware or software to monitor writes to this address, you
; can capture the debug information.
; This routine preserves all registers, AND preserves (as best as possible)
; current interrupt and timer state.
; This writes heavily to rDIV, which has the side effect of incrementing other
; timers extremely quickly. To counteract this, I preserve interrupt flags
; and the timer and restore them at the end.
bug_snapshot:
di ; disable interrupts since we'll be doing some SP manipulation
push af
ldh a, [rTIMA] ; preserve timer counter. We'll restore
push af ; at end of snapshot
ldh a, [rIF] ; preserve interrupt flags. We'll restore
push af ; at end of snapshot
push hl
add sp, 6 ; backtrack past pushed HL & rIF & rTIMA values
pop af ; get original AF
ldh [rDIV], a ; write A
add sp, -1 ; go forward 1 byte. To align SP with F (flags)
; (remember SP starts at top of memory)
pop af ; now we pop off F? into AF
ldh [rDIV], a ; write F
; at this point, SP is +1 (1 byte before our pushed values began).
; it also is 1 byte into the return address (pushed when calling
; bug_snapshot)
; we've pushed a total of 8 bytes. So SP is 9 bytes away from top
; of where we want to be
add SP, -1 ; align SP to where it was before we pushed 8 bytes
; (so SP is 8 bytes from top). From here we can Pop the
; return address -- aka PC before snapshot was called
ld a, b
ldh [rDIV], a ; write b
ld a, c
ldh [rDIV], a ; write c
ld a, d
ldh [rDIV], a ; write d
ld a, e
ldh [rDIV], a ; write e
ld a, h
ldh [rDIV], a ; write h
ld a, l
ldh [rDIV], a ; write l
ld hl, sp+2 ; get SP address (before fxn call)
; +2 basically offsets so that HL now holds
; where SP would have pointed right before this
; routine was called
ld a, h
ldh [rDIV], a ; write S
ld a, l
ldh [rDIV], a ; write P
; (remember, we left SP ready to pop off return addr)
pop hl ; return address == PC before bug_snapshot was called
; SP is now 10 bytes away from top (PC,AF,AF,AF,HL)
add sp, -10 ; return SP to top of stack, ready to pop
ld a, h
ldh [rDIV], a ; write P
ld a, l
ldh [rDIV], a ; write C
; that's it! We've written to rDIV our entire register stack, in order.
; AF, BC, DE, HL, SP, PC
pop hl
pop af ; restore original interrupt flags (so that we
ldh [rIF], a ; interrupts to pre-snapshot state)
pop af
ldh [rTIMA], a ; restore timer to pre-snapshot state
pop af ;get original AF values
reti ; return and enable interrupts
ENDC ; end definition of debug.asm