SRET: a handler answers the way a subroutine does

CALL saves A, B and Data Pointers 0 to 2 and nothing else, which is exactly
why Q and DP3 are how a subroutine hands something back. An interrupt saves
all of it, so a service with an answer had to reach into its own frame and
un-save two fields by hand:

  MVSD.2
  DPUP.2 0d02           ; the saved Q, by an offset it had to know
  STA.2
  RETI

Thirty places in CosmOS did that. Every one knew the frame's layout by
heart, and all thirty would have gone quietly wrong the day the frame
gained a field - the same duplicated fact this project keeps being bitten
by, except duplicated into thirty places AND into the CPU.

SRET is 0x76, in the seat the block split left for it. It is RETI's frame
with RET's rule applied: A, B and DP0 to DP2 come back, the saved Q and DP3
are dropped, and the Interrupt Flag is restored from the frame - only that
bit, so carry survives a service the way it survives a call, and there is
one rule rather than two. RETI stays exactly as it was: a hardware handler
has nothing to say and must leave no trace.

CosmOS is 10,969 bytes against 11,122, and no handler knows a frame offset.

TWO MISTAKES WORTH RECORDING, both mine, both caught by tests.

The first conversion matched STA.2 with a regular expression that did not
allow a trailing comment, so it ran past the end of one handler and into
the next. The second understood the pattern and still got it wrong: the old
frame write carried the answer from A into the saved Q slot, so simply
deleting the write left Q holding whatever it happened to hold. Services
that answer by calling something were fine - Q already had it - and
services that set A directly silently reported success for every failure.
cosmosCwd is what noticed, by saying "cannot go there" about a directory
that was there. Sixteen handlers move the answer into Q now.

Seven MVQA went with it. They copied Q into A so the frame write could
carry it; SRET puts A back, so they moved a value nobody would ever read.
This commit is contained in:
Anachronaut
2026-08-27 18:18:36 -04:00
parent cd5f548736
commit c8c9f0b363
9 changed files with 275 additions and 133 deletions
+106 -130
View File
@@ -1011,19 +1011,15 @@ handleFileStart:
; What a name means on the disk is about to change, so the remembered file goes.
CALL fileForget
CALL sbfsStreamStart
MVQA
MVSD.2
DPUP.2 0d02
STA.2
RETI
SRET
fileStartNoDisk:
POPA
MVSD.2
DPUP.2 0d02
INIA 0d1
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
; DP1 is where the block comes from, and A and B together say which block of the file it
; is, counting from zero - the same way osFileBlock is told which one to fetch.
@@ -1033,11 +1029,7 @@ handleFileWrite:
INCD.2
STB.2
CALL sbfsStreamWrite
MVQA
MVSD.2
DPUP.2 0d02
STA.2
RETI
SRET
; DP1 is where the block goes, and A and B together say which one, the same way writing is
; told. Reads back a block of the file being written.
@@ -1047,11 +1039,7 @@ handleFileFetch:
INCD.2
STB.2
CALL sbfsStreamFetch
MVQA
MVSD.2
DPUP.2 0d02
STA.2
RETI
SRET
; DP3 is how many whole blocks it came to and A is what is left over, told the same way
; osFileStart is told. It need not be what was asked for: a writer that cannot know its
@@ -1071,11 +1059,7 @@ handleFileDone:
STB.2
CALL fileForget
CALL sbfsStreamDone
MVQA
MVSD.2
DPUP.2 0d02
STA.2
RETI
SRET
; ---- osChangeDir ----
;
@@ -1115,18 +1099,18 @@ changeDirTake:
SETD.1 SbfsCwd
CALL sbfsCopyWord
MVSD.2
DPUP.2 0d02
RSTA
STA.2 ; Q is zero: the machine is there now.
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
changeDirNo:
MVSD.2
DPUP.2 0d02
INIA 0d1
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
; ---- cd ----
;
@@ -1776,24 +1760,25 @@ handleFileRead:
SETD.2 SbfsFileTail
LDB.2
MVSD.2
DPUP.2 0d05 ; The saved DP3, high byte first.
STA.2
INCD.2
STB.2
MVSD.2
DPUP.2 0d02 ; And the saved Q.
; A two byte answer, and DP3 is where a routine hands one back. Built with the
; Stack rather than written into the frame: SRET leaves DP3 alone, so there is
; nothing to reach into. PSHA then PSHB puts the high byte above the low one,
; which is the order POPD reads them in.
PSHA
PSHB
POPD.3
RSTA
STA.2
RETI
RSTB
CCF
ADD ; Q is zero: it is there.
SRET
fileReadNo:
MVSD.2
DPUP.2 0d02
INIA 0d1
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
; DP0 names the file, DP1 is the bytes, and A and B together are how many. Q is zero if it
; saved. Whether it was there before makes no difference, which is what saving means.
@@ -1818,19 +1803,15 @@ handleFileSave:
; What a name means on the disk is about to change, so the remembered file goes.
CALL fileForget
CALL sbfsSaveFile
MVQA
MVSD.2
DPUP.2 0d02
STA.2
RETI
SRET
fileSaveNoDisk:
POPA
MVSD.2
DPUP.2 0d02
INIA 0d1
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
; DP0 names it. Q is zero if it went.
handleFileDelete:
@@ -1840,11 +1821,7 @@ handleFileDelete:
; What a name means on the disk is about to change, so the remembered file goes.
CALL fileForget
CALL sbfsDelete
MVQA
MVSD.2
DPUP.2 0d02
STA.2
RETI
SRET
; DP0 is the name it has, DP1 the name it should have. Q is zero if it moved.
handleFileRename:
@@ -1854,18 +1831,14 @@ handleFileRename:
; What a name means on the disk is about to change, so the remembered file goes.
CALL fileForget
CALL sbfsRename
MVQA
MVSD.2
DPUP.2 0d02
STA.2
RETI
SRET
serviceNoDisk:
MVSD.2
DPUP.2 0d02
INIA 0d1
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
; ---- Reading a file that will not fit ----
;
@@ -1978,31 +1951,32 @@ handleFileInfo:
INCD.2
LDB.2
MVSD.2
DPUP.2 0d05 ; The saved DP3, high byte first.
STA.2
INCD.2
STB.2
MVSD.2
DPUP.2 0d02
; A two byte answer, and DP3 is where a routine hands one back. Built with the
; Stack rather than written into the frame: SRET leaves DP3 alone, so there is
; nothing to reach into. PSHA then PSHB puts the high byte above the low one,
; which is the order POPD reads them in.
PSHA
PSHB
POPD.3
RSTA
STA.2 ; And the saved Q: it is there.
RETI
RSTB
CCF
ADD ; Q is zero: it is there.
SRET
fileInfoNoDisk:
MVSD.2
DPUP.2 0d02
INIA 0d1
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
fileInfoMissing:
MVSD.2
DPUP.2 0d02
INIA 0d2
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
; DP0 names it, DP1 says where to put it, and A and B together are which block, counting
; from zero. Q is zero if it read, and DP3 comes back holding how many of the block's bytes
@@ -2053,45 +2027,46 @@ fileBlockWhole:
RSTB
fileBlockAnswer:
MVSD.2
DPUP.2 0d05
STA.2
INCD.2
STB.2
MVSD.2
DPUP.2 0d02
; A two byte answer, and DP3 is where a routine hands one back. Built with the
; Stack rather than written into the frame: SRET leaves DP3 alone, so there is
; nothing to reach into. PSHA then PSHB puts the high byte above the low one,
; which is the order POPD reads them in.
PSHA
PSHB
POPD.3
RSTA
STA.2
RETI
RSTB
CCF
ADD ; Q is zero: it is there.
SRET
fileBlockNoDisk:
MVSD.2
DPUP.2 0d02
INIA 0d1
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
fileBlockMissing:
MVSD.2
DPUP.2 0d02
INIA 0d2
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
fileBlockPastEnd:
MVSD.2
DPUP.2 0d02
INIA 0d3
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
fileBlockFailed:
MVSD.2
DPUP.2 0d02
INIA 0d4
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
; A breakpoint. Shows every register as the interrupted program had them, waits for a key,
; and returns as though nothing happened.
@@ -2291,18 +2266,18 @@ handleBootState:
BNQ bootStateNone
SETD.2 SbfsStateWas
LDA.2
MVSD.2
DPUP.2 0d02
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
bootStateNone:
; No disk, or one that would not answer. Nothing there to be unsettled about.
MVSD.2
DPUP.2 0d02
RSTA
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
handleBootSettle:
SETD.2 DiskReady
@@ -2312,18 +2287,18 @@ handleBootSettle:
RSTA
CALL sbfsSetBootState
BNQ bootSettleNo
MVSD.2
DPUP.2 0d02
RSTA
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
bootSettleNo:
MVSD.2
DPUP.2 0d02
INIA 0d1
STA.2
RETI
RSTB
CCF
ADD ; A is the answer, so Q becomes it.
SRET
handleExit:
SETD.1 SystemStack
@@ -3829,7 +3804,7 @@ ShapeLength:
0d1 0d3 0d2 0d2 0d3 0d4 0d3
InstructionCount:
0d71
0d72
; ---- The instruction table ----
;
@@ -3862,6 +3837,7 @@ Instructions:
0x73 0d0 "RETI"
0x74 0d0 "RRET"
0x75 0d0 "RET "
0x76 0d0 "SRET"
0x20 0d0 "RSTA"
0x21 0d0 "RSTB"
0x22 0d0 "INCA"