CosmOS pre-alpha and launchable application versions of old programs.
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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!"
|
||||
Reference in New Issue
Block a user