Infrastructure for system services through software interrupts.

This commit is contained in:
Anachronaut
2026-08-19 15:16:30 -04:00
parent e3100b4718
commit c4b59acc68
5 changed files with 152 additions and 1 deletions
+12
View File
@@ -759,6 +759,18 @@ handlePrintString:
handleReadLine:
CALL readLine
; readLine works out how long the line was, and RETI would throw that away: it restores
; every register from the frame, which is exactly what makes an interrupt safe to arrive
; unannounced and exactly what stops a service answering. So the answer is written into
; the frame, over the saved Q, and RETI puts it back as though the caller had computed it.
;
; This has to be here rather than in a routine, because the offset is from where the
; Stack Pointer is now and a CALL moves it by ten.
MVQA
MVSD.1
DPUP.1 0d02
STA.1
RETI
; What the program was asked to work on. DP0 says where to put it and B how much room
+107
View File
@@ -0,0 +1,107 @@
; A software interrupt handing something back.
;
; RETI restores every register from the frame, which is what makes an interrupt safe to
; arrive at an arbitrary moment: the interrupted code cannot tell it happened. A SERVICE is
; not arbitrary - it was asked for - and the same rule means it has no way to answer.
;
; So a service that has something to say writes it INTO ITS OWN FRAME, over the saved
; register, and lets RETI put it back. MVSD is what makes the frame reachable: it copies
; the Stack Pointer into a Data Pointer, and the frame sits just above it.
;
; +1 Status +5 DP3 high +9 DP1 high +13 resume high
; +2 Q +6 DP3 low +10 DP1 low +14 resume low
; +3 A +7 DP2 high +11 DP0 high
; +4 B +8 DP2 low +12 DP0 low
;
; WHICH REGISTERS A SERVICE MAY ANSWER IN is a convention rather than a rule, and it is the
; same one CALL already has: Q and DP3. A subroutine cannot hand back A, B or DP0 to DP2
; because RET puts them back; a service could write over any of them, and should not, for
; exactly the reason the first list exists. A caller expects what it kept to still be there.
;
; ONLY THE HANDLER ITSELF CAN DO THIS. The offsets are from where the Stack Pointer is, and
; a CALL moves it by ten. A routine called by a handler that tried this would be writing
; into its own return address.
;
; Correct output is:
; quiet: 7 a service that says nothing leaves Q as it found it
; answer: 42 one that does, does not
; pointer: ABC and DP3 comes back the same way
#Include console.asm
#Program
start:
; Something recognisable in Q, so that a service leaving it alone is visible.
INIA 0d7
RSTB
CCF
ADD
SETD.0 QuietText
CALL printString
SWI quiet
MVQA
CALL printByteDecimal
CALL newLine
SETD.0 AnswerText
CALL printString
SWI answer
MVQA
CALL printByteDecimal
CALL newLine
SETD.0 PointerText
CALL printString
SWI pointer
PSHD.3
POPD.0
CALL printString
CALL newLine
HALT
; Says nothing, so whatever the caller had in Q is still there afterwards.
quiet:
INIA 0d99
RSTB
CCF
ADD ; Q is 99 in here, and nobody outside will ever know.
RETI
answer:
INIA 0d42
MVSD.1
DPUP.1 0d02 ; The saved Q.
STA.1
RETI
pointer:
SETD.0 Letters
MVSD.1
DPUP.1 0d05 ; The saved DP3, high byte first the way everything is stored.
PSHD.0
POPA ; The low half comes off the Stack first.
POPB
STB.1
INCD.1
STA.1
RETI
#Data
QuietText:
"quiet: "
AnswerText:
"answer: "
PointerText:
"pointer: "
Letters:
"ABC"
#Vectors
Boot start
quiet quiet
answer answer
pointer pointer