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:
Anachronaut
2026-08-21 11:50:52 -04:00
co-authored by Claude Opus 5
parent 04424a8917
commit 20989c3439
7 changed files with 785 additions and 98 deletions
+614 -73
View File
@@ -53,12 +53,14 @@ start:
CALL passOne
BNQ stopped
CALL settleFormat
CALL deriveName ; After the first pass: what it is called depends on whether a
; #Base turned up, and that is not known until then.
CALL layOutImage
BNQ stopped
CALL passTwo
BNQ stopped
CALL writeVectors
CALL writeImage
BNQ stopped
@@ -299,22 +301,39 @@ labelNowhere:
; ---- A line of the Vector Segment ----
;
; A name on its own is a declaration and the assembler numbers it. A name with a number
; after it is pinned, which is how anything two separately assembled programs must agree
; about is fixed. A name with a HANDLER after it says this program implements the vector,
; and that needs a Vector Segment in the output file, which is not built yet.
; Four shapes, and which one it is cannot be known without looking ahead:
;
; Which of the three it is cannot be known without looking at the next token, so the next
; token is looked at and handed back if it turns out to belong to the following line. A
; LINE is what tells them apart: two names on one line are a name and its handler, and two
; names on two lines are two declarations.
; name declared, and the assembler gives it a number
; name 0dNN declared with a number both sides have agreed
; name handler this program implements it
; name 0dNN handler both at once
; Device 0xNN handler named by the port, because that is what decides it
;
; A LINE is what tells them apart. Two names on one line are a name and its handler; two
; names on two lines are two declarations. So the next token is read and handed back if it
; turns out to belong to the following entry.
;
; THE FIRST PASS DECLARES AND THE SECOND IMPLEMENTS. Handlers are addresses and no address
; is known until every label has been placed, so the second pass is the earliest a handler
; can be resolved - and by then the names are all in the table waiting for one.
doVectorLine:
SETD.0 ClsType
LDA.0
INIB 0d5
XOR
BNQ vectorNotAName
RSTA
SETD.0 VecPinnedGiven
STA.0
SETD.0 VecHandlerHere
STA.0
SETD.0 VecIsDevice
STA.0
SETD.0 TokText
SETD.1 WordDevice
CALL sameFolded
BNQ vectorNamed
INIA 0d1
SETD.0 VecIsDevice
STA.0
vectorNamed:
SETD.0 TokText
SETD.1 VecName
CALL srcKeepName
@@ -322,82 +341,315 @@ doVectorLine:
SETD.2 TokLine
CALL numSet
CALL tokNext
BNQ vectorAutomatic ; The file ended, so that was a declaration on its own.
; ---- Whatever else is on this line ----
CALL vectorNextOnLine
BNQ vectorLineEnds
SETD.0 TokLine
SETD.2 VecLineWas
CALL numCompare
BNQ vectorHandBack ; A different line, so it belongs to the next entry.
CALL clsToken
BNQ vectorFailed
SETD.0 ClsType
LDA.0
INIB 0d2
XOR
BNQ vectorHasHandler
BNQ vectorMaybeHandler
; A pinned number. Anything after it on the same line would be a handler.
; A number. Between a name and any handler it PINS the vector; after Device it says
; which port. Nothing else can appear there, because a label may not begin with a digit.
INIA 0d1
SETD.0 VecPinnedGiven
STA.0
SETD.0 VecPinned
SETD.2 ClsValue
LDA.2
STA.0
CALL tokNext
BNQ vectorPinnedDone
SETD.0 TokLine
SETD.2 VecLineWas
CALL numCompare
BNQ vectorHandBackPinned
BRI vectorHasHandler
CALL vectorNextOnLine
BNQ vectorLineEnds
vectorPinnedDone:
vectorMaybeHandler:
; A name here is the handler. Its address is wanted, and only the second pass has one.
SETD.0 ClsType
LDA.0
INIB 0d5
XOR
BNQ vectorOddToken
INIA 0d1
SETD.0 VecHandlerHere
STA.0
SETD.0 TokText
SETD.1 VecHandlerName
CALL srcKeepName
; Anything after the handler is a line that says too much.
CALL vectorNextOnLine
BRQ vectorTooMuch
vectorLineEnds:
SETD.0 Emitting
LDA.0
BNA vectorImplement
BRI vectorDeclareIt
; ---- The second pass: fill in the handler ----
vectorImplement:
SETD.0 VecHandlerHere
LDA.0
BRA vectorLineDone ; Nothing to implement, so nothing to do.
SETD.0 VecHandlerName
CALL labFind
BNQ vectorNoHandler
SETD.0 VecPutHandler
SETD.2 LabAddress
CALL numSet
SETD.0 VecIsDevice
LDA.0
BNA vectorImplementDevice
SETD.0 VecName
CALL vecFind
BNQ vectorLost
CALL vecWriteHandler
BRI vectorLineDone
vectorImplementDevice:
; A device brings no name, so there is nothing to look up: the entry is made now, in the
; place the line sits. Nothing can refer to it, which is why it needs no name of its own.
SETD.0 VecPinned
LDA.0
CALL declareVector
RET
SETD.0 VecPutNumber
STA.0
INIA 0d1
SETD.0 VecPutBase
STA.0
SETD.0 DeviceName
SETD.2 VecPutNumber
LDA.2
CALL vecDeclare
BNQ vectorFailed
SETD.0 DeviceName
CALL vecFind
BNQ vectorLost
CALL vecWriteHandler
CALL vecFreshDeviceName
BRI vectorLineDone
vectorHandBackPinned:
CALL tokBack
BRI vectorPinnedDone
; ---- The first pass: give it a number ----
vectorDeclareIt:
SETD.0 VecIsDevice
LDA.0
BNA vectorDeviceCheck
vectorHandBack:
CALL tokBack
vectorAutomatic:
SETD.0 VecName
CALL vecFind
BRQ vectorAlready
; A reserved name is where the machine looks rather than where a program says to look,
; so its number is not anybody's to choose.
CALL vectorReserved
BNQ vectorNotReserved
SETD.0 VecPinnedGiven
LDA.0
BNA vectorFixedNumber
BRI vectorSetNumber
vectorNotReserved:
SETD.0 VecPinnedGiven
LDA.0
BRA vectorAutoNumber
; A pinned number comes from the range set aside for what two separately assembled
; programs have to agree about. Below it belongs to the machine and above it is handed
; out by the assembler, so neither can be asked for.
SETD.0 VecPinned
LDA.0
INIB 0d16
CCF
SUB
BRC vectorBadNumber
SETD.0 VecPinned
LDA.0
INIB 0d64
CCF
SUB
BNC vectorBadNumber
BRI vectorSetNumber
vectorAutoNumber:
CALL vecTakeAuto
SETD.0 VecPutNumber
LDA.0
CALL declareVector
RET
; Writes VecName down with the number in A - but only in the first pass. The second one
; walks the same lines and must not declare anything again, the same way it does not add a
; label again: the table is the first pass's answer and the second pass only reads it.
declareVector:
SETD.0 VecTaking
SETD.0 VecPinned
STA.0
SETD.0 Emitting
vectorSetNumber:
SETD.0 VecPinned
LDA.0
BNA declareVectorSkip
SETD.0 VecPutNumber
STA.0
RSTA
SETD.0 VecPutBase
STA.0
SETD.0 VecName
SETD.2 VecTaking
SETD.2 VecPutNumber
LDA.2
CALL vecDeclare
RET
BNQ vectorFailed
BRI vectorLineDone
declareVectorSkip:
vectorAlready:
; Met before. A handler now is somebody implementing what was declared earlier; nothing
; but a handler means the name was declared twice.
SETD.0 VecHandlerHere
LDA.0
BRA vectorTwice
SETD.0 VecPinnedGiven
LDA.0
BRA vectorLineDone
SETD.0 VecPinned
LDA.0
SETD.2 VecNumber
LDB.2
XOR
BNQ vectorDisagrees ; The two sides name different vectors by one name.
BRI vectorLineDone
vectorDeviceCheck:
; Device says which port as a number, and then where to go.
SETD.0 VecPinnedGiven
LDA.0
BRA vectorDeviceBare
SETD.0 VecHandlerHere
LDA.0
BRA vectorDeviceBare
vectorLineDone:
RSTA
RSTB
CCF
ADD
RET
vectorHasHandler:
SETD.0 HandlerText
; The next token of this line, if there is one. Q is zero if there was, and it has been
; classified; otherwise it has been handed back for the next entry to have.
vectorNextOnLine:
CALL tokNext
BNQ vectorNextNone
SETD.0 TokLine
SETD.2 VecLineWas
CALL numCompare
BNQ vectorNextOther
CALL clsToken
BNQ vectorNextNone
RSTA
RSTB
CCF
ADD
RET
vectorNextOther:
CALL tokBack
vectorNextNone:
RSTA
INIB 0d1
CCF
ADD
RET
; Q is zero if VecName is one of the vectors the machine itself uses, and then VecPinned
; is the number it must have. These are matched without regard to case, the way mnemonics
; are: they are part of the language rather than names somebody chose.
vectorReserved:
RSTA
SETD.0 ReservedLeft
STA.0
SETD.0 ReservedNames
SETD.1 ReservedWalk
STD.0.1
reservedLoop:
SETD.0 ReservedLeft
LDA.0
SETD.2 ReservedCount
LDB.2
CCF
SUB
BRQ reservedNo
SETD.1 ReservedWalk
LDD.0.1
SETD.1 VecName
CALL sameFolded
BRQ reservedYes
INIA 0d16
SETD.0 ReservedWalk
CALL numAddByte
SETD.0 ReservedLeft
LDA.0
INCA
STA.0
BRI reservedLoop
reservedYes:
SETD.1 ReservedWalk
LDD.0.1
DPUP.0 0d15
LDA.0
SETD.0 VecPinned
STA.0
RSTA
RSTB
CCF
ADD
RET
reservedNo:
RSTA
INIB 0d1
CCF
ADD
RET
; Devices have no names of their own, so they are given one nothing can type: a space
; followed by a counter. It keeps them apart in a table that refuses a repeated name.
vecFreshDeviceName:
SETD.0 DeviceName
INCD.0
LDA.0
INCA
STA.0
RET
vectorFixedNumber:
SETD.0 FixedNumberText
CALL clsComplain
BRI vectorFailed
vectorNotAName:
vectorBadNumber:
SETD.0 BadNumberText
CALL clsComplain
BRI vectorFailed
vectorTwice:
SETD.0 VecTwiceText
CALL clsComplain
BRI vectorFailed
vectorDisagrees:
SETD.0 DisagreeNumberText
CALL clsComplain
BRI vectorFailed
vectorDeviceBare:
SETD.0 DeviceBareText
CALL clsComplain
BRI vectorFailed
vectorNoHandler:
SETD.0 NoHandlerText
CALL clsComplain
BRI vectorFailed
vectorLost:
SETD.0 LostVectorText
CALL clsComplain
BRI vectorFailed
vectorTooMuch:
SETD.0 TooMuchText
CALL clsComplain
BRI vectorFailed
vectorOddToken:
SETD.0 VectorOddText
CALL clsComplain
vectorFailed:
@@ -1043,6 +1295,34 @@ dropColonDone:
; Where each segment's bytes will go, and the header in front of them. Both lengths are
; known now, which is the whole reason the first pass exists.
; Which of the two file formats this is, settled once and written down.
;
; A program that says where it goes is a loadable one and gets the SBEX header; one that
; says nothing is a boot image and gets SPBT. The difference is not a version but a
; question of what the file needs of whatever reads it.
;
; ONE FLAG RATHER THAN THE TEST REPEATED. It is asked in seven places - the header, the
; extension, where the vectors go, whether they carry a marker, how long the file is, and
; whether a Boot line is the entry or a handler - and written out each time it read as an
; OR of the two bases, whose sense is the opposite of what most of those places want. 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.
settleFormat:
RSTA
SETD.0 Loadable
STA.0
SETD.0 ProgBased
LDA.0
SETD.2 DataBased
LDB.2
OR
BRQ settleFormatDone
INIA 0d1
SETD.0 Loadable
STA.0
settleFormatDone:
RET
layOutImage:
; How long each segment came out, which is where its cursor ended less where it began.
SETD.0 ImgProgLen
@@ -1058,15 +1338,9 @@ layOutImage:
SETD.2 DataBase
CALL numTake
; A program that says where it goes is a loadable one and gets the SBEX header; one that
; says nothing is a boot image and gets SPBT. The difference is not a version but a
; question of what the file needs of whatever reads it.
SETD.0 ProgBased
SETD.0 Loadable
LDA.0
SETD.2 DataBased
LDB.2
OR
BNQ layOutLoadable
BNA layOutLoadable
; ---- A boot image ----
;
@@ -1297,6 +1571,206 @@ byteAt:
STA.0
RET
; ---- The vectors the file carries ----
;
; Last in the file, after the code and the data, so that everything before them sits where
; a loader that knows nothing about vectors already expects to find it.
;
; Four bytes each: WHERE THE SLOT IS, then what goes in it. Saying the slot outright rather
; than the vector number means the loader does no arithmetic and does not have to know
; where either vector table begins, and one entry can name a software or a hardware vector
; without saying which it is.
writeVectors:
SETD.0 VecInstalled
CALL numZero
SETD.0 EntryAddress
SETD.2 ProgBase
CALL numSet
; Counted first, because a boot image writes the length of the run before the run.
CALL countVectors
SETD.0 ImgWalk
SETD.2 DataPut
CALL numSet
SETD.0 VecInstalled
LDA.0
INCD.0
LDB.0
OR
BRQ writeVectorsNone
SETD.0 Loadable
LDA.0
BNA writeVectorsRoom
; A boot image marks the run and says how long it is, the way it does for its segments.
SETD.0 MagicVEC
INIA 0d3
CALL putBytes
SETD.0 VecBytes
CALL putWord
writeVectorsRoom:
SETD.0 VecWhich
CALL numZero
writeVectorLoop:
SETD.0 VecWhich
SETD.2 VecCount
CALL numCompare
BNC writeVectorsDone
CALL vecSlotAt
CALL vecReadFields
SETD.0 VecHasHandler
LDA.0
BRA writeVectorNext
CALL vectorIsEntry
BRQ writeVectorNext ; Boot in a loadable program is the entry, not a handler.
; Where the slot is: the table it belongs to, plus two bytes for every vector before it.
SETD.0 SlotAt
SETD.2 SoftwareBase
CALL numSet
SETD.0 VecBase
LDA.0
BRA writeVectorSlot
SETD.0 SlotAt
SETD.2 HardwareBase
CALL numSet
writeVectorSlot:
SETD.0 VecNumber
LDA.0
SETD.0 SlotAt
CALL numAddByte
SETD.0 VecNumber
LDA.0
SETD.0 SlotAt
CALL numAddByte
SETD.0 SlotAt
CALL putWord
SETD.0 VecHandler
CALL putWord
writeVectorNext:
SETD.0 VecWhich
CALL numStep
BRI writeVectorLoop
writeVectorsDone:
SETD.0 ImgTotal
SETD.2 VecBytes
CALL numAdd
SETD.0 Loadable
LDA.0
BNA writeVectorsHeader
INIA 0d5
SETD.0 ImgTotal
CALL numAddByte ; The marker and the length in front of them.
writeVectorsHeader:
; A file that brings vectors needs something of its loader a version one loader does not
; know how to give, so it says version two and an older one refuses it rather than
; running the program without its handlers.
SETD.0 Loadable
LDA.0
BRA writeVectorsNone
SETD.0 Image
SETD.1 ImgWalk
STD.0.1
SETD.0 ImgWalk
INIA 0d4
CALL numAddByte
INIA 0d2
CALL putByte
SETD.0 VecInstalled
INCD.0
LDA.0
CALL putByte
writeVectorsNone:
; And where to start, which a Boot line fills in and everything else leaves as the first
; byte of the code.
SETD.0 Loadable
LDA.0
BRA writeVectorsOut
SETD.0 Image
SETD.1 ImgWalk
STD.0.1
SETD.0 ImgWalk
INIA 0d8
CALL numAddByte
SETD.0 EntryAddress
CALL putWord
writeVectorsOut:
RET
; How many vectors will be written, into VecInstalled, and how many bytes that is.
countVectors:
SETD.0 VecWhich
CALL numZero
countVectorLoop:
SETD.0 VecWhich
SETD.2 VecCount
CALL numCompare
BNC countVectorsDone
CALL vecSlotAt
CALL vecReadFields
SETD.0 VecHasHandler
LDA.0
BRA countVectorNext
CALL vectorIsEntry
BRQ countVectorTakeEntry
SETD.0 VecInstalled
CALL numStep
BRI countVectorNext
countVectorTakeEntry:
SETD.0 EntryAddress
SETD.2 VecHandler
CALL numSet
countVectorNext:
SETD.0 VecWhich
CALL numStep
BRI countVectorLoop
countVectorsDone:
SETD.0 VecBytes
SETD.2 VecInstalled
CALL numSet
SETD.0 VecBytes
SETD.2 VecBytes
CALL numAdd
SETD.0 VecBytes
SETD.2 VecBytes
CALL numAdd ; Four bytes an entry.
RET
; Q is zero if this entry is the Boot line of a LOADABLE program, which fills the entry
; field instead of being installed. Vector zero is where the whole machine starts, and a
; program being 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.
vectorIsEntry:
SETD.0 Loadable
LDA.0
BRA vectorNotEntry
SETD.0 VecBase
LDA.0
BNA vectorNotEntry
SETD.0 VecNumber
LDA.0
BNA vectorNotEntry
RSTA
RSTB
CCF
ADD
RET
vectorNotEntry:
RSTA
INIB 0d1
CCF
ADD
RET
writeImage:
SETD.0 OutName
SETD.1 Image
@@ -1369,12 +1843,9 @@ deriveEnd:
deriveAtDot:
SETD.1 DotAt
LDD.1.1
SETD.0 ProgBased
SETD.0 Loadable
LDA.0
SETD.2 DataBased
LDB.2
OR
BNQ deriveLoadable
BNA deriveLoadable
SETD.0 ExtensionBin
BRI deriveCopy
deriveLoadable:
@@ -1473,6 +1944,8 @@ DataBase:
0x00 0x00
ProgBased:
0x00
Loadable:
0x00
DataBased:
0x00
ProgUsed:
@@ -1495,8 +1968,56 @@ VecLineWas:
0x00 0x00
VecPinned:
0x00
VecTaking:
VecPinnedGiven:
0x00
VecHandlerHere:
0x00
VecIsDevice:
0x00
VecInstalled:
0x00 0x00
VecBytes:
0x00 0x00
EntryAddress:
0x00 0x00
SlotAt:
0x00 0x00
SoftwareBase:
0xFC 0x00
HardwareBase:
0xFE 0x00
ReservedLeft:
0x00
ReservedWalk:
0x00 0x00
ReservedCount:
0d5
VecHandlerName:
#Reserve 0d23
; A name nothing can type, so that devices - which have no names of their own - can live in
; a table that refuses a repeated one. A space, then a counter.
DeviceName:
0x20 0x01 0x00
#Reserve 0d20
; The vectors that already mean something. Sixteen bytes each: fifteen of name, then the
; number the machine fixed for it.
ReservedNames:
"Boot"
#Reserve 0d10
0d0
"SoftReset"
#Reserve 0d5
0d1
"BadOpcode"
#Reserve 0d5
0d2
"GuardViolation"
0d3
"BankFault"
#Reserve 0d5
0d4
ProgPut:
0x00 0x00
@@ -1537,6 +2058,8 @@ MagicPRG:
"PRG"
MagicDAT:
"DAT"
MagicVEC:
"VEC"
ExtensionBin:
".bin"
ExtensionSbx:
@@ -1556,6 +2079,8 @@ WordReserve:
"#Reserve"
WordAlign:
"#Align"
WordDevice:
"Device"
UsageText:
"say which file to assemble, as in: run hello.asm
@@ -1597,8 +2122,24 @@ NumberBadText:
"a directive wants a number here, written 0x.. or 0d.."
UnknownVectorText:
"no vector of that name is declared anywhere in this program"
HandlerText:
"this assembler cannot build a Vector Segment yet, so it cannot install a handler"
FixedNumberText:
"that vector's number is fixed by the machine and is not anybody's to give"
BadNumberText:
"that is not a vector number that can be given: below 16 belongs to the machine, and 64
and up is handed out by the assembler"
VecTwiceText:
"that vector is declared more than once"
DisagreeNumberText:
"that vector was already given a different number, so the two sides disagree about which
vector this name means"
DeviceBareText:
"Device has to say which port, as a number, and then where to go"
NoHandlerText:
"there is no label of that name for the handler to be"
LostVectorText:
"a vector went missing between the two passes"
TooMuchText:
"there is more on that line than a vector entry can be"
VectorOddText:
"only names belong in the Vector Segment"
DisagreeText:
+31
View File
@@ -673,6 +673,35 @@ clsDigitNo:
; ---- Odds and ends ----
; Q is zero if the strings at DP0 and DP1 are the same, ignoring case.
;
; For the words that are part of the LANGUAGE rather than names somebody chose: Device, and
; the vectors the machine already uses. Mnemonics are matched the same way, for the same
; reason - nobody should have to remember how the manual capitalised something.
sameFolded:
LDA.0
CALL clsUpper
SETD.2 ClsByte
LDA.2
SETD.2 ClsFoldHold
STA.2
LDA.1
CALL clsUpper
SETD.2 ClsByte
LDA.2
SETD.2 ClsFoldHold
LDB.2
CCF
SUB
BNQ sameFoldedDone
LDA.0
BRA sameFoldedDone ; They ended together, so they matched all the way.
INCD.0
INCD.1
BRI sameFolded
sameFoldedDone:
RET
; ClsLength becomes the byte in A. Everything but a string is a small number, and this is
; how a small number is written into a sixteen bit field.
clsSetLength:
@@ -814,6 +843,8 @@ ClsDigitHold:
0x00
ClsByte:
0x00
ClsFoldHold:
0x00
ClsLeft:
0x00
ClsLeft2:
+99 -16
View File
@@ -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