; 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 0x4000 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 0x2000 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 "