CosmOS pre-alpha and launchable application versions of old programs.
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
; Exercises the console's status and control ports.
|
||||
;
|
||||
; The console answers on three ports now: 0x00 for bytes as it always did, 0x01 for what
|
||||
; it is doing, and 0x02 to say which mode it should be in. The point of the mode is that
|
||||
; the data port never changed, so this checks the old behaviour is still there as much as
|
||||
; it checks the new behaviour arrived.
|
||||
;
|
||||
; What the status bits mean:
|
||||
; bit 0 READY reading the data port will not have to wait
|
||||
; bit 1 ENDED input has run out for good
|
||||
; bit 2 KEYMODE the console is in key mode
|
||||
;
|
||||
; This runs with input from a file rather than a terminal, so key mode has no terminal to
|
||||
; put into another state and the mode bit is the only thing that changes. That is on
|
||||
; purpose: the same program has to work either way, and a test that needed a terminal
|
||||
; could not run here at all.
|
||||
|
||||
#Include console.asm
|
||||
|
||||
#Program
|
||||
|
||||
start:
|
||||
SETD.0 Banner
|
||||
CALL printString
|
||||
CALL newLine
|
||||
|
||||
; ---- What mode does it start in ----
|
||||
;
|
||||
; Nothing has been written to the control port, so this is how the machine comes up, and
|
||||
; every program written before these ports existed runs in exactly this.
|
||||
SETD.0 AtStartLabel
|
||||
CALL printString
|
||||
CALL showStatus
|
||||
|
||||
; ---- Into key mode ----
|
||||
INIA 0x01
|
||||
OUTA 0x02
|
||||
SETD.0 KeyModeLabel
|
||||
CALL printString
|
||||
CALL showStatus
|
||||
|
||||
; ---- Read what is waiting ----
|
||||
;
|
||||
; Reading until ENDED rather than until a count, which is the thing that could not be
|
||||
; done before: 0xFF from the data port used to mean both "the byte 0xFF" and "there is
|
||||
; no more", and nothing could tell those apart.
|
||||
SETD.0 ReadLabel
|
||||
CALL printString
|
||||
CALL newLine
|
||||
readLoop:
|
||||
INA 0x01
|
||||
INIB 0x02 ; ENDED
|
||||
AND
|
||||
BNQ readDone
|
||||
INA 0x00
|
||||
OUTA 0x00 ; Nothing echoes in key mode, so the program does it.
|
||||
BRI readLoop
|
||||
readDone:
|
||||
CALL newLine
|
||||
|
||||
; ---- The status at the end of input ----
|
||||
;
|
||||
; READY is set as well as ENDED, because a read does answer at once. It just answers
|
||||
; 0xFF forever. ENDED is what says so.
|
||||
SETD.0 AtEndLabel
|
||||
CALL printString
|
||||
CALL showStatus
|
||||
|
||||
; ---- Back to line mode ----
|
||||
RSTA
|
||||
OUTA 0x02
|
||||
SETD.0 LineModeLabel
|
||||
CALL printString
|
||||
CALL showStatus
|
||||
|
||||
HALT
|
||||
|
||||
; Prints the status byte as hex and then names the bits that are up, so that a change in
|
||||
; the output says which bit moved rather than only that the number is different.
|
||||
showStatus:
|
||||
INA 0x01
|
||||
PSHA
|
||||
CALL printByteHex
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
POPA
|
||||
PSHA
|
||||
|
||||
INIB 0x01
|
||||
AND
|
||||
BRQ showNotReady
|
||||
SETD.0 ReadyWord
|
||||
CALL printString
|
||||
showNotReady:
|
||||
POPA
|
||||
PSHA
|
||||
|
||||
INIB 0x02
|
||||
AND
|
||||
BRQ showNotEnded
|
||||
SETD.0 EndedWord
|
||||
CALL printString
|
||||
showNotEnded:
|
||||
POPA
|
||||
|
||||
INIB 0x04
|
||||
AND
|
||||
BRQ showNotKeys
|
||||
SETD.0 KeysWord
|
||||
CALL printString
|
||||
showNotKeys:
|
||||
CALL newLine
|
||||
RET
|
||||
|
||||
#Data
|
||||
|
||||
Banner:
|
||||
"console mode ports"
|
||||
|
||||
AtStartLabel:
|
||||
"at start: "
|
||||
KeyModeLabel:
|
||||
"key mode: "
|
||||
AtEndLabel:
|
||||
"at the end: "
|
||||
LineModeLabel:
|
||||
"line mode: "
|
||||
ReadLabel:
|
||||
"what came in:"
|
||||
|
||||
ReadyWord:
|
||||
"ready "
|
||||
EndedWord:
|
||||
"ended "
|
||||
KeysWord:
|
||||
"keys "
|
||||
|
||||
#Vectors
|
||||
|
||||
Boot start
|
||||
@@ -0,0 +1,201 @@
|
||||
; Exercises console.asm, the modern console library.
|
||||
;
|
||||
; Every routine in it is called here with the cases that are easy to get wrong: a zero,
|
||||
; the largest thing that fits, the boundary either side of a carry, and a leading zero
|
||||
; that should not print. The reading half is driven by consoleTest.in, which ends with a
|
||||
; line longer than the buffer so that the truncation and the swallowing of the rest are
|
||||
; both shown rather than assumed.
|
||||
;
|
||||
; There is no BRI at the top of console.asm, so this program says where it begins in its
|
||||
; Vector Segment. That is the whole of the arrangement the old library needed a branch for.
|
||||
|
||||
#Include console.asm
|
||||
|
||||
#Program
|
||||
|
||||
start:
|
||||
SETD.0 Banner
|
||||
CALL printString
|
||||
CALL newLine
|
||||
|
||||
; ---- Hexadecimal ----
|
||||
SETD.0 HexLabel
|
||||
CALL printString
|
||||
RSTA
|
||||
CALL printByteHex
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
INIA 0x0F
|
||||
CALL printByteHex
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
INIA 0xA5
|
||||
CALL printByteHex
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
INIA 0xFF
|
||||
CALL printByteHex
|
||||
CALL newLine
|
||||
|
||||
SETD.0 WordHexLabel
|
||||
CALL printString
|
||||
SETD.0 Zero
|
||||
CALL printWordHex
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
SETD.0 Thousand
|
||||
CALL printWordHex
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
SETD.0 Largest
|
||||
CALL printWordHex
|
||||
CALL newLine
|
||||
|
||||
; ---- Decimal, one byte wide ----
|
||||
; Zero has to print a digit, and a hundred has to not print its tens as a blank.
|
||||
SETD.0 ByteLabel
|
||||
CALL printString
|
||||
RSTA
|
||||
CALL printByteDecimal
|
||||
CALL blank
|
||||
INIA 0d7
|
||||
CALL printByteDecimal
|
||||
CALL blank
|
||||
INIA 0d42
|
||||
CALL printByteDecimal
|
||||
CALL blank
|
||||
INIA 0d100
|
||||
CALL printByteDecimal
|
||||
CALL blank
|
||||
INIA 0d255
|
||||
CALL printByteDecimal
|
||||
CALL newLine
|
||||
|
||||
; ---- Decimal, two bytes wide ----
|
||||
; Ten thousand and one is the case that catches a place that stops counting too soon,
|
||||
; and the three zeroes in the middle of it are the case that catches a leading zero
|
||||
; test applied after the first digit.
|
||||
SETD.0 WordLabel
|
||||
CALL printString
|
||||
SETD.0 Zero
|
||||
CALL printWordDecimal
|
||||
CALL blank
|
||||
SETD.0 Nine
|
||||
CALL printWordDecimal
|
||||
CALL blank
|
||||
SETD.0 Ten
|
||||
CALL printWordDecimal
|
||||
CALL blank
|
||||
SETD.0 TwoFiveFive
|
||||
CALL printWordDecimal
|
||||
CALL blank
|
||||
SETD.0 Thousand
|
||||
CALL printWordDecimal
|
||||
CALL blank
|
||||
SETD.0 TenThousandOne
|
||||
CALL printWordDecimal
|
||||
CALL blank
|
||||
SETD.0 Largest
|
||||
CALL printWordDecimal
|
||||
CALL newLine
|
||||
|
||||
; ---- Spaces ----
|
||||
; None of them is a fair number to ask for, and has to print nothing at all.
|
||||
SETD.0 SpaceLabel
|
||||
CALL printString
|
||||
INIA 0x7C
|
||||
OUTA 0x00
|
||||
RSTA
|
||||
CALL printSpaces
|
||||
INIA 0x7C
|
||||
OUTA 0x00
|
||||
INIA 0d4
|
||||
CALL printSpaces
|
||||
INIA 0x7C
|
||||
OUTA 0x00
|
||||
CALL newLine
|
||||
|
||||
; ---- Reading ----
|
||||
SETD.0 ReadLabel
|
||||
CALL printString
|
||||
CALL newLine
|
||||
|
||||
readLoop:
|
||||
SETD.0 LineBuffer
|
||||
INIB 0d16
|
||||
CALL readLine
|
||||
SETD.0 LineLength
|
||||
STQ.0
|
||||
|
||||
; Running out is not an empty line, and this is where the difference shows.
|
||||
SETD.0 ConsoleEndOfInput
|
||||
LDA.0
|
||||
BNA readDone
|
||||
|
||||
INIA 0x5B
|
||||
OUTA 0x00
|
||||
SETD.0 LineBuffer
|
||||
CALL printString
|
||||
INIA 0x5D
|
||||
OUTA 0x00
|
||||
CALL blank
|
||||
SETD.0 LineLength
|
||||
LDA.0
|
||||
CALL printByteDecimal
|
||||
CALL newLine
|
||||
BRI readLoop
|
||||
|
||||
readDone:
|
||||
SETD.0 Ended
|
||||
CALL printString
|
||||
CALL newLine
|
||||
HALT
|
||||
|
||||
; One space. Used between the numbers so that each line reads as a list.
|
||||
blank:
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
RET
|
||||
|
||||
#Data
|
||||
|
||||
Banner:
|
||||
"console test"
|
||||
HexLabel:
|
||||
"byte hex: "
|
||||
WordHexLabel:
|
||||
"word hex: "
|
||||
ByteLabel:
|
||||
"byte dec: "
|
||||
WordLabel:
|
||||
"word dec: "
|
||||
SpaceLabel:
|
||||
"spaces: "
|
||||
ReadLabel:
|
||||
"lines read:"
|
||||
Ended:
|
||||
"end of input"
|
||||
|
||||
Zero:
|
||||
0x00 0x00
|
||||
Nine:
|
||||
0x00 0x09
|
||||
Ten:
|
||||
0x00 0x0A
|
||||
TwoFiveFive:
|
||||
0x00 0xFF
|
||||
Thousand:
|
||||
0x03 0xE8
|
||||
TenThousandOne:
|
||||
0x27 0x11
|
||||
Largest:
|
||||
0xFF 0xFF
|
||||
|
||||
LineLength:
|
||||
0x00
|
||||
LineBuffer:
|
||||
#Reserve 0d17
|
||||
|
||||
#Vectors
|
||||
|
||||
Boot start
|
||||
@@ -0,0 +1,22 @@
|
||||
; A program that bases one segment and forgets the other.
|
||||
;
|
||||
; This is the shape of a real bug: the original Fib-8, brought over to CosmOS as an
|
||||
; application, based its code at 0x2000 but got its Data Segment from an included library
|
||||
; and never based it. The three bytes went to 0x0000, on top of the console's own
|
||||
; variables, and it appeared to work because those three bytes are scratch that readLine
|
||||
; rewrites before every use. A larger data segment would have reached the decimal table
|
||||
; and the mounted filesystem behind it, and failed somewhere else entirely.
|
||||
;
|
||||
; Nothing relocates, so the assembler is the last place to catch it.
|
||||
|
||||
#Program
|
||||
|
||||
#Base 0x2000
|
||||
|
||||
start:
|
||||
HALT
|
||||
|
||||
#Data
|
||||
|
||||
value:
|
||||
0x00
|
||||
@@ -0,0 +1,132 @@
|
||||
; Walking the directory of a SplitBit filesystem.
|
||||
;
|
||||
; sbfsFind answers a question about one name. This is the other thing a directory is for:
|
||||
; being listed. The disk was made by SplitDisk, so the order and contents here are what
|
||||
; the other implementation of the format wrote, not what this one assumed.
|
||||
;
|
||||
; The cases that matter are all on this disk already. There are twelve files, which is
|
||||
; more than the eight an entry block holds, so the walk has to cross from the first
|
||||
; directory block into the second. aName22CharactersLong! fills the name field exactly and
|
||||
; so has no zero byte to end it, which is what the twenty third byte of SbfsName is for.
|
||||
; empty.txt has no blocks at all.
|
||||
;
|
||||
; The count at the end is the proof that the walk stopped where the directory did. A walk
|
||||
; that stepped its pointer only when it took an entry would loop forever on a free one,
|
||||
; and one that stepped only when it skipped would hand out every eighth file.
|
||||
;
|
||||
; Correct output is the twelve files with their sizes, then how many there were.
|
||||
|
||||
#Include console.asm
|
||||
#Include sbfs.asm
|
||||
|
||||
#Program
|
||||
|
||||
start:
|
||||
CALL sbfsMount
|
||||
BRQ mounted
|
||||
SETD.0 NoMount
|
||||
CALL printString
|
||||
CALL newLine
|
||||
HALT
|
||||
|
||||
mounted:
|
||||
; Nothing found yet.
|
||||
RSTA
|
||||
SETD.0 Seen
|
||||
STA.0
|
||||
|
||||
CALL sbfsFirst
|
||||
BRI walkCheck
|
||||
|
||||
walkStep:
|
||||
CALL sbfsNext
|
||||
walkCheck:
|
||||
BNQ walkDone
|
||||
|
||||
SETD.0 Seen
|
||||
LDA.0
|
||||
INCA
|
||||
STA.0
|
||||
|
||||
SETD.0 SbfsName
|
||||
CALL printString
|
||||
|
||||
; Line the sizes up, so a name that fills the field and a short one both read cleanly.
|
||||
SETD.0 SbfsName
|
||||
CALL nameWidth
|
||||
MVQA
|
||||
CALL printSpaces
|
||||
|
||||
; A file is its blocks times 256 plus its tail, which is the block count in the high
|
||||
; byte and the tail in the low one. Nothing has to multiply anything.
|
||||
SETD.0 SbfsFileBlocks
|
||||
DPUP.0 0d01
|
||||
LDA.0
|
||||
SETD.1 Size
|
||||
STA.1
|
||||
SETD.0 SbfsFileTail
|
||||
LDA.0
|
||||
SETD.1 Size
|
||||
INCD.1
|
||||
STA.1
|
||||
|
||||
SETD.0 Size
|
||||
CALL printWordDecimal
|
||||
CALL newLine
|
||||
BRI walkStep
|
||||
|
||||
walkDone:
|
||||
SETD.0 Total
|
||||
CALL printString
|
||||
SETD.0 Seen
|
||||
LDA.0
|
||||
CALL printByteDecimal
|
||||
CALL newLine
|
||||
HALT
|
||||
|
||||
; DP0 names a string. Q is how many spaces would pad it out to twenty four characters.
|
||||
; A name that is already that long gets one space, because none at all would run the
|
||||
; name into the number.
|
||||
nameWidth:
|
||||
INIA 0d24
|
||||
SETD.1 Padding
|
||||
STA.1
|
||||
widthLoop:
|
||||
LDA.0
|
||||
BRA widthDone
|
||||
SETD.1 Padding
|
||||
LDA.1
|
||||
DECA
|
||||
STA.1
|
||||
BRA widthFloor ; It is already too long to pad.
|
||||
INCD.0
|
||||
BRI widthLoop
|
||||
widthFloor:
|
||||
INIA 0d1
|
||||
SETD.1 Padding
|
||||
STA.1
|
||||
widthDone:
|
||||
SETD.1 Padding
|
||||
LDA.1
|
||||
RSTB
|
||||
CCF
|
||||
ADD
|
||||
RET
|
||||
|
||||
#Data
|
||||
|
||||
NoMount:
|
||||
"no filesystem on that disk"
|
||||
Total:
|
||||
"files: "
|
||||
|
||||
Seen:
|
||||
0x00
|
||||
Padding:
|
||||
0x00
|
||||
Size:
|
||||
0x00 0x00
|
||||
|
||||
#Vectors
|
||||
|
||||
Boot start
|
||||
@@ -0,0 +1,210 @@
|
||||
; Tests MVDS, which copies a Data Pointer into the Stack Pointer.
|
||||
;
|
||||
; This is the dangerous one. Moving the Stack Pointer abandons everything below the new
|
||||
; position: return addresses, saved registers, interrupt frames, all of it. Nothing is
|
||||
; unwound, because moving the Stack does not move what is on it.
|
||||
;
|
||||
; It is here for one job, and that job is what this program acts out. A system that runs
|
||||
; other programs and takes the machine back afterwards cannot do so without it. A program
|
||||
; that gives up part way through leaves whatever it pushed behind, and nothing is ever
|
||||
; going to return and tidy it away. Without MVDS the Stack only ever moves downward, a
|
||||
; little further with every program run, and a shell cannot outlive many of them.
|
||||
;
|
||||
; The other half of the story is that moving the Stack does not destroy what was on it.
|
||||
; A routine that puts the Stack Pointer back where it found it returns perfectly normally,
|
||||
; because the frame was only stepped away from. Both halves are acted out here, and the
|
||||
; nesting case with them: two routines that each borrow a Stack, one inside the other,
|
||||
; each keeping its own saved Stack Pointer on the Stack it borrowed. A fixed location in
|
||||
; Data Memory would fail exactly there, and would fail silently.
|
||||
;
|
||||
; Both sequences are the ones the Programming Manual prints under "The Stack Pointer,
|
||||
; Set By Hand", so if either example ever stops working this test says so.
|
||||
;
|
||||
; Correct output is:
|
||||
; stack reclaim test
|
||||
; at start: FFFF
|
||||
; spent: FFF7
|
||||
; reclaimed: FFFF
|
||||
; still good: 33
|
||||
; borrowed: 7 and 9
|
||||
; back home: FFFF
|
||||
|
||||
#Include console.asm
|
||||
|
||||
#Program
|
||||
|
||||
start:
|
||||
SETD.0 Banner
|
||||
CALL printString
|
||||
CALL newLine
|
||||
|
||||
; Where the Stack is before anything has touched it. Read first, so that no call has
|
||||
; had a chance to move it.
|
||||
MVSD.0
|
||||
SETD.1 SavedStack
|
||||
STD.0.1
|
||||
|
||||
SETD.0 StartLabel
|
||||
CALL printString
|
||||
SETD.0 SavedStack
|
||||
CALL printWordHex
|
||||
CALL newLine
|
||||
|
||||
; Spend some Stack and never give it back. This is not a subroutine, because a
|
||||
; subroutine that pushed without popping would take its own return address with it.
|
||||
; It stands in for a program that stops half way through.
|
||||
INIA 0x11
|
||||
PSHA PSHA PSHA PSHA
|
||||
PSHA PSHA PSHA PSHA
|
||||
|
||||
MVSD.0
|
||||
SETD.1 Spent
|
||||
STD.0.1
|
||||
|
||||
SETD.0 SpentLabel
|
||||
CALL printString
|
||||
SETD.0 Spent
|
||||
CALL printWordHex
|
||||
CALL newLine
|
||||
|
||||
; Take it back. Nothing pops those eight bytes: they are simply no longer on the Stack,
|
||||
; which is the whole of what reclaiming means.
|
||||
SETD.1 SavedStack
|
||||
LDD.0.1
|
||||
MVDS.0
|
||||
|
||||
MVSD.0
|
||||
SETD.1 Reclaimed
|
||||
STD.0.1
|
||||
|
||||
SETD.0 ReclaimedLabel
|
||||
CALL printString
|
||||
SETD.0 Reclaimed
|
||||
CALL printWordHex
|
||||
CALL newLine
|
||||
|
||||
; A Stack Pointer in the right place is not proof that the Stack works. Push something
|
||||
; through it and get it back, so that the reclaimed Stack is shown to be usable and not
|
||||
; merely well positioned.
|
||||
SETD.0 GoodLabel
|
||||
CALL printString
|
||||
INIA 0d33
|
||||
PSHA
|
||||
RSTA
|
||||
POPA
|
||||
CALL printByteDecimal
|
||||
CALL newLine
|
||||
|
||||
; ---- Moving the Stack and coming back from it ----
|
||||
; borrower runs on a Stack of its own and returns with a plain RET, which only works
|
||||
; because putting the Stack Pointer back leaves the frame exactly as CALL left it.
|
||||
CALL borrower
|
||||
|
||||
SETD.0 BorrowedLabel
|
||||
CALL printString
|
||||
SETD.0 OuterResult
|
||||
LDA.0
|
||||
CALL printByteDecimal
|
||||
SETD.0 AndText
|
||||
CALL printString
|
||||
SETD.0 InnerResult
|
||||
LDA.0
|
||||
CALL printByteDecimal
|
||||
CALL newLine
|
||||
|
||||
; The Stack has to be back where it started, or the nesting quietly lost track of it.
|
||||
MVSD.0
|
||||
SETD.1 Reclaimed
|
||||
STD.0.1
|
||||
SETD.0 HomeLabel
|
||||
CALL printString
|
||||
SETD.0 Reclaimed
|
||||
CALL printWordHex
|
||||
CALL newLine
|
||||
HALT
|
||||
|
||||
; Borrows a Stack, works on it, calls something that does the same thing again, then puts
|
||||
; the Stack back and returns. The old Stack Pointer goes onto the borrowed Stack rather
|
||||
; than into a fixed place in memory, which is what makes the nesting safe: the inner
|
||||
; routine keeps its own copy on its own Stack and cannot tread on this one.
|
||||
borrower:
|
||||
MVSD.3
|
||||
SETD.0 OuterTop
|
||||
MVDS.0
|
||||
PSHD.3
|
||||
|
||||
INIA 0d7
|
||||
PSHA
|
||||
RSTA
|
||||
POPA
|
||||
SETD.1 OuterResult
|
||||
STA.1
|
||||
|
||||
CALL innerBorrower
|
||||
|
||||
POPD.3
|
||||
MVDS.3
|
||||
RET
|
||||
|
||||
; The same again, one level down and on a Stack of its own.
|
||||
innerBorrower:
|
||||
MVSD.3
|
||||
SETD.0 InnerTop
|
||||
MVDS.0
|
||||
PSHD.3
|
||||
|
||||
INIA 0d9
|
||||
PSHA
|
||||
RSTA
|
||||
POPA
|
||||
SETD.1 InnerResult
|
||||
STA.1
|
||||
|
||||
POPD.3
|
||||
MVDS.3
|
||||
RET
|
||||
|
||||
#Data
|
||||
|
||||
Banner:
|
||||
"stack reclaim test"
|
||||
StartLabel:
|
||||
"at start: "
|
||||
SpentLabel:
|
||||
"spent: "
|
||||
ReclaimedLabel:
|
||||
"reclaimed: "
|
||||
GoodLabel:
|
||||
"still good: "
|
||||
BorrowedLabel:
|
||||
"borrowed: "
|
||||
AndText:
|
||||
" and "
|
||||
HomeLabel:
|
||||
"back home: "
|
||||
|
||||
SavedStack:
|
||||
0x00 0x00
|
||||
Spent:
|
||||
0x00 0x00
|
||||
Reclaimed:
|
||||
0x00 0x00
|
||||
OuterResult:
|
||||
0x00
|
||||
InnerResult:
|
||||
0x00
|
||||
|
||||
; Two places a Stack can live, sixty four bytes each. The label is on the last byte of
|
||||
; each, because a Stack grows downward from wherever it is set.
|
||||
OuterStack:
|
||||
#Reserve 0d63
|
||||
OuterTop:
|
||||
0x00
|
||||
InnerStack:
|
||||
#Reserve 0d63
|
||||
InnerTop:
|
||||
0x00
|
||||
|
||||
#Vectors
|
||||
|
||||
Boot start
|
||||
@@ -0,0 +1,204 @@
|
||||
; Exercises text.asm, which is how CosmOS understands what was typed at it.
|
||||
;
|
||||
; The cases here are the ones that decide whether a shell feels right or feels broken.
|
||||
; textSame has to say that "dir" and "dirty" are different words, or every command would
|
||||
; match its own prefix. textSplit has to give an empty rest rather than a missing one when
|
||||
; there is no argument, so that "load" with nothing after it is a question the shell can
|
||||
; answer. And textHexWord has to take upper and lower case alike, refuse things that only
|
||||
; look like numbers, and push digits off the top rather than refusing a fifth one.
|
||||
;
|
||||
; Correct output is:
|
||||
; split: [dump] [program 2000]
|
||||
; split: [dir] []
|
||||
; same: yes no no no
|
||||
; hex: 2000 00FF 00FF BEEF FFFF 0000
|
||||
; 0 is a fine way to begin a string
|
||||
; no number here
|
||||
|
||||
#Include console.asm
|
||||
#Include text.asm
|
||||
|
||||
#Program
|
||||
|
||||
start:
|
||||
; ---- Splitting ----
|
||||
SETD.0 LineOne
|
||||
CALL textSplit
|
||||
SETD.0 SplitLabel
|
||||
CALL printString
|
||||
INIA 0x5B
|
||||
OUTA 0x00
|
||||
SETD.0 LineOne
|
||||
CALL printString
|
||||
INIA 0x5D
|
||||
OUTA 0x00
|
||||
CALL blank
|
||||
INIA 0x5B
|
||||
OUTA 0x00
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
CALL printString
|
||||
INIA 0x5D
|
||||
OUTA 0x00
|
||||
CALL newLine
|
||||
|
||||
; One word on its own. The rest has to be empty rather than absent.
|
||||
SETD.0 LineTwo
|
||||
CALL textSplit
|
||||
SETD.0 SplitLabel
|
||||
CALL printString
|
||||
INIA 0x5B
|
||||
OUTA 0x00
|
||||
SETD.0 LineTwo
|
||||
CALL printString
|
||||
INIA 0x5D
|
||||
OUTA 0x00
|
||||
CALL blank
|
||||
INIA 0x5B
|
||||
OUTA 0x00
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
CALL printString
|
||||
INIA 0x5D
|
||||
OUTA 0x00
|
||||
CALL newLine
|
||||
|
||||
; ---- Comparing ----
|
||||
; The same, then longer, then shorter, then different. The middle two are what a
|
||||
; comparison that stopped at the first ending would get wrong.
|
||||
SETD.0 SameLabel
|
||||
CALL printString
|
||||
SETD.0 WordDir
|
||||
SETD.1 WordDir2
|
||||
CALL textSame
|
||||
CALL sayQ
|
||||
SETD.0 WordDir
|
||||
SETD.1 WordDirty
|
||||
CALL textSame
|
||||
CALL sayQ
|
||||
SETD.0 WordDirty
|
||||
SETD.1 WordDir2
|
||||
CALL textSame
|
||||
CALL sayQ
|
||||
SETD.0 WordDir
|
||||
SETD.1 WordRun
|
||||
CALL textSame
|
||||
CALL sayQ
|
||||
CALL newLine
|
||||
|
||||
; ---- Reading numbers ----
|
||||
SETD.0 HexLabel
|
||||
CALL printString
|
||||
SETD.0 HexPlain
|
||||
CALL showHex
|
||||
SETD.0 HexUpper
|
||||
CALL showHex
|
||||
SETD.0 HexLower
|
||||
CALL showHex
|
||||
SETD.0 HexMixed
|
||||
CALL showHex
|
||||
SETD.0 HexTooMany
|
||||
CALL showHex
|
||||
SETD.0 HexStops
|
||||
CALL showHex
|
||||
CALL newLine
|
||||
|
||||
; A string may begin with anything, including a zero. The assembler strips the quotes
|
||||
; before it decides what a token is, so a string starting with a zero looked exactly
|
||||
; like a malformed literal and was rejected as one. This line is here so that it cannot
|
||||
; go back to being rejected quietly.
|
||||
SETD.0 ZeroString
|
||||
CALL printString
|
||||
CALL newLine
|
||||
|
||||
; Something that is not a number at all has to be refused rather than read as zero.
|
||||
SETD.0 HexNone
|
||||
CALL textHexWord
|
||||
BRQ hexUnexpected
|
||||
SETD.0 NoNumber
|
||||
CALL printString
|
||||
CALL newLine
|
||||
HALT
|
||||
hexUnexpected:
|
||||
SETD.0 Unexpected
|
||||
CALL printString
|
||||
CALL newLine
|
||||
HALT
|
||||
|
||||
; DP0 names text. Reads a number from it and prints what came out.
|
||||
showHex:
|
||||
CALL textHexWord
|
||||
SETD.0 TextValue
|
||||
CALL printWordHex
|
||||
CALL blank
|
||||
RET
|
||||
|
||||
; Prints yes if Q is zero and no if it is not. Q survives a RET, which is what lets the
|
||||
; answer get this far.
|
||||
sayQ:
|
||||
BNQ sayNo
|
||||
SETD.0 YesText
|
||||
CALL printString
|
||||
RET
|
||||
sayNo:
|
||||
SETD.0 NoText
|
||||
CALL printString
|
||||
RET
|
||||
|
||||
blank:
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
RET
|
||||
|
||||
#Data
|
||||
|
||||
SplitLabel:
|
||||
"split: "
|
||||
SameLabel:
|
||||
"same: "
|
||||
HexLabel:
|
||||
"hex: "
|
||||
YesText:
|
||||
"yes "
|
||||
NoText:
|
||||
"no "
|
||||
NoNumber:
|
||||
"no number here"
|
||||
ZeroString:
|
||||
"0 is a fine way to begin a string"
|
||||
Unexpected:
|
||||
"a number was found where there is none"
|
||||
|
||||
; textSplit writes over the space it cuts at, so these are written out rather than shared.
|
||||
LineOne:
|
||||
"dump program 2000"
|
||||
LineTwo:
|
||||
"dir"
|
||||
|
||||
WordDir:
|
||||
"dir"
|
||||
WordDir2:
|
||||
"dir"
|
||||
WordDirty:
|
||||
"dirty"
|
||||
WordRun:
|
||||
"run"
|
||||
|
||||
HexPlain:
|
||||
"2000"
|
||||
HexUpper:
|
||||
"FF"
|
||||
HexLower:
|
||||
"ff"
|
||||
HexMixed:
|
||||
"bEeF"
|
||||
HexTooMany:
|
||||
"12FFFF"
|
||||
HexStops:
|
||||
"0 and more"
|
||||
HexNone:
|
||||
"zebra"
|
||||
|
||||
#Vectors
|
||||
|
||||
Boot start
|
||||
Reference in New Issue
Block a user