Files
Anachronaut c8c9f0b363 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.
2026-08-27 18:18:36 -04:00

115 lines
3.2 KiB
NASM

; The instruction set, as the assembler needs to see it.
;
; A SECOND COPY, and it is worth saying why rather than hoping nobody notices. The monitor
; has one of these in cosmos.asm, and the assembler cannot use it: the monitor's copy lives
; in the system's data at an address that moves every time CosmOS is rebuilt, and there is
; no linker to reach it by name. So the assembler carries its own 448 bytes. That is the
; cost of having no libraries, paid where it is cheapest to pay.
;
; Both copies are generated by Tests/instructiontable.py from the C assembler's own list,
; and Tests/docs.sh checks both against it. Neither can drift without the suite saying so.
;
; Seven bytes an entry: the opcode, the shape, and four characters of name with the zero
; the assembler puts after a string. Every mnemonic is four characters or fewer, so a name
; padded to four is an exact match rather than a prefix.
;
; It goes in the DATA Segment, because the assembler has to read it and an instruction can
; only read Data Memory. A table in Program Memory could not be reached by the program
; holding it, except through the memory controller.
#Data
; How many bytes an instruction of each shape runs to, the opcode included. The assembler
; does not use this to size a token - the operand that follows is a token of its own and
; carries its own length - but it is what says an instruction is well formed.
AsmShapeLength:
0d1 0d3 0d2 0d2 0d3 0d4 0d3
; How many Data Pointer selectors an instruction of each shape names. This is what the
; assembler needs: a selector is part of the mnemonic rather than a token after it, so it
; is the one thing about an instruction's length that is not settled by the opcode alone.
;
; 0 no operand 4 a selector and a byte
; 1 an address 5 a selector and an address, which is SETD
; 2 a byte 6 two selectors, which is LDD and STD
; 3 a selector
AsmShapeSelectors:
0d0 0d0 0d0 0d1 0d1 0d1 0d2
AsmInstructionCount:
0d72
AsmInstructions:
0x10 0d0 "ADD "
0x11 0d0 "SUB "
0x12 0d0 "AND "
0x13 0d0 "OR "
0x14 0d0 "XOR "
0x15 0d0 "NOTA"
0x16 0d0 "NOTB"
0x17 0d0 "SHL "
0x18 0d0 "SHR "
0x60 0d1 "BRI "
0x61 0d1 "BRQ "
0x62 0d1 "BRA "
0x63 0d1 "BRB "
0x64 0d1 "BRC "
0x65 0d3 "BRD "
0x66 0d1 "BNQ "
0x67 0d1 "BNA "
0x68 0d1 "BNB "
0x69 0d1 "BNC "
0x70 0d1 "RCAL"
0x71 0d1 "CALL"
0x72 0d2 "SWI "
0x73 0d0 "RETI"
0x74 0d0 "RRET"
0x75 0d0 "RET "
0x76 0d0 "SRET"
0x20 0d0 "RSTA"
0x21 0d0 "RSTB"
0x22 0d0 "INCA"
0x23 0d0 "INCB"
0x24 0d0 "DECA"
0x25 0d0 "DECB"
0x26 0d2 "INIA"
0x27 0d2 "INIB"
0x28 0d0 "CCF "
0x29 0d0 "MVQA"
0x2A 0d0 "MVQB"
0x2B 0d0 "SIF "
0x2C 0d0 "CIF "
0x30 0d0 "PSHQ"
0x31 0d0 "PSHA"
0x32 0d0 "PSHB"
0x33 0d3 "PSHD"
0x34 0d0 "POPA"
0x35 0d0 "POPB"
0x36 0d3 "POPD"
0x40 0d3 "INCD"
0x41 0d3 "DECD"
0x42 0d3 "LDA "
0x43 0d3 "LDB "
0x44 0d3 "STQ "
0x45 0d3 "STA "
0x46 0d3 "STB "
0x47 0d5 "SETD"
0x48 0d4 "DPUP"
0x49 0d4 "DPDN"
0x4A 0d6 "LDD "
0x4B 0d6 "STD "
0x4C 0d3 "MVSD"
0x4D 0d3 "MVDS"
0x4E 0d3 "DPUA"
0x4F 0d3 "DPDA"
0x50 0d3 "DPUW"
0x51 0d3 "DPDW"
0xD0 0d2 "OUTQ"
0xD1 0d2 "OUTA"
0xD2 0d2 "OUTB"
0xE0 0d2 "INA "
0xE1 0d2 "INB "
0xF0 0d0 "NOP "
0xFE 0d0 "WAIT"
0xFF 0d0 "HALT"