diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index de98093..5b2c5f9 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -1259,7 +1259,7 @@ Those numbers are written down once, in `Programs/CosmOS/Source/services.asm`, w | osReadLine | DP0 names somewhere to put a line, B says how much room there is. Reads one from the console, with the shell's own editing - arrows, Home, End, Delete - but not its history. Q comes back holding how long it was. The console is left in whatever mode it was found in. | | osExit | Gives the machine back. Does not return. | | osArgument | DP0 names somewhere to put whatever followed the run command, B says how much room there is. | -| osFileRead | DP0 names a file, DP1 says where to put it. Q is zero if it read, and DP3 comes back holding how many bytes there were. | +| osFileRead | DP0 names a file, DP1 says where to put it. Q is zero if it read, and DP3 comes back holding how many bytes there were. It writes WHOLE BLOCKS: a sixteen byte file still puts 256 bytes where it is told, so the room given has to be the length rounded up to the next 256. | | osFileSave | DP0 names a file, DP1 is the bytes, A and B together are how many. Q is zero if it saved, whether or not it was there before. | | osFileDelete | DP0 names a file. Q is zero if it went. | | osFileRename | DP0 is the name a file has, DP1 the name it should have. Q is zero if it moved. | @@ -1317,6 +1317,13 @@ A file of 256 blocks or more is refused by `osFileRead` rather than partly read, ### Reading A File That Will Not Fit: +`osFileRead` writes in whole blocks, because a block is what a disk is read in. A sixteen +byte file still puts 256 bytes wherever it is pointed, and a caller that reserves exactly the +file's length writes over whatever follows it - which is a quiet corruption rather than a +refusal, and looks like a bug somewhere else entirely. `DP3` still reports the file's own +length; the bytes past it are the rest of the block. Reserve the length rounded up to the next +256, read into that, and copy the parts wanted where they are wanted. + `osFileRead` hands over a whole file, which settles the question for anything under 64K and settles nothing above it. CosmOS's own source is above it: the sources together are a hundred kilobytes and Data Memory is sixty four. A machine that assembles itself has to be able to read a file bigger than its memory, and this is what that stands on. So there is a second way to ask. `osFileInfo` says how big something is and `osFileBlock` hands over one block of it, and between them a program reads a file of any size through a buffer of 256 bytes. diff --git a/Programs/CosmOS/Source/services.asm b/Programs/CosmOS/Source/services.asm index 38cce47..d21d313 100644 --- a/Programs/CosmOS/Source/services.asm +++ b/Programs/CosmOS/Source/services.asm @@ -41,6 +41,13 @@ ; going out it is A and B together, and neither direction needs a record in memory that ; both sides have to agree on the shape of. osFileRead 0d20 ; DP0 names it, DP1 says where. Q is zero if it read, DP3 is how many bytes. + ; ---- AND IT WRITES WHOLE BLOCKS ---- + ; + ; A disk is read a block at a time, so a sixteen byte file still puts + ; 256 bytes where it is told to. The room given has to be the file's + ; length ROUNDED UP to the next 256, and a caller that gives exactly + ; the length writes over whatever follows it. DP3 still says how many + ; bytes are the file's; the rest is whatever was on the block. osFileSave 0d21 ; DP0 names it, DP1 is the bytes, A and B are how many. Q is zero if it saved. osFileDelete 0d22 ; DP0 names it. Q is zero if it went. osFileRename 0d23 ; DP0 is the name it has, DP1 the name it should have. Q is zero if it moved. diff --git a/Tests/video.sh b/Tests/video.sh index 71652eb..c25dff6 100755 --- a/Tests/video.sh +++ b/Tests/video.sh @@ -1788,35 +1788,67 @@ HOME="$(countColour "$BUILD/ceiling22000000.ppm" f0f0f0)" && result ok "and gravity brings it back from there" "the warning went with the height" \ || result no "and gravity brings it back from there" "$HOME pixels still up" -# ---- Orbit, which is measured and not checked here ---- +# ---- Orbit, which can be checked again now it can be placed ---- # # Gravity minus the swing outwards. Below orbital speed the pull wins and the lander falls; -# above it the swing wins and the lander climbs; and falling buys sideways speed while -# climbing spends it. That is what makes a closed orbit rather than a one way trip, and it -# was measured doing it. Placed at 80 sideways and left alone, turning points at: +# above it the swing wins and it climbs; and falling buys sideways speed while climbing spends +# it. That is what makes a closed orbit rather than a one way trip. # -# tick 44 height -1024 sideways 59 apoapse, and it falls away -# tick 123 height -124 sideways 71 periapse, and it climbs again -# tick 165 height -1024 sideways 59 -# tick 241 height -124 sideways 70 -# tick 299 height -1024 sideways 59 -# tick 369 height -114 sideways 70 +# THERE WAS NO CHECK FOR THIS FOR A LONG TIME, and the reason is worth keeping. Reaching a +# given orbit through the controls takes a sustained burn while holding height, and the phase +# of that burn against the gravity tick - one frame in ten - decides whether the thruster is +# seen at all, so two pad files a frame apart fly differently. A version of this check did +# exist and passed against one disk and failed against another, which is a check measuring the +# boot time rather than the physics. It was deleted rather than left looking tested. # -# Four hundred and eighty ticks with no sign of the amplitude decaying, a period of about a -# hundred and thirty ticks, which is twenty two seconds of play. -# -# THERE IS STILL NO CHECK FOR IT, and it is still deliberate. The measurement above was taken -# with a build that PLACED the lander at eighty sideways, because reaching orbital speed -# through the controls takes a sustained burn while holding height, and the phase of that burn -# against the gravity tick - one frame in ten - decides whether the thruster is seen at all. -# Two pad files a frame apart fly differently. That is a fine thing for a game and a poor one -# for a check. -# -# What would make it checkable is still the same thing: a way to put the lander somewhere with -# a velocity, without flying it there, which is now wanted by four separate untested paths. -# The other candidate is a hand flown recording of an orbit, the way the delivery below is a -# hand flown delivery. Until one of those exists this is verified by somebody measuring it, -# and said so here rather than left looking tested. +# A state file settles it. Placed at eighty sideways, which is sixteen over orbital, the +# lander climbs, turns over, falls, and climbs again - and the turning points are BROAD, tens +# of pixels across, so the samples below have nothing like the margin problem the old one did. +# The station is parked below the surface out of the way, because it laps every four seconds +# and would otherwise wander into the middle of the measurement. +python3 -c " +import struct +open('$BUILD/orbit.state','wb').write(struct.pack(' /dev/null +for when in 3000000 13000000 25000000 37000000; do + timeout 60 "$EMU" --fast --cycles $when --keyboard "$BUILD/fly.keys" \ + --pad "$BUILD/none.pad" --screen "$BUILD/orbit$when.ppm" \ + --disk "$BUILD/orbit.img" --ram-disk 2048 \ + "$BUILD/cosmos.bin" > "$BUILD/orbit.out" 2>&1 || true +done +read -r LOWONE HIGHONE LOWTWO HIGHTWO </dev/null || echo "-1 -1 -1 -1") +EOT +# A smaller row is higher up. Fifty pixels of change either way, against turning points that +# are flat to within ten - so this is measuring the turn and not the sampling. +[ "$HIGHONE" -lt "$(( LOWONE - 50 ))" ] && [ "$HIGHONE" -gt 0 ] \ + && result ok "a lander over orbital speed climbs" "row $LOWONE became row $HIGHONE" \ + || result no "a lander over orbital speed climbs" "row $LOWONE became row $HIGHONE" +# And turns over on its own, with nothing touched: the climb spends the speed that bought it. +[ "$LOWTWO" -gt "$(( HIGHONE + 50 ))" ] \ + && result ok "and then falls again, which is an apoapse" "row $HIGHONE became row $LOWTWO" \ + || result no "and then falls again, which is an apoapse" "row $HIGHONE became row $LOWTWO" +# Round again, and no lower than the first time: the exchange is a trade, not a leak. +[ "$HIGHTWO" -lt "$(( LOWTWO - 50 ))" ] && [ "$HIGHTWO" -le "$HIGHONE" ] \ + && result ok "and round again without decaying" "$HIGHONE, $LOWTWO, then $HIGHTWO" \ + || result no "and round again without decaying" "$HIGHONE, $LOWTWO, then $HIGHTWO" # ---- A delivery, flown by hand, and what it cost ---- #