Infrastructure for system services through software interrupts.
This commit is contained in:
@@ -759,6 +759,18 @@ handlePrintString:
|
|||||||
|
|
||||||
handleReadLine:
|
handleReadLine:
|
||||||
CALL readLine
|
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
|
RETI
|
||||||
|
|
||||||
; What the program was asked to work on. DP0 says where to put it and B how much room
|
; What the program was asked to work on. DP0 says where to put it and B how much room
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -552,7 +552,7 @@ Those numbers are written down once, in `Programs/CosmOS/Source/services.asm`, w
|
|||||||
| Service | Does |
|
| Service | Does |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| osPrintString | DP0 names a string ending in a zero byte. Prints it. |
|
| osPrintString | DP0 names a string ending in a zero byte. Prints it. |
|
||||||
| osReadLine | DP0 names somewhere to put a line, B says how much room there is. Reads one from the console. |
|
| osReadLine | DP0 names somewhere to put a line, B says how much room there is. Reads one from the console. Q comes back holding how long it was. |
|
||||||
| osExit | Gives the machine back. Does not return. |
|
| osExit | Gives the machine back. Does not return. |
|
||||||
| osArgument | DP0 names somewhere to put whatever followed the run command, B says how much room there is. |
|
| osArgument | DP0 names somewhere to put whatever followed the run command, B says how much room there is. |
|
||||||
|
|
||||||
@@ -567,6 +567,27 @@ Those numbers are written down once, in `Programs/CosmOS/Source/services.asm`, w
|
|||||||
|
|
||||||
A handler is entered with the caller's registers exactly as they were, because an interrupt frame is pushed rather than cleared. That is why a service can be given a pointer in DP0 and a count in B without any of it being copied anywhere first.
|
A handler is entered with the caller's registers exactly as they were, because an interrupt frame is pushed rather than cleared. That is why a service can be given a pointer in DP0 and a count in B without any of it being copied anywhere first.
|
||||||
|
|
||||||
|
### How A Service Answers:
|
||||||
|
|
||||||
|
The same thing that makes an interrupt safe makes a service mute. RETI restores every register from the frame, so whatever a handler worked out is thrown away on the way out — which is exactly right for a device interrupting at a moment nobody chose, and useless for a service that was asked a question.
|
||||||
|
|
||||||
|
A service answers by **writing into its own frame**, over the saved register, and letting RETI put it back. MVSD copies the Stack Pointer into a Data Pointer and the frame sits just above it, so returning a byte in Q is three instructions:
|
||||||
|
|
||||||
|
```
|
||||||
|
answer:
|
||||||
|
INIA 0d42
|
||||||
|
MVSD.1
|
||||||
|
DPUP.1 0d02 ; The saved Q. See the frame table under Interrupts.
|
||||||
|
STA.1
|
||||||
|
RETI
|
||||||
|
```
|
||||||
|
|
||||||
|
**Which registers a service may answer in is the convention CALL already has: Q and DP3.** A subroutine cannot hand back A, B or Data Pointers 0 to 2 because RET puts them back; a service *could* write over any of them and should not, for exactly the reason that list exists. A caller is entitled to find what it kept still there.
|
||||||
|
|
||||||
|
**Only the handler itself can do this.** The offsets are from wherever the Stack Pointer is, and a CALL moves it by ten — so a routine called by a handler that tried the same thing would be writing into its own return address. The poke belongs inline, next to the RETI.
|
||||||
|
|
||||||
|
A service that has nothing to say does nothing, and the caller's registers arrive back untouched. That is worth knowing from the other side too: a service cannot corrupt a register by accident, only by deciding to.
|
||||||
|
|
||||||
## Loading A Program From A Disk:
|
## Loading A Program From A Disk:
|
||||||
|
|
||||||
A program that was not the one the machine booted from carries sixteen bytes in front of it saying where it belongs.
|
A program that was not the one the machine booted from carries sixteen bytes in front of it saying where it belongs.
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
quiet: 7
|
||||||
|
answer: 42
|
||||||
|
pointer: ABC
|
||||||
|
Execution halted.
|
||||||
|
[exit 0]
|
||||||
@@ -141,6 +141,12 @@ vectorTest | testPrograms/vectorTest.asm | run | -
|
|||||||
# it. This pins both ends of that range and leaves a third to the assembler, then calls all
|
# it. This pins both ends of that range and leaves a third to the assembler, then calls all
|
||||||
# three by name: a number going astray shows up as the wrong word rather than as silence.
|
# three by name: a number going astray shows up as the wrong word rather than as silence.
|
||||||
pinnedVectorTest | testPrograms/pinnedVectorTest.asm | run | - | -
|
pinnedVectorTest | testPrograms/pinnedVectorTest.asm | run | - | -
|
||||||
|
# A service handing something back, which RETI otherwise makes impossible: it restores every
|
||||||
|
# register from the frame, so a handler that worked something out has no way to say so. The
|
||||||
|
# answer is written into the frame over the saved register. All three cases are here - a
|
||||||
|
# service that says nothing and leaves Q exactly as it found it, one that answers in Q, and
|
||||||
|
# one that answers in DP3 - because the first is what makes the other two safe to rely on.
|
||||||
|
serviceReturnTest | testPrograms/serviceReturnTest.asm | run | - | -
|
||||||
deviceTest | testPrograms/deviceTest.asm | run | - | -
|
deviceTest | testPrograms/deviceTest.asm | run | - | -
|
||||||
faultResumeTest | testPrograms/faultResumeTest.asm | run | - | -
|
faultResumeTest | testPrograms/faultResumeTest.asm | run | - | -
|
||||||
# The worked example out of the Assembler Manual, so the manual cannot go stale.
|
# The worked example out of the Assembler Manual, so the manual cannot go stale.
|
||||||
|
|||||||
Reference in New Issue
Block a user