M3: programs that bring their own vectors
> load Asm.sbx
> run Keys.asm
wrote Keys.sbx: program 558, data 85, labels 52
> load Keys.sbx
> run
keys, by interrupt. q stops.
ab
the console has been handed back
The machine assembles a program carrying an interrupt handler, the loader
installs its vector, the console interrupts into it, and the shell takes the
vector back at exit. Byte for byte identical to the C assembler's, and
Tests/native.sh now checks a boot image and four loadable programs on every
run.
WHAT IT TOOK:
A declaration and an implementation are the SAME ENTRY. services.asm says
a service is called osPrintString and has number 16; cosmos.asm says
osPrintString is handled by handlePrintString. The name is met twice and
the second time fills in the handler, which is what lets one shared file
serve both the caller and the implementer.
So the first pass declares and the second implements. That is forced: a
handler is an address, and no address is known until every label has been
placed.
Boot in a loadable program fills the entry field rather than being
installed - vector zero is where the whole machine starts, and a program
loaded into a running system has no business saying anything about that.
A boot image is the one thing that does, so there it is installed like any
other, behind a "VEC" marker in the SPBT file.
Device is named by the port, and Device with the five reserved names are
matched without regard to case, the way mnemonics are: they are part of
the language rather than names the programmer chose. Devices have no names
of their own, so they are given one nothing can type.
TWO BUGS, both of a kind worth naming.
The first: "is this a loadable program" was written out as an OR of the two
segment bases in seven places, and the sense wanted is the opposite in most
of them. One of the seven had it backwards and put a version ONE header on a
file carrying vectors, which a loader is right to refuse. It is one flag
now, settled once and tested the same way everywhere.
The second: finding the entry to write a handler into means calling vecFind,
which reads the entry's fields out - including the handler it does not have
yet. An address resolved into VecHandler before the find was overwritten
with zero by the find itself, and the file came out with a vector pointing
at address zero: a slot that looked installed and went nowhere. The
resolved address has a variable of its own now.
Keys.asm and console.asm go on the CosmOS disk, so the whole path can be
watched rather than only tested.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
04424a8917
commit
20989c3439
@@ -7,8 +7,23 @@
|
||||
;
|
||||
; FIXED FIELDS HERE, unlike the label table's arena. There are at most a couple of hundred
|
||||
; of these against several hundred labels, and the names are short, so packing them would
|
||||
; cost more code than it saved. Twenty four bytes an entry: a name of up to twenty two with
|
||||
; its zero, and the number.
|
||||
; cost more code than it saved. Twenty eight bytes an entry:
|
||||
;
|
||||
; 0 23 the name, up to twenty two characters and a zero
|
||||
; 23 1 the vector number
|
||||
; 24 1 which table: 0 software, 1 hardware
|
||||
; 25 2 the handler's address
|
||||
; 27 1 whether a handler has been supplied
|
||||
;
|
||||
; A DECLARATION AND AN IMPLEMENTATION ARE THE SAME ENTRY. services.asm says a service is
|
||||
; called osPrintString and has number 16; cosmos.asm says osPrintString is handled by
|
||||
; handlePrintString. Both sides include the first file, so the name is met twice, and the
|
||||
; second time fills in the handler rather than making a second entry. That is what lets one
|
||||
; shared file serve a program that calls a service and the system that implements it.
|
||||
;
|
||||
; THE ORDER OF THIS TABLE IS THE ORDER OF THE OUTPUT FILE, and the order is declaration
|
||||
; order rather than implementation order, because that is where the entry was made. It has
|
||||
; to match what the other assembler does byte for byte.
|
||||
;
|
||||
; Numbers come from two places. A pinned one is written down in the source, and that is how
|
||||
; anything two separately assembled programs must agree about is fixed - the system's
|
||||
@@ -27,7 +42,8 @@ vecReset:
|
||||
STA.0
|
||||
RET
|
||||
|
||||
; Declares the name at DP0 with the number in A. Q is zero if it went in.
|
||||
; Declares the name at DP0 with the number in A, in the table VecPutBase names. Q is zero
|
||||
; if it went in.
|
||||
vecDeclare:
|
||||
SETD.2 VecPutNumber
|
||||
STA.2
|
||||
@@ -66,6 +82,17 @@ vecDeclareFresh:
|
||||
SETD.2 VecPutNumber
|
||||
LDA.2
|
||||
STA.0
|
||||
INCD.0
|
||||
SETD.2 VecPutBase
|
||||
LDA.2
|
||||
STA.0 ; Which table it lives in.
|
||||
INCD.0
|
||||
RSTA
|
||||
STA.0
|
||||
INCD.0
|
||||
STA.0
|
||||
INCD.0
|
||||
STA.0 ; No handler yet, and no address to go with one.
|
||||
|
||||
SETD.0 VecCount
|
||||
CALL numStep
|
||||
@@ -127,16 +154,7 @@ vecFindLoop:
|
||||
BRI vecFindLoop
|
||||
|
||||
vecFindGot:
|
||||
SETD.1 VecSlot
|
||||
LDD.0.1
|
||||
INIA 0d23
|
||||
SETD.0 VecSlot
|
||||
CALL numAddByte
|
||||
SETD.1 VecSlot
|
||||
LDD.0.1
|
||||
LDA.0
|
||||
SETD.0 VecNumber
|
||||
STA.0
|
||||
CALL vecReadFields
|
||||
RSTA
|
||||
RSTB
|
||||
CCF
|
||||
@@ -150,7 +168,62 @@ vecFindMissing:
|
||||
ADD
|
||||
RET
|
||||
|
||||
; Where entry number VecWhich sits, into VecSlot. Twenty four bytes an entry.
|
||||
; Everything the entry at VecSlot says, into VecNumber, VecBase, VecHandler and
|
||||
; VecHasHandler. VecSlot is left pointing past the name, at the number.
|
||||
vecReadFields:
|
||||
INIA 0d23
|
||||
SETD.0 VecSlot
|
||||
CALL numAddByte
|
||||
SETD.1 VecSlot
|
||||
LDD.0.1
|
||||
LDA.0
|
||||
SETD.1 VecNumber
|
||||
STA.1
|
||||
INCD.0
|
||||
LDA.0
|
||||
SETD.1 VecBase
|
||||
STA.1
|
||||
INCD.0
|
||||
LDA.0
|
||||
SETD.1 VecHandler
|
||||
STA.1
|
||||
INCD.0
|
||||
LDA.0
|
||||
SETD.1 VecHandler
|
||||
INCD.1
|
||||
STA.1
|
||||
INCD.0
|
||||
LDA.0
|
||||
SETD.1 VecHasHandler
|
||||
STA.1
|
||||
RET
|
||||
|
||||
; Gives the entry at VecSlot the handler in VecPutHandler. VecSlot must already have been
|
||||
; walked past the name by vecReadFields, which is how it is always reached.
|
||||
;
|
||||
; A VARIABLE OF ITS OWN, not VecHandler, and that is not tidiness. Finding the entry to
|
||||
; write to means calling vecFind, which reads the entry's fields out - including the
|
||||
; handler it does not have yet. An address resolved into VecHandler before the find was
|
||||
; overwritten with zero by the find itself, and the file came out with a vector pointing
|
||||
; at address zero: a slot that looked installed and went nowhere.
|
||||
vecWriteHandler:
|
||||
SETD.1 VecSlot
|
||||
LDD.0.1
|
||||
INCD.0
|
||||
INCD.0
|
||||
SETD.2 VecPutHandler
|
||||
LDA.2
|
||||
STA.0
|
||||
INCD.0
|
||||
INCD.2
|
||||
LDA.2
|
||||
STA.0
|
||||
INCD.0
|
||||
INIA 0d1
|
||||
STA.0
|
||||
RET
|
||||
|
||||
; Where entry number VecWhich sits, into VecSlot. Twenty eight bytes an entry.
|
||||
vecSlotAt:
|
||||
SETD.0 VecNames
|
||||
SETD.1 VecSlot
|
||||
@@ -165,7 +238,7 @@ vecSlotLoop:
|
||||
LDB.0
|
||||
OR
|
||||
BRQ vecSlotDone
|
||||
INIA 0d24
|
||||
INIA 0d28
|
||||
SETD.0 VecSlot
|
||||
CALL numAddByte
|
||||
SETD.0 VecSlotLeft
|
||||
@@ -193,6 +266,16 @@ VecNumber:
|
||||
0x00
|
||||
VecPutNumber:
|
||||
0x00
|
||||
VecPutBase:
|
||||
0x00
|
||||
VecHandler:
|
||||
0x00 0x00
|
||||
VecPutHandler:
|
||||
0x00 0x00
|
||||
VecHasHandler:
|
||||
0x00
|
||||
VecBase:
|
||||
0x00
|
||||
VecNextAuto:
|
||||
0x00
|
||||
VecOne:
|
||||
@@ -211,4 +294,4 @@ VecName:
|
||||
#Reserve 0d23
|
||||
|
||||
VecNames:
|
||||
#Reserve 0d1536
|
||||
#Reserve 0d1792
|
||||
|
||||
Reference in New Issue
Block a user