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:
@@ -45,6 +45,7 @@ It has ten registers:
|
||||
- Bit 0 is the Carry/Borrow Flag. Any arithmetic operation either sets or clears it depending on whether or not the result causes Q to overflow/underflow. It is a 1 if a carry/underflow occurred, and a 0 otherwise. If A or B overflows or underflows from the use of an increment or decrement instruction, this flag will also be set. Non-overflowing increments or decrements will also reset it.
|
||||
- Bit 1 is the Fault Flag. It is set when the CPU cannot get past something and no handler was installed to deal with it: a byte that is not an instruction, or a dispatch through an empty vector. See Faults.
|
||||
- Bit 2 is the Interrupt Flag. It is set by SIF and cleared by CIF. While it is set the CPU answers devices asking for attention; while it is clear they wait. Arriving at a handler clears it, and RETI restores it along with the rest of the Status register. See Hardware Interrupts.
|
||||
- RETI restores this register whole; SRET restores only the Interrupt Flag from it and leaves the rest as the handler left it, so a service can answer in the Carry Flag the same way a subroutine can.
|
||||
- Bit 7 is the Halt Flag. It is set by the HALT instruction, and by a fault.
|
||||
- There is no Wait Flag. The CPU stopped in a WAIT is stopped in a way no program can see, precisely because this register is saved and restored across an interrupt and a wait must not be. See WAIT under Special Operations.
|
||||
|
||||
@@ -91,7 +92,7 @@ The Bytes column is the total length of the instruction, counting its opcode, an
|
||||
| 68 | BNB | 3 | Branch if B is not zero. |
|
||||
| 69 | BNC | 3 | Branch if the Carry Flag is clear. |
|
||||
|
||||
### Subroutine Operations: 6 Instructions
|
||||
### Subroutine Operations: 7 Instructions
|
||||
|
||||
A block of their own, because the branches and these outgrew one nibble between them. Each
|
||||
raw form sits immediately below the ordinary one it cannot be mixed with - RCAL under CALL,
|
||||
@@ -105,6 +106,7 @@ machine somewhere nobody named.
|
||||
| 72 | SWI | 2 | Software Interrupt. The next byte names a software vector. Pushes an interrupt frame and dispatches through it. Never masked. |
|
||||
| 73 | RETI | 1 | Return from an interrupt. Restores everything the frame holds and carries on from where the interrupt arrived. |
|
||||
| 74 | RRET | 1 | Return from a raw call. Takes back the Program Counter and nothing else. |
|
||||
| 76 | SRET | 1 | Return from a handler that has an answer. Restores what RET restores - A, B and Data Pointers 0 through 2 - and puts the Interrupt Flag back from the frame. The saved Q and Data Pointer 3 are dropped, so what the handler left in them is what the caller receives. |
|
||||
| 75 | RET | 1 | Return from subroutine. Restores A, B, and Data Pointers 0 through 2 from the Stack, then sets the Program Counter to the instruction after the CALL. Data Pointer 3 and Q are left as the subroutine leaves them. |
|
||||
|
||||
### Register Operations: 13 Instructions
|
||||
|
||||
Reference in New Issue
Block a user