From fb7b224bbb518e022dda21a4d194a4e840865ddd Mon Sep 17 00:00:00 2001 From: Anachronaut Date: Tue, 25 Aug 2026 16:58:17 -0400 Subject: [PATCH] S2: the assembler writes the file as it makes it The output image is gone. It was eighteen kilobytes and it is now one block of window, because the file was always produced in order and only ever needed to be written that way. Everything works in FILE OFFSETS now. A cursor is a two byte number counting from the front of the file, and since a block is two hundred and fifty six bytes, the block it lands in is the offset's high byte and the place within that block is its low one - so there is no division anywhere, and ImgWalk, ProgPut and DataPut needed no change but where they start. ONE WINDOW RATHER THAN THREE. The plan said three: one per segment, and a third for the block where the program ends and the data begins, which belongs to both. Fetching a block back instead makes all of that one case. The header is patched after every byte is out, the boundary block is written by both cursors, and both are simply revisits - a revisit is what fetching handles. osFileFetch is the service that allows it, and is the read side of the write. A run of bytes in one segment costs nothing extra; a switch between segments costs two block operations, and a source file has a few dozen switches and several thousand bytes. Two bugs, both a pointer meaning two things: putAt took the cursor to advance in DP2 and then wanted DP2 for the window's address. A call puts DP2 back the way it was AT THE CALL, so the step at the end moved whatever the last call had left there - the window walked off across memory while the cursor stood still. It goes in memory now, like the block did in S1, and for the same reason. The size the file is created at could not be right. How many vectors are actually installed is not known until the second pass has resolved their handlers, and by then the file must already exist to be written into - so Keys, which brings one vector, came out four bytes short. Teaching the first pass to count them meant teaching it about devices, and about a Boot line in a loadable program not being installed at all, which is two ways to disagree with the second pass about what a file contains. So osFileDone is told the size instead. A writer asks for as much as the file could possibly come to - the whole of it plus four bytes for every vector DECLARED, which no file can exceed - and says what it really came to at the end. The blocks it did not use go back to the free count. Asking for too much costs a moment; asking for too little writes off the end of a file. That is a better service for it, not a workaround. A writer that cannot know its size until the last byte is the ordinary case, and it is exactly the case this whole rung exists for. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW --- Programs/CosmOS/Apps/Pour.asm | 10 + Programs/CosmOS/Assembler/Asm.asm | 351 +++++++++++++++++++++++--- Programs/CosmOS/Assembler/scratch.asm | 19 +- Programs/CosmOS/README.md | 16 +- Programs/CosmOS/Source/cosmos.asm | 32 ++- Programs/CosmOS/Source/sbfs.asm | 111 +++++++- Programs/CosmOS/Source/services.asm | 8 +- Tests/expected/cosmosBreak.out | 4 +- 8 files changed, 505 insertions(+), 46 deletions(-) diff --git a/Programs/CosmOS/Apps/Pour.asm b/Programs/CosmOS/Apps/Pour.asm index c5734b1..197a0d2 100644 --- a/Programs/CosmOS/Apps/Pour.asm +++ b/Programs/CosmOS/Apps/Pour.asm @@ -98,6 +98,16 @@ theTail: 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 diff --git a/Programs/CosmOS/Assembler/Asm.asm b/Programs/CosmOS/Assembler/Asm.asm index 9111902..dade95b 100644 --- a/Programs/CosmOS/Assembler/Asm.asm +++ b/Programs/CosmOS/Assembler/Asm.asm @@ -57,6 +57,7 @@ start: CALL settleFormat CALL deriveName ; After the first pass: what it is called depends on whether a ; #Base turned up, and that is not known until then. + CALL layOutImage BNQ stopped CALL passTwo @@ -1360,8 +1361,7 @@ layOutImage: BNQ layOutNo SETD.0 ImgWalk - SETD.2 ScratchImage - CALL numSet + CALL numZero ; The front of the file, not the front of a buffer. SETD.0 MagicSPBT INIA 0d4 CALL putBytes @@ -1414,8 +1414,7 @@ layOutLoadable: BNQ layOutNo SETD.0 ImgWalk - SETD.2 ScratchImage - CALL numSet + CALL numZero ; The front of the file, not the front of a buffer. SETD.0 MagicSBEX INIA 0d4 CALL putBytes @@ -1460,17 +1459,104 @@ layOutNo: ADD RET +; ---- Room for the file ---- +; +; Nothing is held in memory any more, so what is checked here is not a buffer. It is that +; the SIZE CAN STILL BE DESCRIBED: every number in this assembler is two bytes, so a file +; of more than 65,535 would have wrapped round while it was being added up and come out +; smaller than one of its own segments. A wrapped total would allocate a short file and +; write off the end of it. +; +; Then the file is started, which is where running out of DISK is found - and that happens +; before a single byte is written, so a refusal costs nothing that was already there. checkImageRoom: - SETD.0 ImgRoom - SETD.2 ImgTotal + SETD.0 ImgTotal + SETD.2 ImgProgLen + CALL numCompare + BRC imageTooBig ; The total is smaller than a part of it: it went round. + SETD.0 ImgTotal + SETD.2 ImgDataLen CALL numCompare BRC imageTooBig + + ; ---- How much room to ask for ---- + ; + ; MORE THAN THE FILE WILL COME TO, on purpose. How many vectors are actually installed is + ; not known until the second pass has resolved every handler, and by then the file has to + ; exist to be written into. What IS known now is how many vectors were DECLARED, and no + ; more than that can be installed - so the room asked for is the whole file plus four + ; bytes for each of them and five for a marker. + ; + ; Asking for too much costs nothing but a moment: osFileDone is told what it really came + ; to and the difference goes back. Asking for too little would have meant writing off the + ; end of the file, and the first sign of it was Keys coming out four bytes short. + SETD.0 ImgAsk + SETD.2 ImgTotal + CALL numSet + SETD.0 ImgVecMost + SETD.2 VecCount + CALL numSet + SETD.0 ImgVecMost + SETD.2 ImgVecMost + CALL numAdd + SETD.0 ImgVecMost + SETD.2 ImgVecMost + CALL numAdd ; Four bytes an entry. + SETD.0 ImgAsk + SETD.2 ImgVecMost + CALL numAdd + INIA 0d5 + SETD.0 ImgAsk + CALL numAddByte + + ; Blocks are the high half of the size and the tail is the low half, which is how an + ; entry holds one and why nothing here has to divide. + SETD.1 ImgAsk + LDA.1 + SETD.0 ImgOutBlocks + STA.0 ; The high half is whole blocks. + SETD.1 ImgAsk + INCD.1 + LDA.1 + SETD.0 ImgOutTail + STA.0 ; And the low half is what is left over. + + ; DP3 wants the block count and A the tail. The Stack takes the high byte first, which is + ; the way every reader in the system takes a count back out of DP3. + RSTA + PSHA + SETD.0 ImgOutBlocks + LDA.0 + PSHA + POPD.3 + SETD.0 ImgOutTail + LDA.0 + SETD.0 OutName + SWI osFileStart + BNQ imageNoRoom + + ; Nothing in the window yet. + RSTA + SETD.0 OutHeld + STA.0 + SETD.0 OutDirty + STA.0 + RSTA RSTB CCF ADD RET +imageNoRoom: + SETD.0 NoRoomText + SWI osPrintString + RSTA + INIB 0d1 + CCF + ADD + RET + imageTooBig: SETD.0 TooBigText SWI osPrintString @@ -1481,16 +1567,171 @@ imageTooBig: RET ; Puts A down at ImgWalk and steps it. -putByte: +; ---- Putting a byte into the file ---- +; +; THE FILE IS WRITTEN AS IT IS MADE, through one block of window, rather than being built +; whole in memory and handed over at the end. Everything below works in FILE OFFSETS: a +; cursor is a two byte number counting from the front of the file, and because a block is +; two hundred and fifty six bytes, the block it lands in is the offset's HIGH byte and the +; place within that block is its LOW one. No division anywhere, which is just as well. +; +; There are two cursors and they move independently, because #Program and #Data alternate +; in source and the two segments are consecutive in the file. Whenever a byte lands in a +; block other than the one in hand, the one in hand goes back to the disk and the new one +; is fetched - so a run of bytes in one segment costs nothing extra, and a switch between +; segments costs two block operations. There are a few dozen switches in a source file and +; several thousand bytes. +; +; The block is FETCHED rather than assumed blank, and that is what makes the whole thing +; work with one window instead of three. The header is patched after every byte is out; the +; block where the program ends is the same block the data begins in; both are simply +; revisits, and a revisit is what fetching handles. + +; A at the file offset in the two byte number DP2 names, which is then stepped on. +putAt: SETD.0 ImgHold STA.0 - SETD.1 ImgWalk - LDD.0.1 - SETD.2 ImgHold - LDA.2 - STA.0 + + ; WHICH CURSOR THIS IS, KEPT IN MEMORY. It arrives in DP2 and everything below wants DP2 + ; for something of its own - and a call puts DP2 back the way it was AT THE CALL, not the + ; way it was on the way in. Leaving it there meant the step at the end moved whatever the + ; last call had left in DP2, which was the address of the window, and the window walked + ; off across memory while the cursor stood still. + SETD.0 ImgCursor + STD.2.0 + + ; Which block, and where in it: a block is two hundred and fifty six bytes, so the two + ; halves of the offset are exactly those two things. + PSHD.2 + POPD.0 + LDA.0 + SETD.1 ImgBlockWant + STA.1 INCD.0 - STD.0.1 + LDA.0 + SETD.1 ImgSlot + STA.1 + + CALL outHold + BNQ putAtFailed + + SETD.0 OutAt + SETD.2 ScratchWindow + CALL numSet + SETD.0 OutAt + SETD.1 ImgSlot + LDA.1 + CALL numAddByte + SETD.1 OutAt + LDD.0.1 + SETD.1 ImgHold + LDA.1 + STA.0 + + INIA 0x01 + SETD.0 OutDirty + STA.0 + + SETD.1 ImgCursor + LDD.0.1 + CALL numStep + RSTA + RSTB + CCF + ADD + RET + +putAtFailed: + RSTA + INIB 0d1 + CCF + ADD + RET + +; Makes sure the block ImgBlockWant names is the one in the window, writing back whatever +; was there and fetching the new one. +outHold: + SETD.0 OutHeld + LDA.0 + BRA outFetch ; Nothing in hand at all. + SETD.0 OutBlock + LDA.0 + SETD.2 ImgBlockWant + LDB.2 + CCF + SUB + BRQ outHeldAlready + + CALL outFlush + BNQ outHoldNo + +outFetch: + SETD.0 ImgBlockWant + LDA.0 + SETD.1 OutBlock + STA.1 + + SETD.1 ScratchWindow + LDD.1.1 + SETD.0 OutBlock + LDB.0 + RSTA + SWI osFileFetch + BNQ outHoldNo + + INIA 0x01 + SETD.0 OutHeld + STA.0 + RSTA + SETD.0 OutDirty + STA.0 + +outHeldAlready: + RSTA + RSTB + CCF + ADD + RET + +outHoldNo: + RSTA + INIB 0d1 + CCF + ADD + RET + +; Whatever is in the window goes back to the file, if anything has been put in it. +outFlush: + SETD.0 OutHeld + LDA.0 + BRA outFlushNothing + SETD.0 OutDirty + LDA.0 + BRA outFlushNothing + + SETD.1 ScratchWindow + LDD.1.1 + SETD.0 OutBlock + LDB.0 + RSTA + SWI osFileWrite + BNQ outHoldNo + + RSTA + SETD.0 OutDirty + STA.0 + +outFlushNothing: + RSTA + RSTB + CCF + ADD + RET + +; The header and everything else that is written in order goes through ImgWalk. +putByte: + SETD.2 ImgWalk + CALL putAt RET ; Puts A bytes from DP0 down at ImgWalk. @@ -1543,12 +1784,11 @@ emitByte: emitToProgram: SETD.1 ProgPut emitPut: - LDD.0.1 - SETD.2 EmitHold - LDA.2 - STA.0 - INCD.0 - STD.0.1 + PSHD.1 + POPD.2 ; ProgPut or DataPut, whichever this byte belongs to. + SETD.0 EmitHold + LDA.0 + CALL putAt RET ; The byte at DP0 offset by A, into ClsByte. The classifier has one of these; this is the @@ -1676,8 +1916,8 @@ writeVectorsHeader: LDA.0 BRA writeVectorsNone SETD.0 ImgWalk - SETD.2 ScratchImage - CALL numSet + CALL numZero ; Back to the front of the file, which is a revisit like + ; any other: the window fetches block nought again. SETD.0 ImgWalk INIA 0d4 CALL numAddByte @@ -1695,8 +1935,8 @@ writeVectorsNone: LDA.0 BRA writeVectorsOut SETD.0 ImgWalk - SETD.2 ScratchImage - CALL numSet + CALL numZero ; Back to the front of the file, which is a revisit like + ; any other: the window fetches block nought again. SETD.0 ImgWalk INIA 0d8 CALL numAddByte @@ -1772,15 +2012,33 @@ vectorNotEntry: ADD RET +; Whatever is still in the window, and then the file takes its name. Nothing that was +; already on the disk has been touched until this last step. writeImage: - SETD.0 OutName - SETD.1 ScratchImage - LDD.1.1 - SETD.2 ImgTotal - LDA.2 - INCD.2 - LDB.2 - SWI osFileSave + CALL outFlush + BNQ writeFailed + + ; What it really came to, which is what the room was asked for less whatever the vectors + ; did not need. + SETD.1 ImgTotal + LDA.1 + SETD.0 ImgOutBlocks + STA.0 + SETD.1 ImgTotal + INCD.1 + LDA.1 + SETD.0 ImgOutTail + STA.0 + + RSTA + PSHA + SETD.0 ImgOutBlocks + LDA.0 + PSHA + POPD.3 + SETD.0 ImgOutTail + LDA.0 + SWI osFileDone BNQ writeFailed RSTA RSTB @@ -2054,8 +2312,35 @@ DropWalk: ; actually is. Three numbers in three files describe these buffers - this one, LabLimit and ; LabRoom in labels.asm, and the map itself - and each of the three has now been the one ; that was left behind while the other two moved. -ImgRoom: - 0x48 0x00 +; Which block of the file is in the window, and whether anything has been put in it. The +; window itself is in scratch, like every other buffer here: a #Reserve is written into the +; file as zeroes and copied at load, and a program that carries its own scratch pays for it +; twice. +OutAt: + 0x00 0x00 +OutBlock: + 0x00 +OutHeld: + 0x00 +OutDirty: + 0x00 +ImgBlockWant: + 0x00 +ImgSlot: + 0x00 +ImgOutBlocks: + 0x00 +ImgOutTail: + 0x00 +ImgCursor: + 0x00 0x00 +ImgAsk: + 0x00 0x00 +ImgVecMost: + 0x00 0x00 +NoRoomText: +"no room on the disk for the output +" MagicSPBT: "SPBT" diff --git a/Programs/CosmOS/Assembler/scratch.asm b/Programs/CosmOS/Assembler/scratch.asm index 6840db1..6e8bbd7 100644 --- a/Programs/CosmOS/Assembler/scratch.asm +++ b/Programs/CosmOS/Assembler/scratch.asm @@ -17,7 +17,8 @@ ; ; 0x4000 6144 the label index, 1536 entries of four ; 0x5800 16384 the label names, packed end to end -; 0x9800 18432 the output file being built +; 0x9800 256 one block of the output file, on its way to the disk +; 0x9900 18176 free ; 0xE000 1792 the vector names, 64 entries of twenty eight ; 0xE700 2048 the reader's stack, six levels of 301 ; 0xEF00 368 which files have been included, sixteen names of 23 @@ -43,10 +44,16 @@ ; index doubled, and the output given what is left, which is still four and a half thousand ; bytes more than CosmOS needs today. ; -; The output's share is temporary. It exists only because the assembler holds a whole -; finished file in memory before writing it, and that is the next thing to go - the file is -; produced in order and could be written as it is made, which would leave this needing three -; blocks rather than eighteen kilobytes. +; THE OUTPUT IS NO LONGER HELD AT ALL. It used to be built whole in memory and handed over +; at the end, which is what made a buffer of eighteen kilobytes the largest thing this +; machine could assemble. The file is produced in order, so it is written as it is made, +; through one block of window - and the eighteen kilobytes that were its share are free. +; +; What to do with them is not obvious and does not have to be decided today. Nothing here +; is close to full: the names are at half, the index at a third, and the output has no +; ceiling of its own any more. Leaving the room unclaimed is better than sharing it out +; among buffers that do not need it, because an unclaimed page is available to whichever +; one turns out to want it. ; ; That ends at 0xF070, with the Stack coming down from 0xFFFF above it - nearly four ; kilobytes, against the tens of bytes of CALL frames this ever nests. @@ -65,7 +72,7 @@ ScratchLabIndex: 0x40 0x00 ScratchLabArena: 0x58 0x00 -ScratchImage: +ScratchWindow: 0x98 0x00 ScratchVecNames: 0xE0 0x00 diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 7498230..8724f5c 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -512,7 +512,8 @@ Those numbers are written down once, in `Programs/CosmOS/Source/services.asm`, w | osChangeDir | DP0 names a directory. Q is zero if the machine is now in it. What a program changes here, the shell puts back when the program stops. | | osFileStart | DP0 names a file, DP3 is how many whole blocks and A is the bytes left over in the last one. Q is zero if a write is now open. Nothing already on the disk is touched. | | osFileWrite | DP1 is a block, A and B together are which block of the file it is, counting from zero. Q is zero if it was written. An index past the end of the file is refused. | -| osFileDone | No arguments. The old file goes and what was written takes its name. Q is zero if it was committed. | +| osFileDone | DP3 is how many whole blocks it came to and A the bytes left over. The old file goes and what was written takes its name, at that size. Q is zero if it was committed. | +| osFileFetch | DP1 is where a block should go, A and B together are which block. Reads back a block of the file being written. | | osPrintNumber | A and B together are a number. Prints it in decimal, without leading zeroes. | | osBreak | Stops the program, shows every register as it had them, waits for a key, and carries on. | @@ -664,6 +665,8 @@ file a block at a time; `osFileStart`, `osFileWrite` and `osFileDone` write one. ...for each block: DP1 the bytes, A and B which block... SWI osFileWrite + SETD.3 0x00 0x06 ; and what it came to, which need not be + INIA 0d40 ; what was asked for SWI osFileDone ``` @@ -678,6 +681,17 @@ is taken at the start, so a disk that cannot hold it says so while the old one i there. That is stronger than `osFileSave` can manage, where the size is only known once the caller already has every byte in hand. +**The size asked for need not be the size it comes to.** Some sizes are not knowable until +the last byte is out - the assembler cannot say how many vectors a program installs until +it has resolved them, and by then the file it is writing into has to exist. So the room is +taken generously at the start, where running out costs nothing, and `osFileDone` is told +the truth. The blocks that were asked for and not used go back. + +`osFileFetch` reads a block of the file back, which is what lets a program keep only one +block of it in hand. Anything producing two parts of a file at once - source that says +`#Program` and `#Data` in whatever order it likes - has to be able to put a block down, go +and write somewhere else, and pick it up again where it left off. + Two limits differ between the two. `osFileSave` is handed a byte count in two registers and so cannot write more than 65,535 bytes; `osFileStart` is told blocks and a tail, the way an entry holds a size, and reaches the whole disk. And `osFileWrite` refuses an index past the diff --git a/Programs/CosmOS/Source/cosmos.asm b/Programs/CosmOS/Source/cosmos.asm index d4b4c9d..d649f41 100644 --- a/Programs/CosmOS/Source/cosmos.asm +++ b/Programs/CosmOS/Source/cosmos.asm @@ -987,9 +987,36 @@ handleFileWrite: STA.2 RETI -; Nothing to be told. The old file goes and the temporary takes its name, which is the only -; step that can lose anything and the last one. +; DP1 is where the block goes, and A and B together say which one, the same way writing is +; told. Reads back a block of the file being written. +handleFileFetch: + SETD.2 SbfsIndex + STA.2 + INCD.2 + STB.2 + CALL sbfsStreamFetch + MVQA + MVSD.2 + DPUP.2 0d02 + STA.2 + RETI + +; DP3 is how many whole blocks it came to and A is what is left over, told the same way +; osFileStart is told. It need not be what was asked for: a writer that cannot know its +; size until the last byte asks for enough at the start, where running out costs nothing, +; and says the truth here. The blocks it did not use go back. +; +; This is the only step that can lose anything, and the last one. handleFileDone: + SETD.2 SbfsFileTail + STA.2 + PSHD.3 + POPB + POPA + SETD.2 SbfsFileBlocks + STA.2 + INCD.2 + STB.2 CALL fileForget CALL sbfsStreamDone MVQA @@ -3744,6 +3771,7 @@ CommandLine: osFileStart handleFileStart osFileWrite handleFileWrite osFileDone handleFileDone + osFileFetch handleFileFetch osPrintNumber handlePrintNumber osBreak handleBreak Device 0x20 diskDone diff --git a/Programs/CosmOS/Source/sbfs.asm b/Programs/CosmOS/Source/sbfs.asm index c4fe59f..5d57985 100644 --- a/Programs/CosmOS/Source/sbfs.asm +++ b/Programs/CosmOS/Source/sbfs.asm @@ -2420,11 +2420,60 @@ sbfsStreamWrite: CALL sbfsWriteOne RET +; DP1 is where the block should go and SbfsIndex says which one. The other direction of +; sbfsStreamWrite, and the thing that lets a writer keep only ONE block of a file in hand: +; anything writing two parts of a file at once has to be able to put a block down, go +; somewhere else, and pick it up again where it left off. +sbfsStreamFetch: + SETD.0 SbfsStreamOpen + LDA.0 + BRA sbfsStreamNo + + SETD.0 SbfsStreamTo + STD.1.0 + + SETD.0 SbfsFileStart + SETD.2 SbfsStreamAt + CALL sbfsSetWord + SETD.0 SbfsFileBlocks + SETD.2 SbfsStreamBlocks + CALL sbfsSetWord + SETD.0 SbfsStreamTail + LDA.0 + SETD.2 SbfsFileTail + STA.2 + + SETD.0 SbfsStreamTo + LDD.1.0 + CALL sbfsReadOne + RET + +; SbfsFileBlocks and SbfsFileTail say how big it turned out to be, which need not be how +; big it was started at. +; +; A WRITER MAY ASK FOR MORE ROOM THAN IT ENDS UP USING, and that is not laziness on its +; part. Some sizes are not knowable until the last byte is out: the assembler cannot say +; how many vectors a program installs until it has resolved them, and by then the file it +; is writing into must already exist. So the room is taken generously at the start, where +; running out costs nothing, and the size is told the truth here. +; +; The blocks that were asked for and not used go back to the free count. It is a note +; rather than the authority - the directory is - but a note worth keeping right, and two +; disks holding the same files must hold the same bytes. sbfsStreamDone: SETD.0 SbfsStreamOpen LDA.0 BRA sbfsStreamNo + ; What it really came to, put aside before anything walks the disk. + SETD.0 SbfsStreamNewBlocks + SETD.2 SbfsFileBlocks + CALL sbfsSetWord + SETD.0 SbfsFileTail + LDA.0 + SETD.1 SbfsStreamNewTail + STA.1 + ; Now, and not before, the old one goes. It may not be there at all, which is what ; writing something for the first time looks like from here. CALL sbfsStreamWhere @@ -2433,15 +2482,67 @@ sbfsStreamDone: CALL sbfsWipeFound sbfsStreamNoOld: - ; And the temporary takes its name, which is the whole of what committing is. + ; And the temporary takes its name and its true size, which together are the whole of + ; what committing is. CALL sbfsStreamTemp CALL sbfsScanFor BNQ sbfsStreamNo + + ; How much room it was given, before the entry that says so is changed. + CALL sbfsFileExtent + SETD.0 SbfsStreamSpare + SETD.2 SbfsWantBlocks + CALL sbfsSetWord + PSHD.3 POPD.1 DPUP.1 0d06 SETD.0 SbfsStreamLeaf CALL sbfsCopyName + + PSHD.3 + POPD.1 + DPUP.1 0d03 + SETD.0 SbfsStreamNewBlocks + CALL sbfsCopyWord + PSHD.3 + POPD.1 + DPUP.1 0d05 + SETD.0 SbfsStreamNewTail + LDA.0 + STA.1 + + SETD.1 SbfsBuffer + CALL sbfsBufferIn + CALL sbfsWriteBlock + BNQ sbfsStreamNo + + ; And the difference goes back, which is what it was given less what it kept. + SETD.0 SbfsFileBlocks + SETD.2 SbfsStreamNewBlocks + CALL sbfsSetWord + SETD.0 SbfsStreamNewTail + LDA.0 + SETD.1 SbfsFileTail + STA.1 + CALL sbfsFileExtent + SETD.0 SbfsStreamSpare + SETD.2 SbfsWantBlocks + CALL sbfsSubWord + + RSTA + SETD.0 SbfsBlock + STA.0 + INCD.0 + STA.0 + CALL sbfsReadBlock + BNQ sbfsStreamNo + SETD.1 SbfsBuffer + CALL sbfsBufferOut + SETD.0 SbfsBuffer + DPUP.0 0d12 + SETD.2 SbfsStreamSpare + CALL sbfsAddWord SETD.1 SbfsBuffer CALL sbfsBufferIn CALL sbfsWriteBlock @@ -2771,6 +2872,14 @@ SbfsStreamPath: 0x00 0x00 SbfsStreamFrom: 0x00 0x00 +SbfsStreamTo: + 0x00 0x00 +SbfsStreamNewBlocks: + 0x00 0x00 +SbfsStreamNewTail: + 0x00 +SbfsStreamSpare: + 0x00 0x00 SbfsStreamAt: 0x00 0x00 SbfsStreamParent: diff --git a/Programs/CosmOS/Source/services.asm b/Programs/CosmOS/Source/services.asm index 2462d9b..0690bfc 100644 --- a/Programs/CosmOS/Source/services.asm +++ b/Programs/CosmOS/Source/services.asm @@ -102,7 +102,13 @@ ; registers and cannot write more than 65,535. osFileStart 0d29 ; DP0 names it, DP3 is whole blocks, A is bytes in the tail. osFileWrite 0d30 ; DP1 is the block, A and B together are which one, from zero. - osFileDone 0d31 ; No arguments. The temporary takes the name. + osFileDone 0d31 ; DP3 is whole blocks and A the tail: how big it turned out to be. + osFileFetch 0d32 ; DP1 is where it goes, A and B are which block. Reads one back. + +; osFileFetch is what lets a program keep only ONE block of a file in hand while writing +; it. Anything producing two parts of a file at once - an assembler, whose source says +; #Program and #Data in whatever order it likes - has to be able to put a block down, go +; and write somewhere else, and pick it up again where it left off. ; Q is zero if it read, DP3 is how many of its bytes are the file's: ; a whole 0d256 except in a last block that is short. That count is ; why DP3 answers and not a register - 0d256 does not fit in a byte, diff --git a/Tests/expected/cosmosBreak.out b/Tests/expected/cosmosBreak.out index 2c39770..6dce33c 100644 --- a/Tests/expected/cosmosBreak.out +++ b/Tests/expected/cosmosBreak.out @@ -3,11 +3,11 @@ CosmOS > two stops, and what the registers were at each break at 400E A 11 B 22 Q 00 status 00 -DP0 2030 DP1 09F0 DP2 0000 DP3 4000 SP FFFF +DP0 2030 DP1 09F7 DP2 0000 DP3 4000 SP FFFF press a key break at 4023 A 44 B 55 Q 00 status 00 -DP0 2000 DP1 09F0 DP2 0000 DP3 4000 SP FFF5 +DP0 2000 DP1 09F7 DP2 0000 DP3 4000 SP FFF5 press a key carried on to the end finished