musings for making a debugging-mapper
Posted: Thu May 18, 2017 9:21 am
I've been trying to work out in my head how to debug and tracelog solely from cart-edge signals. Obviously, one could simply fully-replicate the 6502 state. That would take too many FPGA resources, I believe, so I'm trying to pare it down first. This will also save me having to implement an entire 6502.
What seems key is maintaining a working knowledge of T₁, that is, when an instruction fetch happens: this allows you to know it's an opcode fetch, and what, if you must; it certainly seems helpful to know BRAnch, JMP, JSR, BRK for tracing.
My first inclination was "count argument bytes, next fetch is opcode, too". It didn't take long to come up with some problem cases: BRA [-1,0,+1], or as the dummy fetch will seem to be at the right address, but won't be an opcode fetch. Then, of course, the knowledge of "is-opcode-fetch" is lost. (The page-boundary fixup cycles are moderately-easily-detectable, on loads nor branches: loads, they're less-than; branches, out-of-range, though care will need be taken to spot a -128 branch)
NMIs may be another problem, at least, if one isn't allowing an assumption that $FFFA-FFFF is never executed. For a debugger, you don't want assumptions like that. However, if one is logging stack accesses, a [surprise] triple-push of PCH, PCL, P (which is a triple-write, if I'm understanding things, something that the processor never does, else) would seem to be a good tripwire for "now interrupting" and is followed by reading the destination vector. (BRK and IRQ are less problematic; IRQ will be asserted by mapper hardware and is thus interceptable; if we maintain opcode fetch parity knowledge, BRK is just another known.)
[hr]
So perhaps I should attack it from a different angle than keeping track. Log the last four reads, keeping track of where, particularly when they're sequential.
Hypothesis: Three sequential reads must include at least one opcode/operand fetch.
(I'm trying to think of("fuzz") how to set up a degenerate["pathological" was the word that timed my post out trying to remember] case for executing through the stack page…hmm…I think a JMP (label) label: would confuse, but there's a simpler triple-read: RTI (an RTS precisely into stack will also trip this.)
ldy #1 .org $4A ldy ($4C),y .db $4E, $00, $any, $any provides 6 nonopcode-fetches. An RTI in-stack will hit 5.
(These are all problems for "keep memory of what's the opcode" too.)
One needs really only trap branches-taken(hard), interrupts/BRK (easy), JSR (easy-ish), JMP (harder, no stack-write evidence)…argh.
I'm pretty sure there's a pathological case for every simple comparison I've come up with so far…
catching a JMP seems easy prima facie (read #$4C, read #$lo, read#$hi, read $hilo) , but the above case would trip it incorrectly.
JSR should be catchable by any double-descending-write that is not followed by the P push an interrupt does…should.Does an indexed y/x=FF pagebreak trigger? No, that's an extra descending read, not a write.
---
ed2:
Pathological cases for a Branch-detector:
What seems key is maintaining a working knowledge of T₁, that is, when an instruction fetch happens: this allows you to know it's an opcode fetch, and what, if you must; it certainly seems helpful to know BRAnch, JMP, JSR, BRK for tracing.
My first inclination was "count argument bytes, next fetch is opcode, too". It didn't take long to come up with some problem cases: BRA [-1,0,+1], or
Code: Select all
LDA next,y ;where y = 1
next:
NMIs may be another problem, at least, if one isn't allowing an assumption that $FFFA-FFFF is never executed. For a debugger, you don't want assumptions like that. However, if one is logging stack accesses, a [surprise] triple-push of PCH, PCL, P (which is a triple-write, if I'm understanding things, something that the processor never does, else) would seem to be a good tripwire for "now interrupting" and is followed by reading the destination vector. (BRK and IRQ are less problematic; IRQ will be asserted by mapper hardware and is thus interceptable; if we maintain opcode fetch parity knowledge, BRK is just another known.)
[hr]
So perhaps I should attack it from a different angle than keeping track. Log the last four reads, keeping track of where, particularly when they're sequential.
Hypothesis: Three sequential reads must include at least one opcode/operand fetch.
(I'm trying to think of("fuzz") how to set up a degenerate["pathological" was the word that timed my post out trying to remember] case for executing through the stack page…hmm…I think a JMP (label) label: would confuse, but there's a simpler triple-read: RTI (an RTS precisely into stack will also trip this.)
ldy #1 .org $4A ldy ($4C),y .db $4E, $00, $any, $any provides 6 nonopcode-fetches. An RTI in-stack will hit 5.
(These are all problems for "keep memory of what's the opcode" too.)
One needs really only trap branches-taken(hard), interrupts/BRK (easy), JSR (easy-ish), JMP (harder, no stack-write evidence)…argh.
I'm pretty sure there's a pathological case for every simple comparison I've come up with so far…
catching a JMP seems easy prima facie (read #$4C, read #$lo, read#$hi, read $hilo) , but the above case would trip it incorrectly.
JSR should be catchable by any double-descending-write that is not followed by the P push an interrupt does…should.
---
ed2:
Pathological cases for a Branch-detector:
Code: Select all
.org $100
ldx #$06
txs
clc
bcc 0
pha ;gets read twice or three times, instead of 1-2 for a branch to +1