diff --git a/Programs/CosmOS/Source/script.asm b/Programs/CosmOS/Source/script.asm index 940649d..d92ee0c 100644 --- a/Programs/CosmOS/Source/script.asm +++ b/Programs/CosmOS/Source/script.asm @@ -154,8 +154,10 @@ scriptOpenFirst: INIB 0d63 CALL copyText + CALL scriptKeepDrive SETD.0 ScriptName SWI osFileInfo + CALL scriptGiveDriveBack BRQ scriptOpenThere INIA 0x01 BRI scriptOpenFailed @@ -426,6 +428,7 @@ scriptFill: OR BRQ scriptFillNoMore + CALL scriptKeepDrive SETD.0 ScriptName SETD.1 ScriptBlock SETD.2 ScriptIndex @@ -433,6 +436,7 @@ scriptFill: INCD.2 LDB.2 SWI osFileBlock + CALL scriptGiveDriveBack BNQ scriptFillNoMore ; DP3 is how many bytes came back. The nought goes after them. @@ -488,6 +492,40 @@ scriptFillNoMore: ADD RET +; ---- Reading a script must not move the person ---- +; +; The name is resolved afresh for every block, and a name with a drive in front of it moves +; the machine to that drive as a side effect of being resolved - sbfsWalk calls sbfsUse on +; the way past. So a script found in the system's place on drive 0, started by somebody +; standing on a disk of their own, would run its lines on the system disk instead of theirs. +; +; It matters more now than it used to. `do 0:/Apps/setup.sh` was always able to do this; +; typing a name now finds scripts the same three places a program is looked for, and the +; third of them is drive-qualified. +; +; The answer that survives the CALL: Q into A and the byte count onto the stack, because +; sbfsUse writes Q and RET puts DP3 back the way it found it. +scriptKeepDrive: + INA 0x24 + SETD.1 ScriptDrive + STA.1 + RET + +scriptGiveDriveBack: + PSHD.3 + MVQA + PSHA + SETD.1 ScriptDrive + LDA.1 + CALL sbfsUse + POPA + POPD.3 + ; A holds what Q held. Adding nought to it is how it becomes Q again. + RSTB + CCF + ADD + RET + ; Everything up to and including the next line feed, thrown away. Used for the shebang. scriptSkipLine: CALL scriptByte @@ -541,6 +579,11 @@ LoudWord: ScriptDepth: 0x00 +; The drive the person is standing on, kept across a block being fetched. Not part of the +; saved state below: it is only ever live for the length of one read. +ScriptDrive: + 0x00 + ; ---- Seventy bytes, and they are next to each other on purpose ---- ; ; Name, blocks left, next block, where in the block, and whether it is echoing: the whole of diff --git a/SplitBit Test Manual.md b/SplitBit Test Manual.md index 3967bc0..716a7e1 100644 --- a/SplitBit Test Manual.md +++ b/SplitBit Test Manual.md @@ -125,7 +125,7 @@ from `make`, not from here. ### 1. Recorded output `Tests/run.sh` assembles each program named in `Tests/manifest`, runs it, and compares -everything it printed against a file in `Tests/expected`. 212 tests, of which 150 run, 35 +everything it printed against a file in `Tests/expected`. 213 tests, of which 151 run, 35 only assemble, 16 are expected to fail to assemble, and 11 boot from ROM with no image given at all. diff --git a/Tests/expected/cosmosScriptDrive.out b/Tests/expected/cosmosScriptDrive.out new file mode 100644 index 0000000..6561a81 --- /dev/null +++ b/Tests/expected/cosmosScriptDrive.out @@ -0,0 +1,18 @@ +CosmOS +> drive 1 +> drive +1 +> where.sh +> echo the script is running +the script is running +> drive +1 +> drive 0 +> drive +0 +> drive +0 +> exit +halted +Execution halted. +[exit 0] diff --git a/Tests/input/cosmosScriptDrive.in b/Tests/input/cosmosScriptDrive.in new file mode 100644 index 0000000..8433866 --- /dev/null +++ b/Tests/input/cosmosScriptDrive.in @@ -0,0 +1,5 @@ +drive 1 +drive +where.sh +drive +exit diff --git a/Tests/makedisks.sh b/Tests/makedisks.sh index 15d4609..865cd71 100755 --- a/Tests/makedisks.sh +++ b/Tests/makedisks.sh @@ -227,6 +227,28 @@ python3 -c "open('twoblocks.txt','w').write('the second disk, at length. ' * 20) "$ROOT/Programs/CosmOS/Apps/Copy.asm" -o "$WORK/Copy.sbx" >/dev/null "$TOOL" put "$DISKS/cosmos.img" "$WORK/Copy.sbx" /Apps/Copy.sbx >/dev/null "$TOOL" put "$DISKS/cosmos.img" "$WORK/Say.sbx" /Apps/Say.sbx >/dev/null +# A SCRIPT in the system's place, which is the one thing on this disk that is fetched from +# drive 0 a block at a time WHILE it runs. Its own name carries the drive in front of it, +# and resolving a name like that moves the machine to that drive - so a script started from +# another disk would run its lines on this one unless something puts the drive back between +# every block. All it does is say where it thinks it is, twice, with a move in between. +python3 -c " +# LONGER THAN A BLOCK on purpose, twice over. A script that fits in one block is read +# entirely while it is being opened, and the opening is not the hard part - the refills are, +# because they happen after the drive has been given back to whoever typed the name. +# +# And it moves itself between the two refills, which is the second half of the same rule: a +# script that goes to another disk has to stay there, so a refill puts back the drive the +# script left the machine on and not the one it was opened from. +def pad(so_far): + out = '' + while so_far + len(out) < 300: + out += '; pad\\n' + return out +one = '#!\\necho the script is running\\ndrive\\n' +two = 'drive 0\\n' +open('where.sh', 'w').write(one + pad(len(one)) + two + pad(len(two)) + 'drive\\n')" +"$TOOL" put "$DISKS/cosmos.img" where.sh /Apps/where.sh >/dev/null # ---- Scripts, including the ones that are meant to go wrong ---- # diff --git a/Tests/manifest b/Tests/manifest index 537a780..1f871e6 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -1123,6 +1123,19 @@ cosmosDrivePath | CosmOS/Source/cosmos.asm | run | cosmosDri # place the shell looks. The drive it says afterwards is the check that fetching a program # did not move the person who ran it. cosmosCrossDisk | CosmOS/Source/cosmos.asm | run | cosmosCrossDisk.in | 90000000 | disks/cosmos.img+disks/other.img +# ---- And a SCRIPT run from the other disk ---- +# +# The same question one step harder. A program is wholly in memory before its first +# instruction runs, so fetching it can move the drive and putting it back afterwards is +# enough. A script is read a block at a time WHILE its lines run, and its name carries the +# drive it lives on - so every refill resolves "0:/Apps/where.sh" and every resolution moves +# the machine to drive 0 unless something puts it back. What the script says about where it +# is standing is the check, and it must say 1. +# +# THEN IT MOVES ITSELF and says so again, across another block boundary. A refill has to put +# back the drive the script left the machine on rather than the one it was opened from, or a +# script that goes to another disk to work is dragged home between two of its own lines. +cosmosScriptDrive | CosmOS/Source/cosmos.asm | run | cosmosScriptDrive.in | 60000000 | disks/cosmos.img+disks/other.img # ---- A beat a program sets for itself ---- # # That reading the status is what takes the tick down, and that without the repeat bit it