Files
SplitBit-Emulator/Programs/CosmOS/Apps/Pour.asm
T
AnachronautandClaude Opus 5 3449405b18 Give the system another page of each memory
CosmOS had 1,161 bytes of Program Memory left before the address applications
load at, and Tab completion is not going to fit in that with anything to spare.
So the wall moves up one page: the system keeps below 0x4FFF and 0x2FFF, and an
application is based at 0x5000 and 0x3000.

A PAGE IS A CHEAP THING TO GIVE IT AND AN EXPENSIVE THING TO RUN OUT OF. An
application still has 44K of Program Memory before the vector table and the
largest one here uses 7.5K, so what was taken from applications is space nothing
has ever asked for - while what the system gained is the difference between
building the next thing and counting bytes while building it.

Not doubling, which was the version that would have cost application space worth
minding. One page, and the same again when it is needed.

Nothing in the machine knows where the wall is, so this is 34 #Base lines, one
threshold in the fault handler, and the table in the CosmOS README that
Tests/docs.sh reads its limits out of.

The native assembler's scratch map had to move with it, and docs.sh said so
before anything ran: its data reached 0x40D6 and its buffers began at 0x4000, so
they were sitting on its variables. That file already carries a paragraph about
the floor coming up and the map staying where it was. It has happened twice now,
and been caught by a check the first time wrote.

Twenty three recordings are the same runs a page higher.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-09-01 16:49:12 -04:00

246 lines
3.9 KiB
NASM

; Writes a file one block at a time, without ever holding the whole of it.
;
; This is the write side of what Stream demonstrates for reading: a file bigger than the
; memory building it. It writes as many blocks as it is asked for, each one filled with a
; pattern that says which block it is, so that what comes off the disk afterwards can be
; checked against what should have gone on rather than merely being the right length.
;
; The argument is how many blocks, in decimal. The last one is deliberately a part block,
; because a tail is the case every off-by-one in a filesystem hides in.
;
; Written by Anachronaut
#Include services.asm
#Program
#Base 0x5000
start:
SETD.0 Argument
INIB 0d8
SWI osArgument
SETD.0 Argument
LDA.0
BRA useDefault
CALL readCount
BRI counted
useDefault:
INIA 0d4
SETD.0 Blocks
STA.0
counted:
; Nothing may be zero blocks: the tail below would then be the whole file.
SETD.0 Blocks
LDA.0
BNA haveCount
INIA 0d1
STA.0
haveCount:
; Whole blocks, and a tail of forty bytes on the end of them.
SETD.0 Name
SETD.3 0x00 0x00
SETD.0 Blocks
LDA.0
PSHA
POPB
RSTA
PSHA
PSHB
POPD.3
SETD.0 Name
INIA 0d40
SWI osFileStart
BNQ startFailed
; Each whole block, filled with its own number.
RSTA
SETD.0 Which
STA.0
nextBlock:
SETD.0 Which
LDA.0
SETD.2 Blocks
LDB.2
CCF
SUB
BRQ theTail
CALL fillBlock
SETD.1 Block
SETD.0 Which
LDA.0
RSTB
PSHA
POPB
RSTA
SWI osFileWrite
BNQ writeFailed
SETD.0 Which
LDA.0
INCA
STA.0
BRI nextBlock
theTail:
; And the part block at the end, which is the same fill cut short by the size given at
; the start. Only the first forty bytes of it will belong to the file.
CALL fillBlock
SETD.1 Block
SETD.0 Which
LDA.0
RSTB
PSHA
POPB
RSTA
SWI osFileWrite
BNQ writeFailed
; And how big it turned out to be, which here is what was asked for: this one knows its
; size from the start. Something that did not - an assembler, say - would ask for more
; than it needed and say the truth here.
RSTA
PSHA
SETD.0 Blocks
LDA.0
PSHA
POPD.3
INIA 0d40
SWI osFileDone
BNQ doneFailed
SETD.0 Wrote
SWI osPrintString
SETD.0 Blocks
LDB.0
RSTA ; A and B together are the number, so the count is the low half.
SWI osPrintNumber
SETD.0 AndTail
SWI osPrintString
RSTA
SWI osExit
; The block becomes 256 copies of the block number plus a fixed byte, so that a block
; written into the wrong place is visible rather than merely being bytes.
fillBlock:
SETD.0 Block
SETD.1 Which
LDA.1
INIB 0x41
CCF
ADD
MVQA
RSTB
fillLoop:
STA.0
INCD.0
DECB
BNB fillLoop
RET
; The argument, in decimal, into Blocks. Anything that is not a digit ends it.
readCount:
RSTA
SETD.1 Blocks
STA.1
SETD.0 Argument
countLoop:
LDA.0
BRA countDone
INIB 0x30
CCF
SUB
MVQA
INIB 0d10
CCF
SUB
BNC countDone ; Not a digit, so the number ended.
SETD.1 Blocks
LDB.1
PSHA
INIA 0d10
CALL timesTen
POPA
SETD.1 Scratch
LDB.1
CCF
ADD
MVQA
SETD.1 Blocks
STA.1
INCD.0
BRI countLoop
countDone:
RET
; B times ten into Scratch, by adding it up. Nothing here is bigger than a byte.
timesTen:
RSTA
SETD.0 Scratch
STA.0
INIA 0d10
tenLoop:
PSHA
SETD.0 Scratch
LDA.0
SETD.2 TenHold
STB.2
LDB.2
CCF
ADD
MVQA
STA.0
POPA
DECA
BNA tenLoop
RET
startFailed:
SETD.0 NoStart
SWI osPrintString
INIA 0d1
SWI osExit
writeFailed:
SETD.0 NoWrite
SWI osPrintString
INIA 0d1
SWI osExit
doneFailed:
SETD.0 NoDone
SWI osPrintString
INIA 0d1
SWI osExit
#Data
#Base 0x3000
Name:
"poured.dat"
Argument:
#Reserve 0d9
Blocks:
0x00
Which:
0x00
Scratch:
0x00
TenHold:
0x00
Block:
#Reserve 0d256
Wrote:
"poured "
AndTail:
" blocks and a tail of 40
"
NoStart:
"could not start it
"
NoWrite:
"could not write a block
"
NoDone:
"could not finish it
"