CosmOS pre-alpha and launchable application versions of old programs.

This commit is contained in:
Anachronaut
2026-08-17 15:31:49 -04:00
parent eff6902bcf
commit 91c9d49d1b
66 changed files with 5612 additions and 160 deletions
+84
View File
@@ -0,0 +1,84 @@
; A Fibonacci number generating program that uses two bytes to store the value.
#Include services.asm
#Program
#Base 0x2000
start:
; Swap ValueB and ValueA.
; First, store ValueA on the stack.
SETD ValueA
LDA
PSHA
INCD
LDA
PSHA
; Now copy ValueB into AB.
SETD ValueB
LDA ; High byte
INCD
LDB ; Low byte
; Now save it back to ValueA
SETD ValueA
STA ; High byte
INCD
STB ; Low byte.
; Now retrieve value A from the stack and store it in ValueB.
POPB
POPA
SETD ValueB
STA
INCD
STB
; Print ValueA.
SETD ValueA
LDA
CALL printByteHex
INCD
LDA
CALL printByteHex
CALL blankSpace
; Now add ValueA and ValueB, and store the result in ValueA.
; Add the low bytes of ValueA and ValueB
SETD ValueB
INCD
LDA
SETD ValueA
INCD
LDB
CCF
ADD
; Store the result in ValueA.
STQ
; Now add the high bytes of ValueA and ValueB.
DECD
LDB
SETD ValueB
LDA
ADD
; If this addition overflows, we're done.
BRC end
; Otherwise, store the result in ValueA.
SETD ValueA
STQ
; And branch back to the beginning of the loop.
BRI start
end:
CALL lineFeed
SWI osExit
#Data
#Base 0x1000
ValueA:
; Low byte, high byte.
0x00 0x01
ValueB:
; Low byte, high byte.
0x00 0x00
#Include print.asm
+140
View File
@@ -0,0 +1,140 @@
; A Fibonacci number generating program that uses four bytes to store the value.
#Include services.asm
#Program
#Base 0x2000
start:
; Swap ValueB and ValueA.
; First, store ValueA on the stack.
SETD ValueA
LDA
PSHA
INCD
LDA
PSHA
INCD
LDA
PSHA
INCD
LDA
PSHA
; Next, store ValueB on the stack.
SETD ValueB
LDA
PSHA
INCD
LDA
PSHA
INCD
LDA
PSHA
INCD
LDA
PSHA
; Then pop ValueB into ValueA.
SETD ValueA
INCD INCD INCD
POPA
STA
DECD
POPA
STA
DECD
POPA
STA
DECD
POPA
STA
; Then pop ValueA into ValueB.
SETD ValueB
INCD INCD INCD
POPA
STA
DECD
POPA
STA
DECD
POPA
STA
DECD
POPA
STA
; Print ValueA.
SETD ValueA
INCD INCD INCD
LDA
CALL printByteHex
DECD
LDA
CALL printByteHex
DECD
LDA
CALL printByteHex
DECD
LDA
CALL printByteHex
CALL blankSpace
; Now add ValueA and ValueB, and store the result in ValueA.
; Add the lowest bytes of ValueA and ValueB.
SETD ValueB
LDB
SETD ValueA
LDA
ADD
; Store it in ValueA's lowest byte.
STQ
; Add the second lowest bytes of ValueA and ValueB.
SETD ValueB
INCD
LDB
SETD ValueA
INCD
LDA
ADD
; Store it in ValueA's second lowest byte.
STQ
; Add the second highest bytes of ValueA and ValueB.
SETD ValueB
INCD INCD
LDB
SETD ValueA
INCD INCD
LDA
ADD
; Store it in ValueA's third lowest byte.
STQ
; Add the highest bytes of ValueA and ValueB.
SETD ValueB
INCD INCD INCD
LDB
SETD ValueA
INCD INCD INCD
LDA
ADD
; If this addition overflows, we're done.
BRC end
; Otherwise, store the result in ValueA's highest byte.
STQ
; And branch back to the beginning of the loop.
BRI start
end:
CALL lineFeed
;HALT
SWI osExit
#Data
#Base 0x1000
ValueA:
; Lowest byte ... Highest byte.
0x01 0x00 0x00 0x00
ValueB:
; Lowest byte ... Highest byte.
0x00 0x00 0x00 0x00
#Include print.asm
+43
View File
@@ -0,0 +1,43 @@
; A Fibonacci number generating program that uses only one byte to store the value.
#Include services.asm
#Program
#Base 0x2000
start:
; Load our initial values into A and B.
INIA 0x00
CALL printByteDecimal
CALL blankSpace
; Move the value into B.
PSHA
POPB
; Load the next starting value into A.
INIA 0x01
CALL printByteDecimal
CALL blankSpace
loop:
ADD ; Add the values together.
BRC end ; If the value overflows, we're done.
; Copy A into B
PSHA
POPB
; Copy Q into A
PSHQ
POPA
; Print A.
CALL printByteDecimal
CALL blankSpace
BRI loop ; Loop again.
end:
CALL lineFeed
;HALT
SWI osExit ; Return to CosmOS.
#Data
#Base 0x1000
#Include print.asm
+388
View File
@@ -0,0 +1,388 @@
; Conway's Game of Life, as an application CosmOS can load and run.
;
; Ported from gameOfLife/16x16LifeModern.asm. The simulation is unchanged: the same
; interleaved 18 by 18 board with a dead border, the same four-Data-Pointer rewrite of
; the neighbor count, and the same glider.
;
; What had to change is when it stops. On the bare machine this program never stopped,
; because Life has no end state to reach and nothing was waiting for the machine back.
; Under CosmOS a program owns the console until it returns, so a program that never ends
; takes the shell with it. There is no HALT to fall into and no key that can interrupt
; it: console input is a blocking read, so a running program cannot ask whether anybody
; has pressed anything.
;
; So it stops on its own, two ways:
;
; IT SETTLES. commitBoard already walks the current and next state of every cell side
; by side, so it can notice for free whether any of them differed. When none did, the
; board has reached a state it will stay in forever and there is nothing left to show.
; The glider does reach one: it crosses the field, runs into the dead border, and
; collapses into a block in the corner at generation 54.
;
; IT RUNS OUT. Settling catches still lifes and extinction. It does not catch an
; oscillator - a blinker would flip back and forth forever and never be "unchanged" -
; so there is a generation limit behind it. It is not meant to be the answer. It is
; there so that no seed anybody tries later can take the shell down with it.
;
; Note that #Include print.asm comes at the END of this file rather than the beginning.
; print.asm opens with a branch to start, which is what a boot image wants at address
; zero; a loadable program wants its own first instruction at its code base instead.
#Include services.asm
#Program
#Base 0x2000
start:
CALL seedGlider
SETD.0 ClearScreen
CALL printString
SETD.3 GenerationsLeft
INIA 0xFF
STA.3
; Key mode, so that one keypress is one byte and stops it. In line mode the terminal
; holds what is typed until Return, so nothing arrives until then and "press any key"
; would really mean "press any key and then Return". It is put back before this program
; returns; CosmOS puts it back too, in case a program stops without doing so.
INIA 0x01
OUTA 0x02
generationLoop:
CALL renderBoard
CALL evolveBoard
CALL commitBoard
; Has anybody asked it to stop? The status port answers without waiting, which is the
; whole reason it exists: reading the data port here would stop the simulation dead
; until somebody typed something, which is the opposite of what is wanted.
;
; READY is clear at the end of input as well as when nothing has been typed, so running
; with input from a file - which is how the tests run it - never stops here. It runs to
; the still life instead, and that is the right answer in both places.
INA 0x01
INIB 0x01 ; READY
AND
BRQ lifeNoKey
INA 0x00 ; Take the key, so it is not left waiting for the shell.
BRI lifeStopped
lifeNoKey:
; commitBoard leaves the flag set if any cell differed from what replaced it. DP3 is
; pointed at it again rather than trusting what the call left behind: RET does not put
; DP3 back, so its value after a call is the callee's business and not a promise.
SETD.3 BoardChanged
LDA.3
BRA lifeSettled
SETD.3 GenerationsLeft
LDA.3
DECA
STA.3
BRA lifeRanOut
CALL frameDelay
BRI generationLoop
; The three ways it can be over. Each one only picks the words; the tidying up is the same
; for all of them and is written once, which is also how the console cannot be left in key
; mode down one path and not another.
lifeStopped:
SETD.0 StoppedText
BRI lifeEnd
lifeSettled:
SETD.0 SettledText
BRI lifeEnd
lifeRanOut:
SETD.0 RanOutText
lifeEnd:
RSTA
OUTA 0x02 ; Line mode, the way it was found.
CALL lineFeed
CALL printString ; DP0 still holds the words: CALL puts DP0 back.
CALL lineFeed
SWI osExit
seedGlider:
SETD.0 Board
DPUP.0 0d42
INIA 0x01
STA.0
SETD.0 Board
DPUP.0 0d80
STA.0
SETD.0 Board
DPUP.0 0d112
STA.0
DPUP.0 0d02
STA.0
DPUP.0 0d02
STA.0
RET
renderBoard:
SETD.0 CursorHome
CALL printString
SETD.1 RowCount
SETD.2 ColCount
INIA 0d16
STA.1
SETD.0 Board
DPUP.0 0d38
renderRow:
INIA 0d16
STA.2
renderCell:
LDA.0
BRA renderDead
INIB 0x23
OUTB 0x00
BRI renderCellDone
renderDead:
INIB 0x20
OUTB 0x00
renderCellDone:
DPUP.0 0d02
LDA.2
DECA
STA.2
BRA renderRowDone
BRI renderCell
renderRowDone:
CALL lineFeed
DPUP.0 0d04
LDA.1
DECA
STA.1
BRA renderDone
BRI renderRow
renderDone:
RET
evolveBoard:
SETD.1 RowCount
SETD.2 ColCount
INIA 0d16
STA.1
SETD.0 Board
DPUP.0 0d38
evolveRow:
INIA 0d16
STA.2
evolveCellLoop:
CALL evolveCell
DPUP.0 0d02
LDA.2
DECA
STA.2
BRA evolveRowDone
BRI evolveCellLoop
evolveRowDone:
DPUP.0 0d04
LDA.1
DECA
STA.1
BRA evolveDone
BRI evolveRow
evolveDone:
RET
evolveCell:
CALL countNeighbors
MVQB ; B is the neighbor count from here down.
; Three neighbors always produces a live cell.
INIA 0d03
CCF
SUB
BRQ makeAlive
; Two neighbors preserve the current state.
INIA 0d02
CCF
SUB
BRQ preserveCell
makeDead:
RSTA
INCD.0
STA.0
DECD.0
RET
preserveCell:
LDA.0
BRA makeDead
makeAlive:
INIA 0x01
INCD.0
STA.0
DECD.0
RET
; Return the eight-neighbor sum in Q. One Stack round-trip copies DP0 into
; volatile DP3; MVQA then keeps the running total entirely in registers.
countNeighbors:
PSHD.0
POPD.3
RSTA
DPDN.3 0d38
LDB.3
CCF
ADD
MVQA
DPUP.3 0d02
LDB.3
CCF
ADD
MVQA
DPUP.3 0d02
LDB.3
CCF
ADD
MVQA
DPUP.3 0d32
LDB.3
CCF
ADD
MVQA
DPUP.3 0d04
LDB.3
CCF
ADD
MVQA
DPUP.3 0d32
LDB.3
CCF
ADD
MVQA
DPUP.3 0d02
LDB.3
CCF
ADD
MVQA
DPUP.3 0d02
LDB.3
CCF
ADD
RET
; Copies each cell's next state over its current one, and says whether any of them
; differed. The comparison is what the bare metal version did not need: it is one XOR
; on two bytes that are already in registers, in a loop that was already visiting every
; cell, which is why "has it settled" costs almost nothing to ask.
;
; DP3 holds the flag for the whole walk. commitBoard calls nothing, so nothing else can
; want DP3 while it works.
commitBoard:
SETD.3 BoardChanged
RSTA
STA.3
SETD.1 RowCount
SETD.2 ColCount
INIA 0d18
STA.1
SETD.0 Board
commitRow:
INIA 0d18
STA.2
commitCell:
LDB.0 ; The cell as it stands.
INCD.0
LDA.0 ; The cell as it is about to stand.
DECD.0
STA.0
XOR ; Q is zero only if those two were the same.
BRQ commitSame
INIA 0x01
STA.3 ; Something moved, so this is not the last generation.
commitSame:
DPUP.0 0d02
LDA.2
DECA
STA.2
BRA commitRowDone
BRI commitCell
commitRowDone:
LDA.1
DECA
STA.1
BRA commitDone
BRI commitRow
commitDone:
RET
frameDelay:
INIA 0xFF
delayOuter:
INIB 0xFF
delayInner:
DECB
BRB delayInnerDone
BRI delayInner
delayInnerDone:
DECA
BRA delayDone
BRI delayOuter
delayDone:
RET
#Data
#Base 0x1000
RowCount:
0x00
ColCount:
0x00
; Cleared at the top of every commitBoard and set by any cell that changed, so after a
; commit it describes that generation and no other.
BoardChanged:
0x00
; Counts down. One byte is enough for a limit that is not meant to be reached.
GenerationsLeft:
0x00
SettledText:
"the board has settled"
RanOutText:
"stopped: still changing after 255 generations"
StoppedText:
"stopped"
ClearScreen:
0x1B
"[2J"
CursorHome:
0x1B
"[H"
; 18 by 18 cells with the current and next states interleaved, so 648 bytes. The
; original leaves this implicit and leans on Data Memory being zero, which works but
; means the assembler believes the board is one byte long: anything placed after it
; would land inside it, and nothing would say so. Reserving the region states how far
; it reaches, so a label added below here is safe.
Board:
#Reserve 0d648
#Include print.asm
+230
View File
@@ -0,0 +1,230 @@
; The 16-bit segmented sieve rewritten for SplitBit's four-Data-Pointer ISA.
;
; This deliberately implements the same algorithm and emits the same text as
; 16bitSegmentedSieve.asm, making the two versions useful as a direct comparison.
; DP0 walks PrimeStates, DP1 holds Page, DP2 walks Segment, and volatile DP3
; marks multiples. CALL preserves the first three pointers automatically.
#Include services.asm
#Program
#Base 0x2000
start:
RSTA
SETD.1 Page
STA.1
nextPage:
SETD.2 Segment
RSTA
RSTB
clearSegment:
STB.2
INCD.2
INCA
BRA segmentCleared
BRI clearSegment
segmentCleared:
; Zero and one are not prime.
LDA.1
BRA excludeZeroAndOne
BRI markSegment
excludeZeroAndOne:
SETD.2 Segment
INIA 0x01
STA.2
INCD.2
STA.2
markSegment:
SETD.0 PrimeStates
INIA 0d54
primeLoop:
CALL processPrime
DPUP.0 0d03
DECA
BRA scanSegment
BRI primeLoop
scanSegment:
SETD.2 Segment
RSTA
scanLoop:
LDB.2
BRB emitPrime
scanNext:
INCD.2
INCA
BRA advancePage
BRI scanLoop
emitPrime:
CALL printCandidateHex
BRI scanNext
advancePage:
LDA.1
INCA
STA.1
BRA finished
BRI nextPage
finished:
CALL lineFeed
SWI osExit ; Return to CosmOS.
; DP0 points at a PrimeStates entry. CALL restores it on return.
processPrime:
INCD.0
LDA.0
LDB.1
XOR
BRQ primeIsActive
RET
primeIsActive:
; B is the prime and A its current offset.
DECD.0
LDB.0
DPUP.0 0d02
LDA.0
; DP3 = Segment + offset. Only this one initial pointer copy needs the Stack.
SETD.3 Segment
PSHB
PSHD.3
POPB
CCF
ADD
PSHQ
POPD.3
POPB
markPrimeLoop:
INIA 0x01
STA.3
; Add the prime to DP3's low byte. A carry crosses into the next window.
PSHD.3
POPA
CCF
ADD
PSHQ
POPD.3
BRC primeFinished
BRI markPrimeLoop
primeFinished:
; DP0 is on the offset byte; advance the saved high byte and save Q as
; the wrapped offset for the following page.
DECD.0
LDA.0
INCA
STA.0
INCD.0
STQ.0
RET
printCandidateHex:
PSHA
LDA.1
CALL printByteHex
POPA
CALL printByteHex
CALL blankSpace
RET
#Data
#Base 0x1000
; Segment has to begin on a page boundary, and now says so itself rather than relying on
; whatever happens to have been assembled before it. The marking loop adds the prime to
; the low byte of DP3 and treats the carry out as the end of the page, so it only finds
; the right boundary if the window starts on one.
;
; The whole window is written out here so that Page and PrimeStates begin after it.
#Align 0x100
Segment:
0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Page:
0x00
PrimeStates:
0x02 0x00 0x04
0x03 0x00 0x09
0x05 0x00 0x19
0x07 0x00 0x31
0x0B 0x00 0x79
0x0D 0x00 0xA9
0x11 0x01 0x21
0x13 0x01 0x69
0x17 0x02 0x11
0x1D 0x03 0x49
0x1F 0x03 0xC1
0x25 0x05 0x59
0x29 0x06 0x91
0x2B 0x07 0x39
0x2F 0x08 0xA1
0x35 0x0A 0xF9
0x3B 0x0D 0x99
0x3D 0x0E 0x89
0x43 0x11 0x89
0x47 0x13 0xB1
0x49 0x14 0xD1
0x4F 0x18 0x61
0x53 0x1A 0xE9
0x59 0x1E 0xF1
0x61 0x24 0xC1
0x65 0x27 0xD9
0x67 0x29 0x71
0x6B 0x2C 0xB9
0x6D 0x2E 0x69
0x71 0x31 0xE1
0x7F 0x3F 0x01
0x83 0x43 0x09
0x89 0x49 0x51
0x8B 0x4B 0x79
0x95 0x56 0xB9
0x97 0x59 0x11
0x9D 0x60 0x49
0xA3 0x67 0xC9
0xA7 0x6C 0xF1
0xAD 0x74 0xE9
0xB3 0x7D 0x29
0xB5 0x7F 0xF9
0xBF 0x8E 0x81
0xC1 0x91 0x81
0xC5 0x97 0x99
0xC7 0x9A 0xB1
0xD3 0xAD 0xE9
0xDF 0xC2 0x41
0xE3 0xC9 0x49
0xE5 0xCC 0xD9
0xE9 0xD4 0x11
0xEF 0xDF 0x21
0xF1 0xE2 0xE1
0xFB 0xF6 0x19
#Include print.asm
+74
View File
@@ -0,0 +1,74 @@
; This is an implementation of The Sieve of Eratosthenes that finds all the primes between 2 and 255.
#Include services.asm
#Program
#Base 0x2000
start:
; Search the list until we find a prime.
SETD DataTop
CCF ; Clear the carry flag. In later cycles, the carry flag will be set at the end of the next loop. We'll want it cleared.
RSTA
RSTB
findPrimeLoop:
LDB ; Load an element into B.
BRB foundPrime ; If it's zero, it's a prime.
INCA ; Increment A, our index.
INCD ; Increment the Data Pointer.
BRA end ; If A becomes zero, we've looked through the whole list without finding another prime.
BRI findPrimeLoop ; Keep searching for the next prime.
foundPrime:
; If we've found a prime, we should print it and mark it off the list so we don't print it again.
CALL printByteDecimal ; A contains our prime, so we can just call the print subroutine.
CALL blankSpace ; Put a space afterward to keep things easy to read.
INIB 0x01 ; Set B to 1.
STB ; Mark this prime off the list.
markMultiples:
; Now, we mark each multiple of this prime as nonprime until we reach the end of the list.
PSHD ; Save the Data Pointer to the stack.
POPB ; Pop its low byte into B.
ADD ; Add them together.
PSHQ ; Store the result back onto the stack.
POPD ; Pop the modified address into the Data Pointer.
INIB 0x01 ; Set B to 1.
STB ; Store B to mark the value as nonprime.
BRC start ; If the previous add overflowed, the next nonprime is outside the range of our list, so start over with a new prime.
BRI markMultiples ; Otherwise, loop again to mark the next multiple as nonprime.
end:
CALL lineFeed ; Print a linefeed to make it look nice.
SWI osExit ; The program is done, we found all the primes!
#Data
#Base 0x1000
; The table of our prime candidates. It has to begin on a page boundary: marking walks
; the pointer's low byte and treats the carry out as running off the end of the table,
; which only finds the right end if the table starts on one.
#Align 0x100
DataTop:
0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
#Include print.asm
+61
View File
@@ -0,0 +1,61 @@
; A program for CosmOS to load and run.
;
; It carries no library of its own and no vector table. Everything it can do it asks the
; system for, by name, through services.asm, which the system includes too. The order of
; the names in that one file is what gives them their numbers, so neither side has a
; number written down anywhere and the two cannot disagree about them.
;
; Compare it with Programs/loadable/hello.asm, which is the same idea one step earlier:
; that one talks to the console port itself and stops with HALT, because when it was
; written there was no system to ask and nowhere to give the machine back to.
;
; It is assembled for where it will live. #Base says so, and that makes the assembler
; write it out as a loadable program rather than as a boot image. Nothing relocates
; anything, so those addresses have to be the ones CosmOS puts it at.
#Include services.asm
#Program
#Base 0x2000 ; Above the system, which keeps below here.
greet:
SETD.0 Opening
SWI osPrintString
SETD.0 Question
SWI osPrintString
SETD.0 Answer
INIB 0d31
SWI osReadLine
SETD.0 Hello
SWI osPrintString
SETD.0 Answer
SWI osPrintString
SETD.0 Ending
SWI osPrintString
; Give the machine back. The system takes its Stack back at this point, so everything
; this program pushed goes with it.
SWI osExit
#Data
#Base 0x1000 ; And its data above the system's.
Opening:
"a program, loaded off a disk, running on the system that loaded it
"
Question:
"what should I call you? "
Hello:
"hello, "
Ending:
". that is all I do.
"
; Thirty one characters and the zero byte that ends them.
Answer:
#Reserve 0d32
+30
View File
@@ -0,0 +1,30 @@
; This is a basic hello world program for the SplitBit CPU.
; We'll create a loop that outputs each byte of our string to Output 0, the text console.
; Include the system services so we can return.
#Include services.asm
#Program
#Base 0x2000 ; Change two:
SETD hello ; Change three
Start:
LDA ; Load a byte of the string into A.
BRA End ; If A is zero, branch out of the loop.
OUTA 0x00 ; Output the value in A to Port 0, the text console.
INCD ; Increment the Data Pointer to the next byte of the string.
BRI Start ; Branch immediately to the start of the loop.
End:
INIA 0x0A ; We'll load a linefeed into A and output it to make it look nice.
OUTA 0x00 ; Output it to the text console.
;HALT ; Terminate the program.
; Instead, let's call osExit to return the system nicely. Fourth change.
SWI osExit
#Data
#Base 0x1000 ; Five, adjust the base of the data segment.
hello: ; Throw a label here so we can explicitly point at this data. Six, actually.
"Hello, World!"