; 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 ; sret: 42 and SRET says the same thing without touching the frame ; sretptr: ABC for a pointer too ; sretkept: 7 while everything a RET would restore still comes back ; ; ---- The frame editing above is what SRET exists to replace ---- ; ; Everything between MVSD and RETI in "answer" and "pointer" is a routine reaching into its ; own frame to un-save two fields, using offsets it has to know by heart. Thirty places in ; CosmOS did that, and all thirty would have gone quietly wrong the day the frame gained a ; field. SRET is the same instruction sequence as RETI with the saved Q and DP3 stepped ; over instead of restored, so a handler answers the way a subroutine does and nothing ; below has to know what a frame looks like. ; ; Both are kept here on purpose. RETI is still how a HARDWARE handler says it was never ; there, and a service that has nothing to say should still use it. #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 ; ---- And the same three answers, given with SRET instead ---- SETD.0 SretText CALL printString SWI sretAnswer MVQA CALL printByteDecimal CALL newLine SETD.0 SretPtrText CALL printString SWI sretPointer PSHD.3 POPD.0 CALL printString CALL newLine ; What SRET must still put back. A handler that trampled A, B and DP0 to DP2 would break ; every caller, which is exactly why RET restores them - so SRET does too, and the number ; printed here is the caller's own, loaded before the service was asked for anything. INIA 0d7 RSTB CCF ADD ; Q is the caller's seven, SETD.0 SretKeptText ; and DP0 names the label, BEFORE the service is asked. SWI sretTrample ; Printed after the call rather than before it, which is the whole of the check: the ; handler pointed DP0 somewhere else, so this says "sretkept" only if SRET brought the ; caller's own pointer back. Printing first would have tested nothing at all. CALL printString MVQA CALL printByteDecimal 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 ; ---- The same answers, without the frame ---- ; ; Q and DP3 are set the way any subroutine sets them, and SRET leaves them alone. There is ; no MVSD, no offset, and nothing here that would need revisiting if the frame changed. sretAnswer: INIA 0d42 RSTB CCF ADD SRET sretPointer: SETD.3 Letters SRET ; Sets everything a RET would restore to something wrong, to show that SRET restores it. ; If any of these came back, the caller's 7 would not. sretTrample: INIA 0d99 INIB 0d98 SETD.0 Letters SETD.1 Letters SETD.2 Letters SRET #Data QuietText: "quiet: " AnswerText: "answer: " PointerText: "pointer: " SretText: "sret: " SretPtrText: "sretptr: " SretKeptText: "sretkept: " Letters: "ABC" #Vectors Boot start quiet quiet answer answer pointer pointer sretAnswer sretAnswer sretPointer sretPointer sretTrample sretTrample