Break: name the status register properly and show the Stack Pointer

The dump labelled the status register "S", which reads as Stack to anybody
sensible - and the Stack Pointer was the one register it did not show, so
there was nothing to contradict the guess. It is written "status" now, and
followed by the bits that are up, because a dump that makes you look the
number up is only half a dump.

The Stack Pointer is not in the frame, since the frame is where the Stack
Pointer is. What the program had is fourteen bytes above it, that being what
entering an interrupt puts down, so it is worked out and shown.

Apps/Break.asm takes its second stop inside a subroutine, so the recorded
output shows the Stack Pointer at FFFF and then at FFF5: a difference of ten,
which is the size of a CALL frame. That checks the value is derived rather
than constant, which the previous version could not have told you.

Reported by Anachronaut, who read the output and asked why a pointer was two
digits long.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Anachronaut
2026-08-19 22:19:15 -04:00
co-authored by Claude Opus 5
parent 5fd995aa62
commit c23adb2836
6 changed files with 80 additions and 17 deletions
+13 -7
View File
@@ -14,8 +14,9 @@
; addresses from a build without, which is the same bargain every machine makes that has a ; addresses from a build without, which is the same bargain every machine makes that has a
; break instruction. ; break instruction.
; ;
; Correct output is two stops, showing A and B changing between them, and the addresses of ; Correct output is two stops. A and B differ between them, the addresses differ, and the
; the two SWIs. ; Stack Pointer differs too, because the second one is inside a subroutine and a call has
; put ten bytes down by then.
#Include services.asm #Include services.asm
@@ -32,16 +33,21 @@ start:
SETD.0 Marker SETD.0 Marker
SWI osBreak SWI osBreak
; Something for the second stop to show as different. ; The second stop is inside a subroutine, so that the Stack Pointer is visibly not where
INIA 0d68 ; it was: a call puts ten bytes down before this one gets there.
INIB 0d85 CALL deeper
SETD.0 Banner
SWI osBreak
SETD.0 DoneText SETD.0 DoneText
SWI osPrintString SWI osPrintString
SWI osExit SWI osExit
deeper:
INIA 0d68
INIB 0d85
SETD.0 Banner
SWI osBreak
RET
#Data #Data
#Base 0x1000 #Base 0x1000
+54 -1
View File
@@ -1049,6 +1049,35 @@ breakAddress:
POPD.1 POPD.1
DPUP.1 0d1 DPUP.1 0d1
CALL breakByte CALL breakByte
; And which bits of it those are, since a debug dump that makes you look the number up is
; only half of one. Halt is not among them: the machine is plainly not halted.
PSHD.3
POPD.1
DPUP.1 0d1
LDA.1
PSHA
INIB 0x01
AND
BRQ breakNoCarry
SETD.0 CarryText
CALL printString
breakNoCarry:
POPA
PSHA
INIB 0x02
AND
BRQ breakNoFault
SETD.0 FaultText
CALL printString
breakNoFault:
POPA
INIB 0x04
AND
BRQ breakNoInts
SETD.0 IntsText
CALL printString
breakNoInts:
CALL newLine CALL newLine
SETD.0 DP0Text SETD.0 DP0Text
@@ -1071,6 +1100,22 @@ breakAddress:
POPD.1 POPD.1
DPUP.1 0d5 DPUP.1 0d5
CALL breakWord CALL breakWord
; The Stack Pointer is not in the frame, because the frame is where the Stack Pointer is.
; What the program had is fourteen bytes above this one, that being what entering an
; interrupt puts down.
SETD.0 SPText
CALL printString
PSHD.3
POPD.0
DPUP.0 0d14
PSHD.0
POPB
POPA
CALL printByteHex
PSHB
POPA
CALL printByteHex
CALL newLine CALL newLine
; Anything typed carries on. Reading the data port waits however the console is set, which ; Anything typed carries on. Reading the data port waits however the console is set, which
@@ -1936,7 +1981,7 @@ BRegText:
QRegText: QRegText:
"Q " "Q "
SRegText: SRegText:
"S " "status "
DP0Text: DP0Text:
"DP0 " "DP0 "
DP1Text: DP1Text:
@@ -1945,6 +1990,14 @@ DP2Text:
"DP2 " "DP2 "
DP3Text: DP3Text:
"DP3 " "DP3 "
SPText:
"SP "
CarryText:
"carry "
FaultText:
"fault "
IntsText:
"interrupts "
ResumeText: ResumeText:
"press a key " "press a key "
MonitorPrompt: MonitorPrompt:
+6 -2
View File
@@ -575,13 +575,17 @@ Those numbers are written down once, in `Programs/CosmOS/Source/services.asm`, w
``` ```
break at 200E break at 200E
A 11 B 22 Q 00 S 00 A 11 B 22 Q 00 status 00
DP0 1030 DP1 05AE DP2 039A DP3 2000 DP0 1030 DP1 05EF DP2 039A DP3 2000 SP FFFF
press a key press a key
``` ```
Every value comes out of the interrupt frame rather than out of the registers, because by the time the handler runs the registers belong to the handler. The frame is what the program had and what RETI is about to give back, so what is shown is what will be resumed with. The address is two before where it resumes: the `SWI` and the vector it names. Every value comes out of the interrupt frame rather than out of the registers, because by the time the handler runs the registers belong to the handler. The frame is what the program had and what RETI is about to give back, so what is shown is what will be resumed with. The address is two before where it resumes: the `SWI` and the vector it names.
**The Stack Pointer is the exception, because it is not in the frame** — the frame is *where* the Stack Pointer is. What the program had is fourteen bytes above the frame, that being what entering an interrupt puts down, so it is worked out rather than read. Breaking inside a subroutine shows it ten lower than breaking outside one, which is the size of a CALL frame and a quick way to see how deep you are.
The status byte is shown as a number and then as the bits that are up — `carry`, `fault`, `interrupts` — because a dump that makes you look the number up is only half a dump.
**Nothing is overwritten, and that is the whole of why it is simple.** A breakpoint poked into a running program has to replace an instruction, and putting that instruction back in order to continue is the same act as disarming the breakpoint. Firing a second time would mean stepping over the restored instruction and putting the breakpoint back behind it, and this machine has no way to step a single instruction. Two bytes of `SWI` cost a little space and fire for ever, because there was never anything to restore. **Nothing is overwritten, and that is the whole of why it is simple.** A breakpoint poked into a running program has to replace an instruction, and putting that instruction back in order to continue is the same act as disarming the breakpoint. Firing a second time would mean stepping over the restored instruction and putting the breakpoint back behind it, and this machine has no way to step a single instruction. Two bytes of `SWI` cost a little space and fire for ever, because there was never anything to restore.
The price is that a breakpoint is part of the program. A build with breakpoints in it has different addresses from a build without — the same bargain every machine makes that has a break instruction. The price is that a breakpoint is part of the program. A build with breakpoints in it has different addresses from a build without — the same bargain every machine makes that has a break instruction.
+5 -5
View File
@@ -2,12 +2,12 @@ CosmOS
> loaded, starting at 2000 > loaded, starting at 2000
> two stops, and what the registers were at each > two stops, and what the registers were at each
break at 200E break at 200E
A 11 B 22 Q 00 S 00 A 11 B 22 Q 00 status 00
DP0 1030 DP1 05CC DP2 039A DP3 2000 DP0 1030 DP1 05EF DP2 039A DP3 2000 SP FFFF
press a key press a key
break at 2018 break at 2023
A 44 B 55 Q 00 S 00 A 44 B 55 Q 00 status 00
DP0 1000 DP1 05CC DP2 039A DP3 2000 DP0 1000 DP1 05EF DP2 039A DP3 2000 SP FFF5
press a key press a key
carried on to the end carried on to the end
finished finished
+1 -1
View File
@@ -52,7 +52,7 @@ Life.sbx 1411
Snake.sbx 2175 Snake.sbx 2175
Keys.sbx 663 Keys.sbx 663
Say.sbx 155 Say.sbx 155
Break.sbx 128 Break.sbx 132
notes.txt 21 notes.txt 21
8 files 8 files
> halted > halted
+1 -1
View File
@@ -6,7 +6,7 @@ Life.sbx 1411
Snake.sbx 2175 Snake.sbx 2175
Keys.sbx 663 Keys.sbx 663
Say.sbx 155 Say.sbx 155
Break.sbx 128 Break.sbx 132
notes.txt 21 notes.txt 21
8 files 8 files
> load what? > load what?