D3: the machine knows where it is

cd moves it, dir lists the directory it is in, and the prompt says which one -
but only when that is not the root, so a machine nobody has moved about on
looks exactly as it always did and every recorded test that never says "cd"
keeps its recorded prompt.

A path beginning with a separator is measured from the root and anything else
from where the machine is, so a bare name means a file in the current
directory. NO PROGRAM HAD TO BE TOLD: the working directory lives in sbfs.asm
beside the thing that resolves paths, because it is what a relative path MEANS.
Keeping it in the shell would have meant either handing it down on every call
or pasting it onto the front of every name, and the second of those is how a
name that is already absolute gets ruined.

Nothing stores the path. The working directory is an entry index and two bytes,
and the text on the prompt is built each time by walking the chain of parents
upward, writing names from the end of a buffer towards the front - which is the
order they arrive in, and saves reversing them afterwards.

sbfsFind splits into a walk and a check. "cd /" and "cd .." both end at the
root quite legitimately, and had no way to say so through a routine whose only
word for the root was "missing".

Typing a program's name now tries two places in order: where you are, then
/Apps. The first makes a program you are working on the one that runs; the
second lets Snake work from anywhere. A word already beginning with a separator
has said where to look, so only that place is tried.

osChangeDir exists so that "a program may move about, and the shell puts the
working directory back" is a thing that can happen rather than a promise about
nothing. Both halves of that were unfalsifiable without it: with no way for a
program to move, removing the restore changed no test. Wander is the program
that moves - it goes where it is told and reads a file there by a bare name -
and with it on the disk, removing the restore fails.

The remembered file is dropped whenever what a relative path means changes: a
cd, a program calling osChangeDir, a program exiting. Removing all of them
fails the test and removing any one of them does not, because today every path
into that cache belongs to a program that exits. It is kept in all three
because the cost is a call and the failure is a file's blocks being handed out
under another file's name.

The cwd fixture holds two files called notes.txt saying different things, and a
Say.sbx in /A that is really hello. Two copies of one program, or two copies of
one file, would have passed with the whole of this deleted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-08-24 22:22:45 -04:00
co-authored by Claude Opus 5
parent 588e02aff5
commit 36ce9f6ccf
14 changed files with 759 additions and 38 deletions
+2 -1
View File
@@ -2,7 +2,8 @@ CosmOS
> dir list what is on the disk
load <file> read a program off the disk
run [words] start what was loaded, and tell it those words
<name> [words] load and start that program off the disk
<name> [words] look where you are and then in /Apps, and start that
cd [path] go to a directory, or to the root with nothing after it
delete <file> take it off the disk
rename <file> <to> call it something else
monitor look at memory, change it, and jump into it
+2 -2
View File
@@ -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 0730 DP2 0000 DP3 4000 SP FFFF
DP0 2030 DP1 082E DP2 0000 DP3 4000 SP FFFF
press a key
break at 4023
A 44 B 55 Q 00 status 00
DP0 2000 DP1 0730 DP2 0000 DP3 4000 SP FFF5
DP0 2000 DP1 082E DP2 0000 DP3 4000 SP FFF5
press a key
carried on to the end
finished
+36
View File
@@ -0,0 +1,36 @@
CosmOS
> Apps <dir>
A <dir>
B <dir>
0 files, 3 directories
> /A> Say.sbx 52
notes.txt 25
2 files
/A> these are the notes in A
finished
/A> /B> and these are the very different notes in B
finished
/B> it says: reached the one in Apps
finished
/B> /A> Hello, World!
finished
/A> > Apps <dir>
A <dir>
B <dir>
0 files, 3 directories
> /B> these are the notes in A
finished
/B> moved, and reading a bare name from there:
these are the notes in A
finished
/B> and these are the very different notes in B
finished
/B> no such file
/B> that is not a directory
/B> > Apps <dir>
A <dir>
B <dir>
0 files, 3 directories
> halted
Execution halted.
[exit 0]
+2 -1
View File
@@ -19,7 +19,8 @@ notes.sbx 21
> dir list what is on the disk
load <file> read a program off the disk
run [words] start what was loaded, and tell it those words
<name> [words] load and start that program off the disk
<name> [words] look where you are and then in /Apps, and start that
cd [path] go to a directory, or to the root with nothing after it
delete <file> take it off the disk
rename <file> <to> call it something else
monitor look at memory, change it, and jump into it
+2 -7
View File
@@ -1,10 +1,7 @@
CosmOS
> Apps <dir>
Deep <dir>
Say.sbx 155
Say.sbx 52
rooted.txt 21
3 files, 2 directories
1 file, 1 directory
> loaded, starting at 4000
> it says: the shallow one
finished
@@ -26,10 +23,8 @@ finished
> that is a directory
> gone
> Apps <dir>
Deep <dir>
Say.sbx 155
rooted.txt 21
2 files, 2 directories
1 file, 1 directory
> halted
Execution halted.
[exit 0]
+2 -4
View File
@@ -1,17 +1,15 @@
CosmOS
> Folder <dir>
Inner <dir>
Edit.sbx 1996
1 file, 2 directories
1 file, 1 directory
> loaded, starting at 4000
> note.txt, 0 lines
> : : : > written, 67 bytes
> finished
> Folder <dir>
Inner <dir>
Edit.sbx 1996
note.txt 67
2 files, 2 directories
2 files, 1 directory
> halted
Execution halted.
[exit 0]
+20
View File
@@ -0,0 +1,20 @@
dir
cd /A
dir
Type notes.txt
cd /B
Type notes.txt
Say reached the one in Apps
cd /A
Say reached the one in A
cd ..
dir
cd /B
Type ../A/notes.txt
Wander /A
Type notes.txt
cd nosuchplace
cd notes.txt
cd /
dir
exit
+31
View File
@@ -219,6 +219,37 @@ awk 'BEGIN { for (i = 0; i < 30; i++) printf "line %02d: ABCDEFGHIJKLMNOPQRSTUVW
printf 'this is not a program' > rooted.txt
"$TOOL" put "$DISKS/tree.img" rooted.txt >/dev/null
# A disk for moving about on. Two directories hold a file of THE SAME NAME with different
# text in it, which is the fixture the working directory needs: "notes.txt" has to mean a
# different file from each of them, and the only way to see that it does is for the two to
# say different things.
#
# It is also what checks the remembered file is dropped when the machine moves. That cache
# is keyed on the path as somebody typed it, so "notes.txt" is the same key in both places
# and nothing about the entry it remembers looks wrong - it is the kind of stale that is
# believed rather than noticed.
#
# Type and Say go in /Apps, so that a program can be started from anywhere; hello goes in
# /A under the name Say.sbx, so that where you are is visibly tried before /Apps.
"$TOOL" format "$DISKS/cwd.img" 256 4 >/dev/null
"$TOOL" mkdir "$DISKS/cwd.img" /Apps >/dev/null
"$TOOL" mkdir "$DISKS/cwd.img" /A >/dev/null
"$TOOL" mkdir "$DISKS/cwd.img" /B >/dev/null
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \
"$ROOT/Programs/CosmOS/Apps/Type.asm" -o "$WORK/Type.sbx" >/dev/null
"$TOOL" put "$DISKS/cwd.img" "$WORK/Type.sbx" /Apps/Type.sbx >/dev/null
"$TOOL" put "$DISKS/cwd.img" "$WORK/Say.sbx" /Apps/Say.sbx >/dev/null
"$TOOL" put "$DISKS/cwd.img" "$WORK/treeHello.sbx" /A/Say.sbx >/dev/null
printf 'these are the notes in A\n' > notesA.txt
printf 'and these are the very different notes in B\n' > notesB.txt
"$TOOL" put "$DISKS/cwd.img" notesA.txt /A/notes.txt >/dev/null
"$TOOL" put "$DISKS/cwd.img" notesB.txt /B/notes.txt >/dev/null
# Wander is the only thing that can move the machine from inside a program, which makes it
# the only thing that can check the shell puts the working directory back afterwards.
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \
"$ROOT/Programs/CosmOS/Apps/Wander.asm" -o "$WORK/Wander.sbx" >/dev/null
"$TOOL" put "$DISKS/cwd.img" "$WORK/Wander.sbx" /Apps/Wander.sbx >/dev/null
# A disk for invoking a program by typing its name. Four files, each there to say one
# thing about how a typed word turns into a file name.
#
+20
View File
@@ -343,6 +343,25 @@ cosmosTree | CosmOS/Source/cosmos.asm | run | cosmosTre
# hold no blocks and must therefore be in nobody's way when a run of free ones is wanted.
# The listing afterwards says where it landed and how big it is.
cosmosTreeWrite | CosmOS/Source/cosmos.asm | run | cosmosTreeWrite.in | - | disks/treewrite.img
# The working directory. cd moves the machine, the prompt says where it is once that is
# not the root, and dir lists one directory rather than the whole disk.
#
# The two notes.txt say different things ON PURPOSE, and that is the whole test: reading
# "notes.txt" from /A and then from /B has to give two different files. If the remembered
# file were not dropped when the machine moves, the second read would hand back the first
# one - same key, same entry, nothing about it looking wrong.
#
# Then that a program is looked for where you are BEFORE /Apps: /A holds a Say.sbx that is
# really hello, so "Say" from /A prints the wrong thing on purpose, and "Say" from /B has
# to reach the real one in /Apps.
#
# And the two things only a program moving the machine itself can check. Wander calls
# osChangeDir, so after "Wander /A" from /B the prompt has to say /B again - the shell puts
# the working directory back the way it puts the Stack and the vector table back. Reading
# notes.txt straight afterwards has to give B's copy, which is the remembered file being
# dropped when the machine moved. Without Wander neither of those could be made to fail,
# because nothing else on the machine can move it.
cosmosCwd | CosmOS/Source/cosmos.asm | run | cosmosCwd.in | - | disks/cwd.img
# Typing a program's name starts it. The disk is built so that each line of the input asks
# a different question of the one rule: "Say hello there" is a bare name with an argument,
# "Say.sbx" is the same file spelled out in full, and "run once more" says the program that
@@ -381,6 +400,7 @@ app-Edit | CosmOS/Apps/Edit.asm | assemble | -
app-Files | CosmOS/Apps/Files.asm | assemble | - | -
app-Stream | CosmOS/Apps/Stream.asm | assemble | - | -
app-Type | CosmOS/Apps/Type.asm | assemble | - | -
app-Wander | CosmOS/Apps/Wander.asm | assemble | - | -
app-More | CosmOS/Apps/More.asm | assemble | - | -
# The assembler that runs on the machine, and its parts. Checked on their own so that a
# failure reads as "it does not assemble" rather than as a broken disk image.