Work across two disks: copy between them, and run a program from one on
files from the other Two things anybody expects of a second disk, and each needed something different. COPYING NEEDED TWO THINGS TO REMEMBER A DRIVE. The write stream is the only thing here that lives across service calls, so it is the only thing whose drive can change underneath it: every osFileBlock names its source path again and goes back to the source drive, and then osFileWrite has to come home. It records the drive it was opened on and returns there. And the file lookup CACHE. It keeps the last path resolved so a reader walking a file does not re-walk the directory for every block - and skipping the walk skipped the drive the path named, so block one of a cross-drive copy read the source's block numbers off the DESTINATION disk. It only showed on files of more than one block, because a file of one is never looked up twice. One block worked and two did not, which is a suspicious enough shape to have suspected sooner. RUNNING A PROGRAM FROM ELSEWHERE NEEDED A THIRD PLACE TO LOOK, and two restorations. The shell tried where you are and /Apps on the disk you are on. It now tries /Apps on drive 0 as well, which is what makes the system's programs work from a disk of your own - one with your files on it and no system, which is most of the point of having a second disk. The drive goes back after the load, because by then the program is in memory and the blocks it came from mean nothing; and again when it exits, because a program that copies between disks moves the drive as its own paths need to and being left wherever it finished is not what was asked for. Copy 1:/a 0:/b now leaves you exactly where you were. The fixture disk grew an /Apps, because it kept its programs at the root and so could not exercise the third place at all. Two hours of the debugging above were spent on a stale disk image. The machine boots the system that is ON the image, so a rebuilt cosmos.bin means nothing until the image is rebuilt too - and the trace said my new code never ran, which was true. Third time this project has been misled by one. 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
4cc6393f5b
commit
e4f4bae762
@@ -836,10 +836,28 @@ tells the two apart. `2things` is a directory; `2:` is a drive.
|
||||
A drive the machine cannot read makes the whole path unfindable, and says so as `no such
|
||||
file` - which it is, since there is nowhere for the rest of it to be.
|
||||
|
||||
**Copying between two disks is still not one command.** Each path resolves on its own drive
|
||||
and the drive stays where the last path left it, so a copy would read from one and write to
|
||||
the other with only one of them selected. That wants the copy itself to change drives between
|
||||
blocks, which is a change to `Copy` rather than to paths.
|
||||
**Copying between two disks is one command**: `Copy 1:/notes.txt 0:/keep.txt`. Every
|
||||
`osFileBlock` names its path again and so goes back to the source drive; the write stream
|
||||
remembers the drive it was opened on and returns there for each block. Between them the copy
|
||||
walks back and forth without `Copy` itself knowing there is more than one disk.
|
||||
|
||||
### Where A Program Is Looked For:
|
||||
|
||||
Three places, tried in order:
|
||||
|
||||
1. Where you are.
|
||||
2. `/Apps` on the disk you are on.
|
||||
3. `/Apps` on drive 0.
|
||||
|
||||
The first makes a program you are working on the one that runs. The second makes `Snake` work
|
||||
from any directory. **The third makes the system's programs work from a disk of your own** -
|
||||
one with your files on it and no system - which is most of the point of having a second disk.
|
||||
|
||||
**Fetching a program does not move you, and neither does running one.** The drive is put back
|
||||
after the load, because by then the program is in memory and the block numbers it came from
|
||||
mean nothing; and put back again when the program exits, because a program that copies between
|
||||
two disks moves the drive as its own paths need it to and being left wherever it finished is
|
||||
not what anybody asked for. So `Copy 1:/a 0:/b` leaves you exactly where you were.
|
||||
|
||||
### Bank Numbers Are One Namespace:
|
||||
|
||||
|
||||
@@ -437,14 +437,24 @@ promptUnknown:
|
||||
LDA.0
|
||||
BRA promptSayUnknown ; No filesystem, so there is nothing to look through.
|
||||
|
||||
; TWO PLACES, TRIED IN ORDER: where you are, and then the system's own place for
|
||||
; programs. The first is what makes a program you are working on the one that runs; the
|
||||
; second is what makes Snake work from anywhere without a copy of it in every directory.
|
||||
; Neither is stored anywhere, so there is nothing to configure and nothing to go stale.
|
||||
; THREE PLACES, TRIED IN ORDER: where you are, the system's own place for programs on the
|
||||
; disk you are on, and then that same place on drive 0. The first is what makes a program
|
||||
; you are working on the one that runs; the second is what makes Snake work from anywhere
|
||||
; without a copy of it in every directory; the third is what makes it work from a disk of
|
||||
; your own, which has your files on it and no system.
|
||||
;
|
||||
; None of them is stored anywhere, so there is nothing to configure and nothing to go
|
||||
; stale.
|
||||
RSTA
|
||||
SETD.0 NamePrefix
|
||||
STA.0
|
||||
|
||||
; Where the person is, kept so it can be given back. A program is fetched from wherever it
|
||||
; lives and then runs on the files of whoever ran it.
|
||||
INA 0x24
|
||||
SETD.0 SearchDrive
|
||||
STA.0
|
||||
|
||||
promptSearch:
|
||||
CALL nameProgram
|
||||
SETD.0 NameOk
|
||||
@@ -454,7 +464,22 @@ promptSearch:
|
||||
CALL loadProgram
|
||||
SETD.0 LoadStatus
|
||||
LDA.0
|
||||
BRA runLoaded ; It loaded, and the machine is its now.
|
||||
BNA promptNotLoaded
|
||||
|
||||
; ---- Loaded, so the drive goes back to the person ----
|
||||
;
|
||||
; The program is in memory now and the block numbers it came from mean nothing any more,
|
||||
; which is what makes this safe here and not in the middle of a path. A program fetched
|
||||
; from the system disk then runs on the disk its user was standing on - which is the whole
|
||||
; point of being able to keep a disk of your own.
|
||||
SETD.0 SearchDrive
|
||||
LDA.0
|
||||
CALL sbfsUse
|
||||
BRI runLoaded ; It loaded, and the machine is its now.
|
||||
|
||||
promptNotLoaded:
|
||||
SETD.0 LoadStatus
|
||||
LDA.0
|
||||
|
||||
; No file of that name is not a fault. It is the ordinary case of a word this shell does
|
||||
; not know, and it is the only answer worth looking somewhere else for. Anything else
|
||||
@@ -467,10 +492,13 @@ promptSearch:
|
||||
BNQ loadFailed
|
||||
|
||||
promptElsewhere:
|
||||
; Was that already the second place?
|
||||
; Was that the last place there is?
|
||||
SETD.0 NamePrefix
|
||||
LDA.0
|
||||
BNA promptSayUnknown
|
||||
INIB 0d2
|
||||
CCF
|
||||
SUB
|
||||
BRQ promptGaveUp
|
||||
|
||||
; A word beginning with a separator has said where to look, and looking somewhere else
|
||||
; would be answering a different question from the one asked.
|
||||
@@ -481,11 +509,19 @@ promptElsewhere:
|
||||
SUB
|
||||
BRQ promptSayUnknown
|
||||
|
||||
INIA 0x01
|
||||
SETD.0 NamePrefix
|
||||
LDA.0
|
||||
INCA
|
||||
STA.0
|
||||
BRI promptSearch
|
||||
|
||||
promptGaveUp:
|
||||
; Every place has been tried, and the drive is put back before anybody is told anything:
|
||||
; the search moved it, and a word the shell does not know should not move somebody either.
|
||||
SETD.0 SearchDrive
|
||||
LDA.0
|
||||
CALL sbfsUse
|
||||
|
||||
promptSayUnknown:
|
||||
; Saying which word was not understood is worth the four instructions: it tells somebody
|
||||
; who mistyped what they actually typed.
|
||||
@@ -760,7 +796,15 @@ nameProgram:
|
||||
SETD.0 NamePrefix
|
||||
LDA.0
|
||||
BRA nameFromLine
|
||||
; One is the system's place on this disk; two is the same place on drive 0.
|
||||
INIB 0d2
|
||||
CCF
|
||||
SUB
|
||||
BRQ nameSystemApps
|
||||
SETD.0 AppsPrefix
|
||||
BRI namePrefixCopy
|
||||
nameSystemApps:
|
||||
SETD.0 SystemAppsPrefix
|
||||
namePrefixCopy:
|
||||
LDA.0
|
||||
BRA nameFromLine
|
||||
@@ -1789,6 +1833,13 @@ scriptNoName:
|
||||
SETD.0 ScriptUsage
|
||||
BRI fileComplain
|
||||
|
||||
; Where the person was standing when a program was started, so it can be given back when the
|
||||
; program has finished with the machine. A program that copies between two disks moves the
|
||||
; drive as its own paths need it to; that is its business, and being left on the disk it
|
||||
; happened to finish with is not what the person asked for.
|
||||
;
|
||||
; SET WHEREVER A PROGRAM STARTS, of which there are two: run, and typing a program's name.
|
||||
; Restored in handleExit, which is the one place they both come back through.
|
||||
; ---- run ----
|
||||
;
|
||||
; Hands the machine to whatever was loaded. Where the Stack is now is written down first,
|
||||
@@ -1805,6 +1856,9 @@ doRun:
|
||||
; program's own name it is what followed the name, which is the same thing meaning the
|
||||
; same thing.
|
||||
runLoaded:
|
||||
INA 0x24
|
||||
SETD.0 RunDrive
|
||||
STA.0
|
||||
MVSD.0
|
||||
SETD.1 SystemStack
|
||||
STD.0.1
|
||||
@@ -2151,9 +2205,20 @@ fileLookup:
|
||||
CALL textSame
|
||||
BNQ fileLookupSearch
|
||||
|
||||
; The same file as last time. The description still has to be put back, because anything
|
||||
; that went to the disk in between - a directory listing, a program being loaded - left
|
||||
; its own answer in those three.
|
||||
; ---- The same file as last time, and possibly not the same disk ----
|
||||
;
|
||||
; The description has to be put back, because anything that went to the disk in between - a
|
||||
; directory listing, a program being loaded - left its own answer in those three.
|
||||
;
|
||||
; AND THE DRIVE WITH IT. Skipping the walk skips the drive the path named, so a cache hit
|
||||
; on a machine that had moved read the right block numbers off the wrong disk. Copying
|
||||
; between two disks is exactly that: block 0 walks and goes to the source, the write goes to
|
||||
; the destination, and block 1 hits this cache. It only showed on files of more than one
|
||||
; block, because a file of one is never looked up twice.
|
||||
SETD.0 FileCacheDrive
|
||||
LDA.0
|
||||
CALL sbfsUse
|
||||
|
||||
SETD.0 SbfsFileStart
|
||||
SETD.2 FileCacheStart
|
||||
CALL sbfsSetWord
|
||||
@@ -2196,6 +2261,14 @@ fileLookupSearch:
|
||||
SETD.0 FileCacheTail
|
||||
STA.0
|
||||
|
||||
; ---- And which disk those block numbers are on ----
|
||||
;
|
||||
; A start block means nothing without it. The walk above went to whatever drive the path
|
||||
; named, so the answer is the drive now.
|
||||
INA 0x24
|
||||
SETD.0 FileCacheDrive
|
||||
STA.0
|
||||
|
||||
; Marked good last, so that a cache half filled is never a cache believed.
|
||||
INIA 0d1
|
||||
SETD.0 FileCacheValid
|
||||
@@ -2622,6 +2695,13 @@ handleExit:
|
||||
SETD.1 LastStatus
|
||||
STA.1
|
||||
|
||||
; The drive the person was on, whatever the program did with it.
|
||||
PSHA
|
||||
SETD.1 RunDrive
|
||||
LDA.1
|
||||
CALL sbfsUse
|
||||
POPA
|
||||
|
||||
; And the line that started it failed, if the program says it did. The script reader asks
|
||||
; one question - did this line work - and a program answering "no" is one of the ways it
|
||||
; can be answered.
|
||||
@@ -3831,6 +3911,8 @@ Separator:
|
||||
; next, and there is no such place yet.
|
||||
AppsPrefix:
|
||||
"/Apps/"
|
||||
SystemAppsPrefix:
|
||||
"0:/Apps/"
|
||||
IsDirectory:
|
||||
"that is a directory"
|
||||
AndText:
|
||||
@@ -4106,6 +4188,12 @@ PrintNumber:
|
||||
;
|
||||
; The name is twenty three bytes for a name of twenty two, so that a name filling the
|
||||
; field still has a zero after it and can be compared as a string.
|
||||
FileCacheDrive:
|
||||
0x00
|
||||
SearchDrive:
|
||||
0x00
|
||||
RunDrive:
|
||||
0x00
|
||||
FileCacheValid:
|
||||
0x00
|
||||
FileCacheName:
|
||||
|
||||
@@ -2932,6 +2932,16 @@ sbfsStreamNoTemp:
|
||||
SETD.2 SbfsFileStart
|
||||
CALL sbfsSetWord
|
||||
|
||||
; ---- And which disk all of that is on ----
|
||||
;
|
||||
; A write stream is the one thing here that lives across service calls, so it is the one
|
||||
; thing that can have the drive changed underneath it. Copying between two disks is exactly
|
||||
; that: every osFileBlock re-resolves the SOURCE path and goes to its drive, and then
|
||||
; osFileWrite has to come back here. The path was walked above, so the drive is right now.
|
||||
INA 0x24
|
||||
SETD.0 SbfsStreamDrive
|
||||
STA.0
|
||||
|
||||
INIA 0x01
|
||||
SETD.0 SbfsStreamOpen
|
||||
STA.0
|
||||
@@ -2941,6 +2951,13 @@ sbfsStreamNoTemp:
|
||||
ADD
|
||||
RET
|
||||
|
||||
; Back to the disk the open stream belongs to, whatever has been read in between.
|
||||
sbfsStreamHere:
|
||||
SETD.0 SbfsStreamDrive
|
||||
LDA.0
|
||||
CALL sbfsUse
|
||||
RET
|
||||
|
||||
sbfsStreamNo:
|
||||
RSTA
|
||||
INIB 0d1
|
||||
@@ -2954,6 +2971,7 @@ sbfsStreamWrite:
|
||||
SETD.0 SbfsStreamOpen
|
||||
LDA.0
|
||||
BRA sbfsStreamNo
|
||||
CALL sbfsStreamHere
|
||||
|
||||
; Where the file is and how big it is, said again rather than looked up: all three were
|
||||
; settled when the temporary was made, and everything since has been describing whatever
|
||||
@@ -2980,6 +2998,7 @@ sbfsStreamFetch:
|
||||
SETD.0 SbfsStreamOpen
|
||||
LDA.0
|
||||
BRA sbfsStreamNo
|
||||
CALL sbfsStreamHere
|
||||
|
||||
SETD.0 SbfsStreamTo
|
||||
STD.1.0
|
||||
@@ -3016,6 +3035,7 @@ sbfsStreamDone:
|
||||
SETD.0 SbfsStreamOpen
|
||||
LDA.0
|
||||
BRA sbfsStreamNo
|
||||
CALL sbfsStreamHere
|
||||
|
||||
; What it really came to, put aside before anything walks the disk.
|
||||
SETD.0 SbfsStreamNewBlocks
|
||||
@@ -3639,6 +3659,8 @@ SbfsTempName:
|
||||
"sbfs.part"
|
||||
|
||||
; ---- What a file being written a block at a time keeps ----
|
||||
SbfsStreamDrive:
|
||||
0x00
|
||||
SbfsStreamOpen:
|
||||
0x00
|
||||
SbfsStreamPath:
|
||||
|
||||
@@ -79,7 +79,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`. 181 tests, of which 119 run, 35
|
||||
everything it printed against a file in `Tests/expected`. 182 tests, of which 120 run, 35
|
||||
only assemble, 16 are expected to fail to assemble, and 11 boot from ROM with no image
|
||||
given at all.
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
CosmOS
|
||||
> > copied
|
||||
finished
|
||||
> 1
|
||||
> it says: from drive one
|
||||
finished
|
||||
> 1
|
||||
> > greet.sbx 211
|
||||
hello.sbx 53
|
||||
Life.sbx 1396
|
||||
Snake.sbx 2164
|
||||
Keys.sbx 664
|
||||
Say.sbx 156
|
||||
Break.sbx 149
|
||||
Grid.sbx 543
|
||||
notes.txt 21
|
||||
Apps <dir>
|
||||
hi.script 121
|
||||
bad.script 45
|
||||
plain.script 24
|
||||
cross.script 280
|
||||
nonl.script 38
|
||||
outer.script 376
|
||||
inner.script 44
|
||||
loop.script 35
|
||||
crossed.txt 560
|
||||
18 files, 1 directory
|
||||
> halted
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
@@ -9,6 +9,7 @@ Say.sbx 156
|
||||
Break.sbx 149
|
||||
Grid.sbx 543
|
||||
notes.txt 21
|
||||
Apps <dir>
|
||||
hi.script 121
|
||||
bad.script 45
|
||||
plain.script 24
|
||||
@@ -17,11 +18,12 @@ nonl.script 38
|
||||
outer.script 376
|
||||
inner.script 44
|
||||
loop.script 35
|
||||
17 files
|
||||
17 files, 1 directory
|
||||
> > other.txt 28
|
||||
notes <dir>
|
||||
2things <dir>
|
||||
1 file, 2 directories
|
||||
twoblocks.txt 560
|
||||
2 files, 2 directories
|
||||
> /notes> > /notes> > /notes> drive: this machine has no such drive
|
||||
/notes> halted
|
||||
Execution halted.
|
||||
|
||||
@@ -13,6 +13,7 @@ Say.sbx 156
|
||||
Break.sbx 149
|
||||
Grid.sbx 543
|
||||
notes.txt 21
|
||||
Apps <dir>
|
||||
hi.script 121
|
||||
bad.script 45
|
||||
plain.script 24
|
||||
@@ -21,7 +22,7 @@ nonl.script 38
|
||||
outer.script 376
|
||||
inner.script 44
|
||||
loop.script 35
|
||||
17 files
|
||||
17 files, 1 directory
|
||||
> halted
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -9,6 +9,7 @@ Say.sbx 156
|
||||
Break.sbx 149
|
||||
Grid.sbx 543
|
||||
notes.txt 21
|
||||
Apps <dir>
|
||||
hi.script 121
|
||||
bad.script 45
|
||||
plain.script 24
|
||||
@@ -17,7 +18,7 @@ nonl.script 38
|
||||
outer.script 376
|
||||
inner.script 44
|
||||
loop.script 35
|
||||
17 files
|
||||
17 files, 1 directory
|
||||
> load what?
|
||||
> no such file
|
||||
> not a program
|
||||
|
||||
@@ -8,6 +8,7 @@ Say.sbx 156
|
||||
Break.sbx 149
|
||||
Grid.sbx 543
|
||||
notes.txt 21
|
||||
Apps <dir>
|
||||
hi.script 121
|
||||
bad.script 45
|
||||
plain.script 24
|
||||
@@ -16,7 +17,7 @@ nonl.script 38
|
||||
outer.script 376
|
||||
inner.script 44
|
||||
loop.script 35
|
||||
17 files
|
||||
17 files, 1 directory
|
||||
> loaded, starting at 4000
|
||||
> it says: the disk took its time
|
||||
finished
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
drive 1
|
||||
Copy 1:/twoblocks.txt 0:/crossed.txt
|
||||
drive
|
||||
Say from drive one
|
||||
drive
|
||||
cd 0:/
|
||||
dir
|
||||
exit
|
||||
@@ -127,6 +127,23 @@ printf 'this lives on the other disk' > other.txt
|
||||
# A name that begins with a digit, because a drive prefix is a digit and a colon and the
|
||||
# colon is the whole of what tells them apart. Without it this would be drive 2.
|
||||
"$TOOL" mkdir "$DISKS/other.img" /2things >/dev/null
|
||||
# A file of more than one block, because a cross-drive copy of a single block never looks
|
||||
# the path up twice and so never meets the cache that used to hand back the right blocks of
|
||||
# the wrong disk.
|
||||
python3 -c "open('twoblocks.txt','w').write('the second disk, at length. ' * 20)"
|
||||
"$TOOL" put "$DISKS/other.img" twoblocks.txt >/dev/null
|
||||
|
||||
# ---- And a /Apps on drive 0, because that is where a system keeps its programs ----
|
||||
#
|
||||
# The apps above sit at the root of this image, which is where they were put before there
|
||||
# were directories and is fine for finding a program on the disk you are standing on. The
|
||||
# THIRD place the shell looks is /Apps on drive 0, and nothing could exercise it while this
|
||||
# disk had no such place.
|
||||
"$TOOL" mkdir "$DISKS/cosmos.img" /Apps >/dev/null
|
||||
"$ROOT/Assembler" -I "$ROOT/Programs/Libraries" -I "$ROOT/Programs/CosmOS/Source" \
|
||||
"$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
|
||||
|
||||
# ---- Scripts, including the ones that are meant to go wrong ----
|
||||
#
|
||||
|
||||
@@ -824,6 +824,20 @@ cosmosDrives | CosmOS/Source/cosmos.asm | run | cosmosDri
|
||||
# system arrives, so it works for anything that takes one. A name beginning with a digit is
|
||||
# still a name: the colon is the whole of what tells the two apart.
|
||||
cosmosDrivePath | CosmOS/Source/cosmos.asm | run | cosmosDrivePath.in | 60000000 | disks/cosmos.img+disks/other.img
|
||||
# ---- Working across two disks ----
|
||||
#
|
||||
# The two things anybody expects of a second disk: copying to it, and running a program that
|
||||
# lives on one disk over files that live on the other.
|
||||
#
|
||||
# THE FILE IS MORE THAN ONE BLOCK ON PURPOSE. A copy of a single block looks its source up
|
||||
# once and never meets the cache that skips the walk - and skipping the walk skipped the
|
||||
# drive, so block one of every cross-drive copy came off the destination. One block worked
|
||||
# and two did not.
|
||||
#
|
||||
# Say lives in /Apps on drive 0 and is run while standing on drive 1, which is the third
|
||||
# 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
|
||||
printDecimalTest | testPrograms/printDecimalTest.asm | xfail | - | -
|
||||
printDigitTest | testPrograms/printDigitTest.asm | xfail | - | -
|
||||
printHexTest | testPrograms/printHexTest.asm | xfail | - | -
|
||||
|
||||
Reference in New Issue
Block a user