Programs can now list and share vectors.

This commit is contained in:
Anachronaut
2026-08-17 21:39:27 -04:00
parent a842c884e8
commit 1d1a14318c
18 changed files with 537 additions and 27 deletions
+101
View File
@@ -0,0 +1,101 @@
; A loaded program that is interrupted by the console rather than asking it for anything.
;
; Until the loadable program format could carry vectors, this could not be written. A
; handler has to be an address in the vector table, and a program that is not the one the
; machine booted from had no way to say what its vectors were, so interrupts belonged to
; boot images and every loaded program had to poll. Snake polls for exactly that reason.
;
; What this shows is the whole path: the assembler writes the vectors into the file, the
; system installs them when the program is run, the console raises its line, the handler
; runs, and the system takes the vectors back out again when the program gives the machine
; back. Nothing in the waiting loop below looks at the console at all.
;
; The vector is named by port, because a device interrupts on the port it is plugged into
; and the console is on port 0x00.
#Include services.asm
#Program
#Base 0x2000
start:
CIF ; Nothing arrives until there is something to catch it.
; Set rather than trusted to be zero. Running a program a second time does not load it
; again, so its Data Segment is exactly as the last run left it - and the last thing the
; last run did was set this.
RSTA
SETD.0 Stopping
STA.0
SETD.0 Banner
CALL printString
CALL newLine
; Key mode and interrupt on input, in one write, since the two control bits are
; independent of each other.
INIA 0x03
OUTA 0x02
SIF
wait:
; This loop is the point. It never touches the console, so every character that appears
; below was put there by something that interrupted it.
SETD.3 Stopping
LDA.3
RSTB
OR
BRQ wait
CALL newLine
RSTA
OUTA 0x02 ; Line mode and no interrupts, the way it was found.
SETD.0 DoneText
CALL printString
CALL newLine
SWI osExit
; Entered because the console had something to say. Never called.
keyHandler:
INA 0x01
INIB 0x01 ; READY: is there a byte, as opposed to the end of input?
AND
BRQ keyNoByte
INA 0x00
INIB 0x71 ; q, which is how this program is stopped.
XOR
BRQ keyStop
OUTA 0x00 ; Nothing echoes in key mode, so the handler does it.
RETI
keyNoByte:
; The end of input raises the line once as well, so a program driven entirely by
; interrupts is told when nothing more is coming instead of waiting for ever.
keyStop:
SETD.3 Stopping
INIA 0x01
STA.3
RETI
#Data
#Base 0x1000
Banner:
"keys, by interrupt. q stops."
DoneText:
"the console has been handed back"
; The only thing the handler and the loop it interrupts have to say to each other.
Stopping:
0x00
#Vectors
Boot start
Device 0x00 keyHandler
#Include console.asm
+9 -7
View File
@@ -9,13 +9,15 @@
; console holds the next key until it is asked, so nothing typed between frames is lost,
; and a script of moves plays back one move to a frame.
;
; WHY IT POLLS RATHER THAN INTERRUPTS. The console can raise an interrupt line when a byte
; arrives, which is the better shape for a game: the loop would never look at the console
; at all. A loaded program cannot use it. Installing a handler means putting an address in
; the vector table, and the loadable program format carries only code and data - a program
; that is not the one the machine booted from has no way to say what its vectors are. So
; interrupts belong to boot images for now, and this asks once a frame, which is what the
; machines this one is pretending to be did anyway.
; WHY IT POLLS RATHER THAN INTERRUPTS. When this was written a loaded program could not be
; interrupted at all: installing a handler means putting an address in the vector table,
; and the loadable format carried only code and data, so a program that was not the one the
; machine booted from had no way to say what its vectors were. That is no longer true - the
; format carries them now, and Keys.asm is the program that shows it.
;
; This still polls, and now by choice. Asking once a frame is what the machines this one is
; pretending to be actually did, it is the shape a game with a frame loop wants anyway, and
; having one of each in the same Apps directory is worth more than having two the same.
;
; THE BOARD IS A PAGE, and that is the whole trick this program turns on. Sixteen by
; sixteen is 256 squares, so a square number is a byte, and the board is aligned so that
+233
View File
@@ -279,12 +279,24 @@ loadMagicLoop:
STA.1
BNA loadMagicLoop
; Version one is code and data. Version two also brings vectors, which is a thing a
; loader has to know how to do rather than a detail it can skip: a program whose handlers
; were quietly dropped would run and then go wrong somewhere with nothing to connect it
; back to here. Anything else is refused.
SETD.0 0x80 0x00
DPUP.0 0d04
LDA.0
SETD.1 LoadVersion
STA.1
INIB 0d1
XOR
BRQ loadVersionKnown
SETD.1 LoadVersion
LDA.1
INIB 0d2
XOR
BNQ loadWrongVersion
loadVersionKnown:
; The code. It comes from the staging area just past the sixteen byte header, and goes
; wherever the header says, in Program Memory, which the instruction set cannot write
@@ -340,6 +352,75 @@ loadMagicLoop:
INIA 0x01
OUTA 0xE8 ; Blit.
; ---- The vectors it brought ----
;
; Kept here rather than installed. A vector points into a program, so it has no business
; being in the table while that program is only loaded and not running: run puts them in
; and exit takes them out again, so the window they are live in is exactly the run.
; Keeping our own copy is also what lets a program be run more than once, since the
; staging area it came in on is fair game for the program's own use.
;
; Where to read them from is not worked out. The data blit left the controller's source
; address on the first byte after the data, which is where they are, so it is read back.
SETD.0 VectorSource
INA 0xE1
STA.0
INCD.0
INA 0xE2
STA.0
SETD.0 0x80 0x00
DPUP.0 0d05
LDA.0
SETD.1 LoadedVectorCount
STA.1
BRA loadVectorsCopied
; More than there is room for is refused rather than half taken. Half a program's
; handlers is not a smaller version of that program.
INIB 0d17
CCF
SUB
BNC loadTooManyVectors
SETD.0 VectorSource
LDD.2.0 ; DP2 walks the entries where they are staged.
SETD.3 LoadedVectors ; DP3 walks our own copy of them.
SETD.1 LoadedVectorCount
LDA.1
SETD.1 VectorsLeft
STA.1
loadVectorCopy:
; Four bytes: where it goes, then what goes there. The two bytes for what was there
; before are left alone until something is actually put in.
LDA.2
STA.3
INCD.2
INCD.3
LDA.2
STA.3
INCD.2
INCD.3
LDA.2
STA.3
INCD.2
INCD.3
LDA.2
STA.3
INCD.2
INCD.3
INCD.3
INCD.3
SETD.1 VectorsLeft
LDA.1
DECA
STA.1
BNA loadVectorCopy
loadVectorsCopied:
; Where it starts. Written out by hand rather than through a routine, because a routine
; could not hand two bytes back: CALL puts A, B and the first three pointers back the
; way it found them.
@@ -370,6 +451,9 @@ loadNoDisk:
loadNothingNamed:
SETD.0 LoadWhat
BRI loadComplain
loadTooManyVectors:
SETD.0 TooManyVectors
BRI loadComplain
loadMissing:
SETD.0 NoSuchFile
BRI loadComplain
@@ -401,6 +485,8 @@ doRun:
SETD.1 SystemStack
STD.0.1
CALL installVectors
; The entry address is a number until BRD makes it a place. DP3 is the one to build it
; in, because it is the pointer nothing puts back.
SETD.1 LoadedEntry
@@ -413,6 +499,112 @@ runNothing:
CALL newLine
BRI prompt
; ---- Putting a program's vectors in, and taking them out again ----
;
; The vector table lives in Program Memory, which no instruction can write, so both of
; these go through the memory controller. Port 0xE9 reads a byte from the source and writes
; a byte to the destination, stepping the address on either way, so a two byte entry is two
; reads or two writes and no address arithmetic in between.
;
; What was in the slot is kept before anything replaces it, and put back afterwards, rather
; than the slot being cleared. Clearing would be wrong wherever a program has installed a
; handler over one the system was already using: the program is allowed to do that, and
; when it goes, what it covered up has to come back rather than becoming a hole.
installVectors:
SETD.0 LoadedVectorCount
LDA.0
BRA installDone
SETD.1 VectorsLeft
STA.1
SETD.3 LoadedVectors
installOne:
; DP3 walks one six byte entry: where it goes, what goes there, and room for what was
; there before. Reading and writing the same slot, so the controller is pointed at it
; from both ends at once and the address is only worked out once.
RSTA
OUTA 0xE0 ; SourceBank: Program Memory.
OUTA 0xE3 ; DestBank: the same.
LDA.3
OUTA 0xE1
OUTA 0xE4
INCD.3
LDA.3
OUTA 0xE2
OUTA 0xE5
INCD.3 ; On the handler.
; What is there now, before anything replaces it.
INA 0xE9
PSHA
INA 0xE9
PSHA
; And the handler in its place.
LDA.3
OUTA 0xE9
INCD.3
LDA.3
OUTA 0xE9
INCD.3 ; On the two bytes kept for what was there before.
; The Stack gives them back in the reverse of the order they went on, so the low byte
; arrives first and is written to the second of the two. Getting this the natural way
; round instead put the low byte where the high one goes and the high byte over the
; handler, which the first run of a program survives - the table is already written by
; then - and the second run does not.
POPA
INCD.3
STA.3
DECD.3
POPA
STA.3
INCD.3
INCD.3
SETD.1 VectorsLeft
LDA.1
DECA
STA.1
BNA installOne
installDone:
RET
removeVectors:
SETD.0 LoadedVectorCount
LDA.0
BRA removeDone
SETD.1 VectorsLeft
STA.1
SETD.3 LoadedVectors
removeOne:
RSTA
OUTA 0xE3 ; DestBank: Program Memory.
LDA.3
OUTA 0xE4
INCD.3
LDA.3
OUTA 0xE5
INCD.3
INCD.3
INCD.3 ; Past the handler, to what was underneath it.
LDA.3
OUTA 0xE9
INCD.3
LDA.3
OUTA 0xE9
INCD.3
SETD.1 VectorsLeft
LDA.1
DECA
STA.1
BNA removeOne
removeDone:
RET
; ---- The services ----
;
; These are what a loaded program is allowed to ask for. The names and their numbers come
@@ -422,6 +614,19 @@ runNothing:
; A handler arrives with the caller's registers exactly as they were: an interrupt frame
; is pushed, not cleared. So the pointer a program put in DP0 is still there to be used.
; The disk finishing, acknowledged and ignored.
;
; The system drives the disk by asking its status port and waiting, so it has no use for
; the line. But the disk raises one after every operation whether anybody wants it or not,
; and a line goes on waiting while the Interrupt Flag is down rather than being lost. The
; shell keeps the flag down, so the line from the last disk read was still standing when
; the first program to enable interrupts ran, and it arrived there - a fault, in a program
; that had never heard of the disk, blamed on the innocent instruction that let it through.
;
; Answering a line is what takes it down, so this is one instruction and that is the point.
diskDone:
RETI
handlePrintString:
CALL printString
RETI
@@ -441,6 +646,11 @@ handleExit:
LDD.0.1
MVDS.0
; Whatever the program put in the vector table comes out again. A vector points into the
; program that supplied it, and the program is gone, so anything left installed would aim
; an interrupt at whatever those addresses hold next.
CALL removeVectors
; The console goes back to how the shell wants it, whatever the program left it in: line
; mode, and not interrupting. A program that wanted either is expected to put it back
; itself, but one that stopped early, or forgot, would otherwise hand back a shell with
@@ -713,6 +923,8 @@ LoadWhat:
"load what?"
NoSuchFile:
"no such file"
TooManyVectors:
"that program wants more vectors than there is room for"
Unreadable:
"could not read it"
NotProgram:
@@ -745,6 +957,26 @@ LoadedEntry:
0x00 0x00
LoadCount:
0x00
LoadVersion:
0x00
; ---- The vectors a loaded program brought with it ----
;
; Six bytes each: where it goes, what goes there, and what was there before. The last two
; are filled in when the program runs and read back when it exits, so what a program covers
; up comes back rather than becoming a hole.
;
; Sixteen is a limit rather than a considered number. It is far more than anything written
; so far wants, and a program asking for more is refused at load rather than having some of
; its handlers installed and the rest dropped.
VectorSource:
0x00 0x00
VectorsLeft:
0x00
LoadedVectorCount:
0x00
LoadedVectors:
#Reserve 0d96
; Where the monitor is looking, so that a bare 'dump' can carry on from it.
DumpBank:
@@ -781,3 +1013,4 @@ CommandLine:
osPrintString handlePrintString
osReadLine handleReadLine
osExit handleExit
Device 0x20 diskDone