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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
9f7dffdeca
commit
fb7b224bbb
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user