CosmOS pre-alpha and launchable application versions of old programs.
This commit is contained in:
@@ -0,0 +1,388 @@
|
||||
; console.asm
|
||||
; Talking to the console.
|
||||
;
|
||||
; This is the modern replacement for print.asm, which was written for a machine with one
|
||||
; Data Pointer and no vector table. The old one is left where it is, because the programs
|
||||
; that include it still work and are meant to keep working.
|
||||
;
|
||||
; Two things are different here, and both are deliberate.
|
||||
;
|
||||
; There is no branch at the top. print.asm begins with BRI start, so that a program
|
||||
; including it arrives at its own entry point instead of falling into the library. That
|
||||
; was the only way to do it before the Boot vector existed. A program including this file
|
||||
; says where it begins in its own Vector Segment:
|
||||
;
|
||||
; #Vectors
|
||||
; Boot start
|
||||
;
|
||||
; And every routine here names the Data Pointer it works through rather than assuming the
|
||||
; only one. A pointer handed in is DP0. Nothing here disturbs what the caller left in
|
||||
; DP3, which is the one a return survives in.
|
||||
;
|
||||
; What a routine gives back is in Q, because Q and DP3 are the only things a RET does not
|
||||
; put back the way it found them.
|
||||
;
|
||||
; Written by Anachronaut
|
||||
|
||||
#Program
|
||||
|
||||
; ---- Characters and strings ----
|
||||
|
||||
; A line feed.
|
||||
newLine:
|
||||
INIA 0x0A
|
||||
OUTA 0x00
|
||||
RET
|
||||
|
||||
; DP0 names a string ending in a zero byte. Prints it.
|
||||
printString:
|
||||
LDA.0
|
||||
BRA printStringDone
|
||||
OUTA 0x00
|
||||
INCD.0
|
||||
BRI printString
|
||||
printStringDone:
|
||||
RET
|
||||
|
||||
; A holds how many spaces to print. None is a fair answer, and prints nothing.
|
||||
printSpaces:
|
||||
BRA printSpacesDone
|
||||
INIB 0x20
|
||||
printSpacesLoop:
|
||||
OUTB 0x00
|
||||
DECA
|
||||
BNA printSpacesLoop
|
||||
printSpacesDone:
|
||||
RET
|
||||
|
||||
; ---- Hexadecimal ----
|
||||
|
||||
; A holds a byte. Prints it as two hexadecimal digits, high one first.
|
||||
;
|
||||
; A and B are a circular shift register sixteen bits long, so rotating right four times
|
||||
; with B empty walks the high nybble down into place and parks the low one in B. The call
|
||||
; between the two halves puts A and B back as they were, which is what lets the second
|
||||
; rotation find the low nybble still waiting.
|
||||
printByteHex:
|
||||
RSTB
|
||||
SHR SHR SHR SHR
|
||||
CALL printHexDigit
|
||||
RSTA
|
||||
SHL SHL SHL SHL
|
||||
CALL printHexDigit
|
||||
RET
|
||||
|
||||
; DP0 names two bytes, most significant first, the way every number on a SplitBit disk is
|
||||
; stored. Prints them as four hexadecimal digits.
|
||||
printWordHex:
|
||||
LDA.0
|
||||
CALL printByteHex
|
||||
INCD.0
|
||||
LDA.0
|
||||
CALL printByteHex
|
||||
RET
|
||||
|
||||
; A holds a nybble. Prints the one character that stands for it.
|
||||
printHexDigit:
|
||||
INIB 0d10
|
||||
CCF
|
||||
SUB
|
||||
BRC printDecimalDigit ; Under ten, so it is a plain digit.
|
||||
INIB 0x37 ; 'A' is ten, so this is the offset that gets there.
|
||||
CCF
|
||||
ADD
|
||||
OUTQ 0x00
|
||||
RET
|
||||
|
||||
; A holds a digit from zero to nine. Prints it.
|
||||
printDecimalDigit:
|
||||
INIB 0x30
|
||||
CCF
|
||||
ADD
|
||||
OUTQ 0x00
|
||||
RET
|
||||
|
||||
; ---- Decimal ----
|
||||
|
||||
; A holds a byte. Prints it in decimal, without leading zeroes.
|
||||
printByteDecimal:
|
||||
SETD.0 ConsoleValue
|
||||
STA.0
|
||||
SETD.1 ConsoleLeading
|
||||
RSTA
|
||||
STA.1 ; Nothing has been printed yet.
|
||||
|
||||
INIA 0d100
|
||||
CALL printBytePlace
|
||||
INIA 0d10
|
||||
CALL printBytePlace
|
||||
|
||||
; Whatever is left is the ones, and it prints whether or not it is a zero, because a
|
||||
; number has to show at least one digit.
|
||||
SETD.0 ConsoleValue
|
||||
LDA.0
|
||||
CALL printDecimalDigit
|
||||
RET
|
||||
|
||||
; A holds a power of ten. Counts how many times it comes out of ConsoleValue, prints that
|
||||
; as a digit, and leaves the remainder behind. A leading zero prints nothing.
|
||||
printBytePlace:
|
||||
SETD.2 ConsoleBytePower
|
||||
STA.2
|
||||
SETD.0 ConsoleValue
|
||||
SETD.1 ConsoleCount
|
||||
RSTA
|
||||
STA.1
|
||||
printBytePlaceLoop:
|
||||
LDA.0
|
||||
LDB.2
|
||||
CCF
|
||||
SUB
|
||||
BRC printBytePlaceDone ; It went below zero, so it does not come out again.
|
||||
STQ.0
|
||||
LDA.1
|
||||
INCA
|
||||
STA.1
|
||||
BRI printBytePlaceLoop
|
||||
printBytePlaceDone:
|
||||
LDA.1
|
||||
BNA printBytePlaceShow
|
||||
; The digit is a zero, which only prints if something has been printed before it.
|
||||
SETD.2 ConsoleLeading
|
||||
LDA.2
|
||||
BRA printBytePlaceQuiet
|
||||
RSTA
|
||||
printBytePlaceShow:
|
||||
CALL printDecimalDigit
|
||||
SETD.2 ConsoleLeading
|
||||
INIA 0x01
|
||||
STA.2
|
||||
printBytePlaceQuiet:
|
||||
RET
|
||||
|
||||
; DP0 names a two byte number, most significant byte first. Prints it in decimal, without
|
||||
; leading zeroes. Sixty five thousand five hundred and thirty five is the largest thing it
|
||||
; can be handed, which is the whole of an address, so nothing overflows this.
|
||||
printWordDecimal:
|
||||
SETD.1 ConsoleWord
|
||||
CALL consoleCopyWord
|
||||
SETD.1 ConsoleLeading
|
||||
RSTA
|
||||
STA.1
|
||||
|
||||
SETD.0 ConsoleTenThousand
|
||||
CALL printWordPlace
|
||||
SETD.0 ConsoleThousand
|
||||
CALL printWordPlace
|
||||
SETD.0 ConsoleHundred
|
||||
CALL printWordPlace
|
||||
SETD.0 ConsoleTen
|
||||
CALL printWordPlace
|
||||
|
||||
; What is left is under ten, so it is in the low byte and it is the last digit.
|
||||
SETD.0 ConsoleWord
|
||||
INCD.0
|
||||
LDA.0
|
||||
CALL printDecimalDigit
|
||||
RET
|
||||
|
||||
; DP0 names a power of ten, two bytes of it. The same counting as printBytePlace, done
|
||||
; sixteen bits wide.
|
||||
printWordPlace:
|
||||
SETD.1 ConsolePower
|
||||
CALL consoleCopyWord
|
||||
SETD.1 ConsoleCount
|
||||
RSTA
|
||||
STA.1
|
||||
printWordPlaceLoop:
|
||||
CALL consoleTakePower
|
||||
BNQ printWordPlaceDone
|
||||
SETD.1 ConsoleCount
|
||||
LDA.1
|
||||
INCA
|
||||
STA.1
|
||||
BRI printWordPlaceLoop
|
||||
printWordPlaceDone:
|
||||
SETD.1 ConsoleCount
|
||||
LDA.1
|
||||
BNA printWordPlaceShow
|
||||
SETD.1 ConsoleLeading
|
||||
LDA.1
|
||||
BRA printWordPlaceQuiet
|
||||
RSTA
|
||||
printWordPlaceShow:
|
||||
CALL printDecimalDigit
|
||||
SETD.1 ConsoleLeading
|
||||
INIA 0x01
|
||||
STA.1
|
||||
printWordPlaceQuiet:
|
||||
RET
|
||||
|
||||
; Takes ConsolePower out of ConsoleWord, if it comes out at all. Q is zero if it did, and
|
||||
; then ConsoleWord is the smaller for it. If it did not, ConsoleWord is left alone.
|
||||
;
|
||||
; The subtraction is done into a spare word rather than in place, because whether it fits
|
||||
; is not known until the high half is done, and by then an in place low half would already
|
||||
; have been spent.
|
||||
;
|
||||
; The low half clears the Carry Flag first and the high half does not: the borrow the low
|
||||
; half leaves behind is exactly what the high half has to subtract as well. Nothing
|
||||
; between them disturbs it, since only the arithmetic instructions and CCF touch it.
|
||||
consoleTakePower:
|
||||
SETD.0 ConsoleWord
|
||||
INCD.0
|
||||
SETD.1 ConsolePower
|
||||
INCD.1
|
||||
LDA.0
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
SETD.2 ConsoleSpare
|
||||
INCD.2
|
||||
STQ.2
|
||||
|
||||
SETD.0 ConsoleWord
|
||||
SETD.1 ConsolePower
|
||||
LDA.0
|
||||
LDB.1
|
||||
SUB
|
||||
BRC consoleTakeNothing
|
||||
SETD.2 ConsoleSpare
|
||||
STQ.2
|
||||
|
||||
SETD.0 ConsoleSpare
|
||||
SETD.1 ConsoleWord
|
||||
CALL consoleCopyWord
|
||||
RSTA
|
||||
RSTB
|
||||
CCF
|
||||
ADD ; Q is zero: it came out.
|
||||
RET
|
||||
consoleTakeNothing:
|
||||
RSTA
|
||||
INIB 0x01
|
||||
CCF
|
||||
ADD ; Q is one: it did not.
|
||||
RET
|
||||
|
||||
; Two bytes from DP0 to DP1, most significant first.
|
||||
consoleCopyWord:
|
||||
LDA.0
|
||||
STA.1
|
||||
INCD.0
|
||||
INCD.1
|
||||
LDA.0
|
||||
STA.1
|
||||
RET
|
||||
|
||||
; ---- Reading ----
|
||||
|
||||
; DP0 names a buffer and B says how many characters it holds, not counting the zero byte
|
||||
; that ends it. Reads a line from the console into it. Q is how long the line turned out
|
||||
; to be.
|
||||
;
|
||||
; A line longer than the buffer is cut short, and the rest of it is read and thrown away
|
||||
; rather than left to turn up as the next line.
|
||||
;
|
||||
; ConsoleEndOfInput is set if the console ran out instead of ending a line. That is a
|
||||
; different thing from an empty line, and a program that reads until there is no more has
|
||||
; to be able to tell them apart.
|
||||
readLine:
|
||||
SETD.1 ConsoleRoom
|
||||
STB.1
|
||||
SETD.1 ConsoleLength
|
||||
RSTA
|
||||
STA.1
|
||||
SETD.1 ConsoleEndOfInput
|
||||
STA.1
|
||||
readLineNext:
|
||||
INA 0x00
|
||||
INIB 0x0A
|
||||
CCF
|
||||
SUB
|
||||
BRQ readLineDone ; A line feed ends the line. A is still the character.
|
||||
INIB 0xFF
|
||||
CCF
|
||||
SUB
|
||||
BRQ readLineEnd ; There is no more to be had.
|
||||
|
||||
; Is there room for it? A still holds the character, so it is put somewhere safe while
|
||||
; the counting is done.
|
||||
SETD.1 ConsoleChar
|
||||
STA.1
|
||||
SETD.1 ConsoleLength
|
||||
LDA.1
|
||||
SETD.1 ConsoleRoom
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
BRQ readLineNext ; Full. Read on, and drop what comes.
|
||||
|
||||
SETD.1 ConsoleChar
|
||||
LDA.1
|
||||
STA.0
|
||||
INCD.0
|
||||
SETD.1 ConsoleLength
|
||||
LDA.1
|
||||
INCA
|
||||
STA.1
|
||||
BRI readLineNext
|
||||
readLineEnd:
|
||||
SETD.1 ConsoleEndOfInput
|
||||
INIA 0x01
|
||||
STA.1
|
||||
readLineDone:
|
||||
RSTA
|
||||
STA.0 ; The zero byte that ends it.
|
||||
SETD.1 ConsoleLength
|
||||
LDA.1
|
||||
RSTB
|
||||
CCF
|
||||
ADD ; Q is how long the line is.
|
||||
RET
|
||||
|
||||
#Data
|
||||
|
||||
; ---- What readLine keeps while it works ----
|
||||
|
||||
ConsoleRoom:
|
||||
0x00
|
||||
ConsoleLength:
|
||||
0x00
|
||||
ConsoleChar:
|
||||
0x00
|
||||
|
||||
; Set when the console ran out rather than ending a line. Cleared at the start of every
|
||||
; readLine, so it always describes the last line read.
|
||||
ConsoleEndOfInput:
|
||||
0x00
|
||||
|
||||
; ---- What the number routines keep while they work ----
|
||||
|
||||
; Whether any digit has been printed yet, which is what decides if a zero is a leading
|
||||
; one or a real one.
|
||||
ConsoleLeading:
|
||||
0x00
|
||||
ConsoleCount:
|
||||
0x00
|
||||
ConsoleValue:
|
||||
0x00
|
||||
ConsoleBytePower:
|
||||
0x00
|
||||
ConsolePower:
|
||||
0x00 0x00
|
||||
ConsoleWord:
|
||||
0x00 0x00
|
||||
ConsoleSpare:
|
||||
0x00 0x00
|
||||
|
||||
; The powers of ten, written the way every number here is written: most significant byte
|
||||
; first.
|
||||
ConsoleTenThousand:
|
||||
0x27 0x10
|
||||
ConsoleThousand:
|
||||
0x03 0xE8
|
||||
ConsoleHundred:
|
||||
0x00 0x64
|
||||
ConsoleTen:
|
||||
0x00 0x0A
|
||||
Reference in New Issue
Block a user