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
@@ -53,12 +53,14 @@ start:
|
|||||||
|
|
||||||
CALL passOne
|
CALL passOne
|
||||||
BNQ stopped
|
BNQ stopped
|
||||||
|
CALL settleFormat
|
||||||
CALL deriveName ; After the first pass: what it is called depends on whether a
|
CALL deriveName ; After the first pass: what it is called depends on whether a
|
||||||
; #Base turned up, and that is not known until then.
|
; #Base turned up, and that is not known until then.
|
||||||
CALL layOutImage
|
CALL layOutImage
|
||||||
BNQ stopped
|
BNQ stopped
|
||||||
CALL passTwo
|
CALL passTwo
|
||||||
BNQ stopped
|
BNQ stopped
|
||||||
|
CALL writeVectors
|
||||||
CALL writeImage
|
CALL writeImage
|
||||||
BNQ stopped
|
BNQ stopped
|
||||||
|
|
||||||
@@ -299,22 +301,39 @@ labelNowhere:
|
|||||||
|
|
||||||
; ---- A line of the Vector Segment ----
|
; ---- A line of the Vector Segment ----
|
||||||
;
|
;
|
||||||
; A name on its own is a declaration and the assembler numbers it. A name with a number
|
; Four shapes, and which one it is cannot be known without looking ahead:
|
||||||
; 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.
|
|
||||||
;
|
;
|
||||||
; Which of the three it is cannot be known without looking at the next token, so the next
|
; name declared, and the assembler gives it a number
|
||||||
; token is looked at and handed back if it turns out to belong to the following line. A
|
; name 0dNN declared with a number both sides have agreed
|
||||||
; LINE is what tells them apart: two names on one line are a name and its handler, and two
|
; name handler this program implements it
|
||||||
; names on two lines are two declarations.
|
; 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:
|
doVectorLine:
|
||||||
SETD.0 ClsType
|
RSTA
|
||||||
LDA.0
|
SETD.0 VecPinnedGiven
|
||||||
INIB 0d5
|
STA.0
|
||||||
XOR
|
SETD.0 VecHandlerHere
|
||||||
BNQ vectorNotAName
|
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.0 TokText
|
||||||
SETD.1 VecName
|
SETD.1 VecName
|
||||||
CALL srcKeepName
|
CALL srcKeepName
|
||||||
@@ -322,82 +341,315 @@ doVectorLine:
|
|||||||
SETD.2 TokLine
|
SETD.2 TokLine
|
||||||
CALL numSet
|
CALL numSet
|
||||||
|
|
||||||
CALL tokNext
|
; ---- Whatever else is on this line ----
|
||||||
BNQ vectorAutomatic ; The file ended, so that was a declaration on its own.
|
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
|
SETD.0 ClsType
|
||||||
LDA.0
|
LDA.0
|
||||||
INIB 0d2
|
INIB 0d2
|
||||||
XOR
|
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.0 VecPinned
|
||||||
SETD.2 ClsValue
|
SETD.2 ClsValue
|
||||||
LDA.2
|
LDA.2
|
||||||
STA.0
|
STA.0
|
||||||
CALL tokNext
|
CALL vectorNextOnLine
|
||||||
BNQ vectorPinnedDone
|
BNQ vectorLineEnds
|
||||||
SETD.0 TokLine
|
|
||||||
SETD.2 VecLineWas
|
|
||||||
CALL numCompare
|
|
||||||
BNQ vectorHandBackPinned
|
|
||||||
BRI vectorHasHandler
|
|
||||||
|
|
||||||
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
|
SETD.0 VecPinned
|
||||||
LDA.0
|
LDA.0
|
||||||
CALL declareVector
|
SETD.0 VecPutNumber
|
||||||
RET
|
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:
|
; ---- The first pass: give it a number ----
|
||||||
CALL tokBack
|
vectorDeclareIt:
|
||||||
BRI vectorPinnedDone
|
SETD.0 VecIsDevice
|
||||||
|
LDA.0
|
||||||
|
BNA vectorDeviceCheck
|
||||||
|
|
||||||
vectorHandBack:
|
SETD.0 VecName
|
||||||
CALL tokBack
|
CALL vecFind
|
||||||
vectorAutomatic:
|
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
|
CALL vecTakeAuto
|
||||||
SETD.0 VecPutNumber
|
SETD.0 VecPutNumber
|
||||||
LDA.0
|
LDA.0
|
||||||
CALL declareVector
|
SETD.0 VecPinned
|
||||||
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
|
|
||||||
STA.0
|
STA.0
|
||||||
SETD.0 Emitting
|
|
||||||
|
vectorSetNumber:
|
||||||
|
SETD.0 VecPinned
|
||||||
LDA.0
|
LDA.0
|
||||||
BNA declareVectorSkip
|
SETD.0 VecPutNumber
|
||||||
|
STA.0
|
||||||
|
RSTA
|
||||||
|
SETD.0 VecPutBase
|
||||||
|
STA.0
|
||||||
SETD.0 VecName
|
SETD.0 VecName
|
||||||
SETD.2 VecTaking
|
SETD.2 VecPutNumber
|
||||||
LDA.2
|
LDA.2
|
||||||
CALL vecDeclare
|
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
|
RSTA
|
||||||
RSTB
|
RSTB
|
||||||
CCF
|
CCF
|
||||||
ADD
|
ADD
|
||||||
RET
|
RET
|
||||||
|
|
||||||
vectorHasHandler:
|
; The next token of this line, if there is one. Q is zero if there was, and it has been
|
||||||
SETD.0 HandlerText
|
; 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
|
CALL clsComplain
|
||||||
BRI vectorFailed
|
BRI vectorFailed
|
||||||
|
vectorBadNumber:
|
||||||
vectorNotAName:
|
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
|
SETD.0 VectorOddText
|
||||||
CALL clsComplain
|
CALL clsComplain
|
||||||
vectorFailed:
|
vectorFailed:
|
||||||
@@ -1043,6 +1295,34 @@ dropColonDone:
|
|||||||
|
|
||||||
; Where each segment's bytes will go, and the header in front of them. Both lengths are
|
; 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.
|
; 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:
|
layOutImage:
|
||||||
; How long each segment came out, which is where its cursor ended less where it began.
|
; How long each segment came out, which is where its cursor ended less where it began.
|
||||||
SETD.0 ImgProgLen
|
SETD.0 ImgProgLen
|
||||||
@@ -1058,15 +1338,9 @@ layOutImage:
|
|||||||
SETD.2 DataBase
|
SETD.2 DataBase
|
||||||
CALL numTake
|
CALL numTake
|
||||||
|
|
||||||
; A program that says where it goes is a loadable one and gets the SBEX header; one that
|
SETD.0 Loadable
|
||||||
; 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
|
|
||||||
LDA.0
|
LDA.0
|
||||||
SETD.2 DataBased
|
BNA layOutLoadable
|
||||||
LDB.2
|
|
||||||
OR
|
|
||||||
BNQ layOutLoadable
|
|
||||||
|
|
||||||
; ---- A boot image ----
|
; ---- A boot image ----
|
||||||
;
|
;
|
||||||
@@ -1297,6 +1571,206 @@ byteAt:
|
|||||||
STA.0
|
STA.0
|
||||||
RET
|
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:
|
writeImage:
|
||||||
SETD.0 OutName
|
SETD.0 OutName
|
||||||
SETD.1 Image
|
SETD.1 Image
|
||||||
@@ -1369,12 +1843,9 @@ deriveEnd:
|
|||||||
deriveAtDot:
|
deriveAtDot:
|
||||||
SETD.1 DotAt
|
SETD.1 DotAt
|
||||||
LDD.1.1
|
LDD.1.1
|
||||||
SETD.0 ProgBased
|
SETD.0 Loadable
|
||||||
LDA.0
|
LDA.0
|
||||||
SETD.2 DataBased
|
BNA deriveLoadable
|
||||||
LDB.2
|
|
||||||
OR
|
|
||||||
BNQ deriveLoadable
|
|
||||||
SETD.0 ExtensionBin
|
SETD.0 ExtensionBin
|
||||||
BRI deriveCopy
|
BRI deriveCopy
|
||||||
deriveLoadable:
|
deriveLoadable:
|
||||||
@@ -1473,6 +1944,8 @@ DataBase:
|
|||||||
0x00 0x00
|
0x00 0x00
|
||||||
ProgBased:
|
ProgBased:
|
||||||
0x00
|
0x00
|
||||||
|
Loadable:
|
||||||
|
0x00
|
||||||
DataBased:
|
DataBased:
|
||||||
0x00
|
0x00
|
||||||
ProgUsed:
|
ProgUsed:
|
||||||
@@ -1495,8 +1968,56 @@ VecLineWas:
|
|||||||
0x00 0x00
|
0x00 0x00
|
||||||
VecPinned:
|
VecPinned:
|
||||||
0x00
|
0x00
|
||||||
VecTaking:
|
VecPinnedGiven:
|
||||||
0x00
|
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:
|
ProgPut:
|
||||||
0x00 0x00
|
0x00 0x00
|
||||||
@@ -1537,6 +2058,8 @@ MagicPRG:
|
|||||||
"PRG"
|
"PRG"
|
||||||
MagicDAT:
|
MagicDAT:
|
||||||
"DAT"
|
"DAT"
|
||||||
|
MagicVEC:
|
||||||
|
"VEC"
|
||||||
ExtensionBin:
|
ExtensionBin:
|
||||||
".bin"
|
".bin"
|
||||||
ExtensionSbx:
|
ExtensionSbx:
|
||||||
@@ -1556,6 +2079,8 @@ WordReserve:
|
|||||||
"#Reserve"
|
"#Reserve"
|
||||||
WordAlign:
|
WordAlign:
|
||||||
"#Align"
|
"#Align"
|
||||||
|
WordDevice:
|
||||||
|
"Device"
|
||||||
|
|
||||||
UsageText:
|
UsageText:
|
||||||
"say which file to assemble, as in: run hello.asm
|
"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.."
|
"a directive wants a number here, written 0x.. or 0d.."
|
||||||
UnknownVectorText:
|
UnknownVectorText:
|
||||||
"no vector of that name is declared anywhere in this program"
|
"no vector of that name is declared anywhere in this program"
|
||||||
HandlerText:
|
FixedNumberText:
|
||||||
"this assembler cannot build a Vector Segment yet, so it cannot install a handler"
|
"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:
|
VectorOddText:
|
||||||
"only names belong in the Vector Segment"
|
"only names belong in the Vector Segment"
|
||||||
DisagreeText:
|
DisagreeText:
|
||||||
|
|||||||
@@ -673,6 +673,35 @@ clsDigitNo:
|
|||||||
|
|
||||||
; ---- Odds and ends ----
|
; ---- 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
|
; 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.
|
; how a small number is written into a sixteen bit field.
|
||||||
clsSetLength:
|
clsSetLength:
|
||||||
@@ -814,6 +843,8 @@ ClsDigitHold:
|
|||||||
0x00
|
0x00
|
||||||
ClsByte:
|
ClsByte:
|
||||||
0x00
|
0x00
|
||||||
|
ClsFoldHold:
|
||||||
|
0x00
|
||||||
ClsLeft:
|
ClsLeft:
|
||||||
0x00
|
0x00
|
||||||
ClsLeft2:
|
ClsLeft2:
|
||||||
|
|||||||
@@ -7,8 +7,23 @@
|
|||||||
;
|
;
|
||||||
; FIXED FIELDS HERE, unlike the label table's arena. There are at most a couple of hundred
|
; 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
|
; 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
|
; cost more code than it saved. Twenty eight bytes an entry:
|
||||||
; its zero, and the number.
|
;
|
||||||
|
; 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
|
; 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
|
; anything two separately assembled programs must agree about is fixed - the system's
|
||||||
@@ -27,7 +42,8 @@ vecReset:
|
|||||||
STA.0
|
STA.0
|
||||||
RET
|
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:
|
vecDeclare:
|
||||||
SETD.2 VecPutNumber
|
SETD.2 VecPutNumber
|
||||||
STA.2
|
STA.2
|
||||||
@@ -66,6 +82,17 @@ vecDeclareFresh:
|
|||||||
SETD.2 VecPutNumber
|
SETD.2 VecPutNumber
|
||||||
LDA.2
|
LDA.2
|
||||||
STA.0
|
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
|
SETD.0 VecCount
|
||||||
CALL numStep
|
CALL numStep
|
||||||
@@ -127,16 +154,7 @@ vecFindLoop:
|
|||||||
BRI vecFindLoop
|
BRI vecFindLoop
|
||||||
|
|
||||||
vecFindGot:
|
vecFindGot:
|
||||||
SETD.1 VecSlot
|
CALL vecReadFields
|
||||||
LDD.0.1
|
|
||||||
INIA 0d23
|
|
||||||
SETD.0 VecSlot
|
|
||||||
CALL numAddByte
|
|
||||||
SETD.1 VecSlot
|
|
||||||
LDD.0.1
|
|
||||||
LDA.0
|
|
||||||
SETD.0 VecNumber
|
|
||||||
STA.0
|
|
||||||
RSTA
|
RSTA
|
||||||
RSTB
|
RSTB
|
||||||
CCF
|
CCF
|
||||||
@@ -150,7 +168,62 @@ vecFindMissing:
|
|||||||
ADD
|
ADD
|
||||||
RET
|
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:
|
vecSlotAt:
|
||||||
SETD.0 VecNames
|
SETD.0 VecNames
|
||||||
SETD.1 VecSlot
|
SETD.1 VecSlot
|
||||||
@@ -165,7 +238,7 @@ vecSlotLoop:
|
|||||||
LDB.0
|
LDB.0
|
||||||
OR
|
OR
|
||||||
BRQ vecSlotDone
|
BRQ vecSlotDone
|
||||||
INIA 0d24
|
INIA 0d28
|
||||||
SETD.0 VecSlot
|
SETD.0 VecSlot
|
||||||
CALL numAddByte
|
CALL numAddByte
|
||||||
SETD.0 VecSlotLeft
|
SETD.0 VecSlotLeft
|
||||||
@@ -193,6 +266,16 @@ VecNumber:
|
|||||||
0x00
|
0x00
|
||||||
VecPutNumber:
|
VecPutNumber:
|
||||||
0x00
|
0x00
|
||||||
|
VecPutBase:
|
||||||
|
0x00
|
||||||
|
VecHandler:
|
||||||
|
0x00 0x00
|
||||||
|
VecPutHandler:
|
||||||
|
0x00 0x00
|
||||||
|
VecHasHandler:
|
||||||
|
0x00
|
||||||
|
VecBase:
|
||||||
|
0x00
|
||||||
VecNextAuto:
|
VecNextAuto:
|
||||||
0x00
|
0x00
|
||||||
VecOne:
|
VecOne:
|
||||||
@@ -211,4 +294,4 @@ VecName:
|
|||||||
#Reserve 0d23
|
#Reserve 0d23
|
||||||
|
|
||||||
VecNames:
|
VecNames:
|
||||||
#Reserve 0d1536
|
#Reserve 0d1792
|
||||||
|
|||||||
+8
-1
@@ -85,7 +85,8 @@ cosmos: $(COSMOS) $(APPS) $(NATIVE_ASM)
|
|||||||
# Made from scratch every time, so that what is on it is what is in Apps/ now and not
|
# Made from scratch every time, so that what is on it is what is in Apps/ now and not
|
||||||
# also whatever used to be.
|
# also whatever used to be.
|
||||||
$(COSMOS_DISK): $(APPS) $(NATIVE_ASM) testPrograms/stringKeyword.asm \
|
$(COSMOS_DISK): $(APPS) $(NATIVE_ASM) testPrograms/stringKeyword.asm \
|
||||||
CosmOS/Apps/hello.asm CosmOS/Apps/Say.asm CosmOS/Source/services.asm
|
CosmOS/Apps/hello.asm CosmOS/Apps/Say.asm CosmOS/Apps/Keys.asm \
|
||||||
|
CosmOS/Source/services.asm CosmOS/Source/console.asm
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
rm -f $@
|
rm -f $@
|
||||||
$(DISKTOOL) format $@ 2048 4
|
$(DISKTOOL) format $@ 2048 4
|
||||||
@@ -98,12 +99,18 @@ $(COSMOS_DISK): $(APPS) $(NATIVE_ASM) testPrograms/stringKeyword.asm \
|
|||||||
@# written straight over the one the host tool put there - which means the next
|
@# written straight over the one the host tool put there - which means the next
|
||||||
@# thing loaded is a program the machine built itself, in the same breath.
|
@# thing loaded is a program the machine built itself, in the same breath.
|
||||||
@#
|
@#
|
||||||
|
@# Keys.asm is the one that brings a vector of its own, so assembling it exercises the
|
||||||
|
@# version two header and the Vector Segment: the loader installs its handler, the
|
||||||
|
@# console interrupts into it, and the shell takes the vector back at exit.
|
||||||
|
@#
|
||||||
@# strings.asm is the odd one out on purpose: it has no #Include and no #Base, so it
|
@# strings.asm is the odd one out on purpose: it has no #Include and no #Base, so it
|
||||||
@# comes out as a boot image rather than a loadable program, and the difference
|
@# comes out as a boot image rather than a loadable program, and the difference
|
||||||
@# between the two is visible on one disk.
|
@# between the two is visible on one disk.
|
||||||
$(DISKTOOL) put $@ CosmOS/Apps/hello.asm
|
$(DISKTOOL) put $@ CosmOS/Apps/hello.asm
|
||||||
$(DISKTOOL) put $@ CosmOS/Apps/Say.asm
|
$(DISKTOOL) put $@ CosmOS/Apps/Say.asm
|
||||||
|
$(DISKTOOL) put $@ CosmOS/Apps/Keys.asm
|
||||||
$(DISKTOOL) put $@ CosmOS/Source/services.asm
|
$(DISKTOOL) put $@ CosmOS/Source/services.asm
|
||||||
|
$(DISKTOOL) put $@ CosmOS/Source/console.asm
|
||||||
$(DISKTOOL) put $@ testPrograms/stringKeyword.asm strings.asm
|
$(DISKTOOL) put $@ testPrograms/stringKeyword.asm strings.asm
|
||||||
|
|
||||||
# The system as well as the disk. Building only the image leaves whatever cosmos.bin was
|
# The system as well as the disk. Building only the image leaves whatever cosmos.bin was
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ SplitBit is a custom 8 bit system designed for hobbyist projects and experimenta
|
|||||||
- Loadable Programs: A program that was not booted from carries a header saying where it belongs, and Programs/loader.asm reads one off a disk, puts it there, and runs it.
|
- Loadable Programs: A program that was not booted from carries a header saying where it belongs, and Programs/loader.asm reads one off a disk, puts it there, and runs it.
|
||||||
- An Operating System: CosmOS boots the machine, mounts a disk, lists what is on it, loads a program and runs it, and takes the machine back when it finishes. It comes with a library of programs to run, including a game and a line editor that writes files a person typed.
|
- An Operating System: CosmOS boots the machine, mounts a disk, lists what is on it, loads a program and runs it, and takes the machine back when it finishes. It comes with a library of programs to run, including a game and a line editor that writes files a person typed.
|
||||||
- System Services: A loaded program reaches the console and the disk through numbered software interrupts rather than carrying a copy of the code that drives them. The numbers are written down in one file that both sides include, so neither ever types one. It took the editor from 4941 bytes to 1983 without changing a line of what it does.
|
- System Services: A loaded program reaches the console and the disk through numbered software interrupts rather than carrying a copy of the code that drives them. The numbers are written down in one file that both sides include, so neither ever types one. It took the editor from 4941 bytes to 1983 without changing a line of what it does.
|
||||||
- A Native Assembler: SplitBit assembles SplitBit. Programs/CosmOS/Assembler/ is an assembler written in SplitBit assembly that runs under CosmOS, reads source off a SplitBit disk, and writes a binary back to it with no host involved. It builds boot images and loadable applications, following #Include, #Base, #Reserve and #Align, so a program assembled on the machine can then be loaded and run by it. Its output has to be byte for byte identical to what the C assembler produces from the same source, which is what Tests/native.sh checks.
|
- A Native Assembler: SplitBit assembles SplitBit. Programs/CosmOS/Assembler/ is an assembler written in SplitBit assembly that runs under CosmOS, reads source off a SplitBit disk, and writes a binary back to it with no host involved. It builds boot images and loadable applications, following every directive the language has - including #Vectors, so a program that brings its own interrupt handlers can be assembled on the machine, loaded by it, and have its vectors installed and taken back again. Its output has to be byte for byte identical to what the C assembler produces from the same source, which is what Tests/native.sh checks.
|
||||||
- Streaming Reads: A file bigger than the machine's memory is read a block at a time, through services that keep nothing open between calls. CosmOS's own source is 104K against 64K of Data Memory, so this is what a self-hosted assembler will stand on.
|
- Streaming Reads: A file bigger than the machine's memory is read a block at a time, through services that keep nothing open between calls. CosmOS's own source is 104K against 64K of Data Memory, so this is what a self-hosted assembler will stand on.
|
||||||
- Storage: A block device with 256 byte blocks and 16 megabytes of them, backed by an image file on the host. It knows blocks and not files, because a filesystem is meant to be software SplitBit runs.
|
- Storage: A block device with 256 byte blocks and 16 megabytes of them, backed by an image file on the host. It knows blocks and not files, because a filesystem is meant to be software SplitBit runs.
|
||||||
- Memory Controller: Reads and writes Program Memory, moves blocks between memory banks, reaches memory that devices bring with them, and guards a range against being written by accident. It is how a SplitBit machine loads a program.
|
- Memory Controller: Reads and writes Program Memory, moves blocks between memory banks, reaches memory that devices bring with them, and guards a range against being written by accident. It is how a SplitBit machine loads a program.
|
||||||
|
|||||||
@@ -520,6 +520,25 @@ wrote Say.sbx: program 46, data 93, labels 7
|
|||||||
it says: built by the machine itself
|
it says: built by the machine itself
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Programs That Bring Vectors:
|
||||||
|
|
||||||
|
A `#Vectors` line that names a **handler** says this program implements that vector, and the file then carries a Vector Segment: four bytes an entry, the address of the slot and the address to put in it, after the code and the data so that everything before them sits where a loader knowing nothing about vectors already expects it. A loadable program carrying any says **version two**, and a loader that cannot install them refuses it rather than running the program without its handlers.
|
||||||
|
|
||||||
|
```
|
||||||
|
#Vectors
|
||||||
|
|
||||||
|
Boot start
|
||||||
|
Device 0x00 keyHandler
|
||||||
|
```
|
||||||
|
|
||||||
|
`Boot` in a loadable program fills the **entry** field rather than 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.
|
||||||
|
|
||||||
|
`Device` is named by the port it is plugged into, because that is what decides which vector it arrives through. `Device`, `Boot`, `SoftReset`, `BadOpcode`, `GuardViolation` and `BankFault` are matched **without regard to case**, the way mnemonics are: they are part of the language rather than names the programmer chose.
|
||||||
|
|
||||||
|
**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. That is what lets one shared file serve both a program that calls a service and the system that implements it — and it is why the first pass declares and the second implements, a handler being an address and no address being known until every label has been placed.
|
||||||
|
|
||||||
### What It Does Not Do Yet:
|
### What It Does Not Do Yet:
|
||||||
|
|
||||||
A `#Vectors` line that names a **handler** rather than only declaring a name. That needs a Vector Segment in the output file and the version two header that carries it, so a program bringing its own interrupt handlers cannot be built on the machine yet. It is refused by name rather than ignored, as everything unfinished here is: an assembler that quietly skipped a directive would produce a file that looked right and was the wrong length, which is the worst thing it could do.
|
Nothing in the language. Every directive this manual describes is understood, and `Tests/native.sh` checks a boot image and four loadable programs — the last of them bringing a vector — against the host assembler byte for byte on every run.
|
||||||
|
|
||||||
|
What is left is scale rather than grammar: whether it can assemble CosmOS itself, which is 104,142 bytes of source across several files and 453 labels.
|
||||||
|
|||||||
+12
-6
@@ -10,9 +10,10 @@
|
|||||||
# Then it runs what the machine built, because a file that matches and does not work would
|
# Then it runs what the machine built, because a file that matches and does not work would
|
||||||
# mean both assemblers were wrong together.
|
# mean both assemblers were wrong together.
|
||||||
#
|
#
|
||||||
# It checks a boot image and three loadable programs. The loadable ones are the harder case
|
# It checks a boot image and four loadable programs. The loadable ones are the harder case:
|
||||||
# and the interesting one: they include another file, they are based somewhere other than
|
# they include another file, they are based somewhere other than zero, and every service
|
||||||
# zero, and every service they call is a name declared in that included file.
|
# they call is a name declared in that included file. The last of them also brings a vector,
|
||||||
|
# which is the only thing here that asks anything of its loader.
|
||||||
#
|
#
|
||||||
# Written by Anachronaut
|
# Written by Anachronaut
|
||||||
|
|
||||||
@@ -62,8 +63,13 @@ mkdir -p "$WORK"
|
|||||||
# The applications, and the file of service names they all include. These are the reason
|
# The applications, and the file of service names they all include. These are the reason
|
||||||
# the second milestone exists: an assembler that cannot follow an #Include cannot build
|
# the second milestone exists: an assembler that cannot follow an #Include cannot build
|
||||||
# anything that asks the system for anything.
|
# anything that asks the system for anything.
|
||||||
APPS="Say greet Files"
|
#
|
||||||
|
# Keys is the last of them and the hardest: it brings a vector of its own, so it needs a
|
||||||
|
# Vector Segment in the output and the version two header that says so. Nothing else here
|
||||||
|
# asks anything of its loader beyond code and data.
|
||||||
|
APPS="Say greet Files Keys"
|
||||||
"$TOOL" put "$WORK/native.img" "$ROOT/Programs/CosmOS/Source/services.asm" services.asm >/dev/null
|
"$TOOL" put "$WORK/native.img" "$ROOT/Programs/CosmOS/Source/services.asm" services.asm >/dev/null
|
||||||
|
"$TOOL" put "$WORK/native.img" "$ROOT/Programs/CosmOS/Source/console.asm" console.asm >/dev/null
|
||||||
for app in $APPS; do
|
for app in $APPS; do
|
||||||
"$TOOL" put "$WORK/native.img" "$ROOT/Programs/CosmOS/Apps/$app.asm" "$app.asm" >/dev/null
|
"$TOOL" put "$WORK/native.img" "$ROOT/Programs/CosmOS/Apps/$app.asm" "$app.asm" >/dev/null
|
||||||
done
|
done
|
||||||
@@ -101,8 +107,8 @@ check "it counted right" grep -q "program 17, data 14, labels 2" "$WORK/session.
|
|||||||
# file in, #Base moves every label to where the program will really live, and every SWI in
|
# file in, #Base moves every label to where the program will really live, and every SWI in
|
||||||
# them names a vector declared in a file the source never mentions by number.
|
# them names a vector declared in a file the source never mentions by number.
|
||||||
for app in $APPS; do
|
for app in $APPS; do
|
||||||
"$ASM" -I "$ROOT/Programs/CosmOS/Source" -o "$WORK/ref-$app.sbx" \
|
"$ASM" -I "$ROOT/Programs/Libraries" -I "$ROOT/Programs/CosmOS/Source" \
|
||||||
"$ROOT/Programs/CosmOS/Apps/$app.asm" >/dev/null
|
-o "$WORK/ref-$app.sbx" "$ROOT/Programs/CosmOS/Apps/$app.asm" >/dev/null
|
||||||
rm -f "$WORK/got-$app.sbx"
|
rm -f "$WORK/got-$app.sbx"
|
||||||
"$TOOL" get "$WORK/native.img" "$app.sbx" "$WORK/got-$app.sbx" >/dev/null 2>&1
|
"$TOOL" get "$WORK/native.img" "$app.sbx" "$WORK/got-$app.sbx" >/dev/null 2>&1
|
||||||
if [ -f "$WORK/got-$app.sbx" ]; then
|
if [ -f "$WORK/got-$app.sbx" ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user