Start a program by typing its name

A word the shell has no command for is now looked for on the disk as
"<name>.sbx", and if it is there it is loaded and started exactly as load and
run would do it. Whatever followed the word reaches the program through
osArgument by the same route as whatever follows run, so "Say hello there"
and "Type notes.txt" work without either program knowing how it was started.

load and run are unchanged and both stay. load is how the monitor puts an
arbitrary file in front of itself, which typing a name deliberately cannot do:
the extension is added rather than assumed, so "notes.txt" looks for
notes.txt.sbx and a text file is unreachable by name whatever is inside it.

Three things this had to get right:

The built-ins are tried first and always win. The search hangs off the end of
the dispatch chain, so a file called dir.sbx cannot become dir, and the
commands worth trusting when the disk is what you are doubting stay
trustworthy. The invoke disk carries a working dir.sbx so that this is checked
rather than asserted.

A file that is found but is broken says so. "not a program" and "I do not
know" are different answers, and giving the second about a file sitting on the
disk would send somebody looking in the wrong place. loadProgram therefore
hands back a status as well as a message, since only "no file of that name"
can fairly be reported as anything other than a fault.

doLoad became that subroutine rather than being copied. It ends in RET instead
of a jump to the prompt, and each way of failing sets its number and its text
together so a new one cannot leave half of the answer behind.

cosmosBreak moves because Break prints the pointers it was handed and those
are the shell's leftovers, which a CALL now puts back.

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 17:25:50 -04:00
co-authored by Claude Opus 5
parent dbe58db660
commit 2b0aeeefd4
8 changed files with 358 additions and 35 deletions
+44 -3
View File
@@ -20,6 +20,8 @@ for itself.
- Loadable Applications: Validate SBEX files, copy their Program and Data segments into
the addresses for which they were assembled, and start them at their declared entry
point.
- Invocation By Name: A word the shell has no command for is looked for on the disk as
`<name>.sbx`, and loaded and started if it is there. Built-in commands are tried first.
- Resident Services: Applications can print strings and numbers, read lines, receive their
command arguments, read and write files, and return to the shell through named software
interrupts.
@@ -83,6 +85,7 @@ CosmOS currently provides these built-in commands:
| `dir` | List the files on the mounted disk and their sizes. |
| `load <file>` | Read and validate an SBEX application, then place its code and data where its header requests. |
| `run [words]` | Start the loaded application and make the rest of the line available to it as an argument. |
| `<name> [words]` | Any word the shell does not recognise is looked for on the disk as `<name>.sbx`, and loaded and started if it is there. |
| `delete <file>` | Remove a file from the filesystem and release its blocks. |
| `rename <file> <to>` | Give a file a different name without moving its contents. |
| `monitor` | Enter monitor mode, in which the prompt becomes `*` and the commands below are also available. |
@@ -115,9 +118,47 @@ For example:
> run
```
Loading and running are separate operations for now. A loaded program may be run again
without being read from disk again, which is useful both as a monitor facility and as a
test that CosmOS correctly restores its Stack and vector table after every run.
Or, equivalently:
```text
> Snake
```
Loading and running remain separate operations, and both of them remain. A loaded program
may be run again without being read from disk again, which is useful both as a monitor
facility and as a test that CosmOS correctly restores its Stack and vector table after
every run; and `load` is how the monitor puts an arbitrary file in front of itself, which
is a thing typing a name deliberately cannot do.
### Starting An Application By Name:
A word the shell has no command for is not immediately an error. Before saying so, the
shell adds `.sbx` to it unless it is already there, looks for a file of that name, and if
one is there loads it and starts it exactly as `load` and `run` would. Whatever followed
the word reaches the program through `osArgument`, the same way and by the same route as
whatever follows `run`.
Three properties of this are deliberate:
**Built-in commands are tried first and always win.** The search happens only after the
whole dispatch chain has failed to match, so a file named `dir.sbx` cannot become `dir`.
The commands that are worth trusting when the disk is the thing being doubted stay
trustworthy.
**The extension is what makes a file reachable by name.** Typing `notes` looks for
`notes.sbx`, and typing `notes.txt` looks for `notes.txt.sbx`. A text file therefore
cannot be started by typing what it is called, whatever happens to be inside it. Only
`load` reaches a file by its literal name.
**A file that is found but is broken says so.** If `notes.sbx` exists and is not an SBEX
program, typing `notes` reports `not a program` rather than `I do not know: notes`.
Reporting an unknown command about a file that is sitting on the disk would send somebody
looking in the wrong place.
Names are matched exactly, including case, because every other name on the filesystem is.
A directory entry holds twenty two characters and four are spoken for by the extension, so
eighteen is the longest a bare name can be; a longer word is reported as unknown, which is
the truth, since no file of that name can exist.
CosmOS also boots without a disk. It reports that no filesystem was found, leaves the
shell and memory monitor available, and refuses commands that require a mounted disk
+237 -30
View File
@@ -185,8 +185,39 @@ promptSay:
BRQ doAssemble
promptUnknown:
; Nothing matched. Saying which word was not understood is worth the four instructions:
; it tells somebody who mistyped what they actually typed.
; Nothing built in matched, so the disk is asked before anybody is told they are wrong. A
; word this shell does not know is very often the name of a program sitting right there,
; and looking costs a walk of the directory.
;
; THE BUILT-IN COMMANDS ARE TRIED FIRST AND ALWAYS WIN. Nothing that turns up on a disk
; can quietly become "dir" or "exit", which is what makes those two worth trusting at the
; moment the disk is the thing being doubted.
SETD.0 DiskReady
LDA.0
BRA promptSayUnknown ; No filesystem, so there is nothing to look through.
CALL nameProgram
SETD.0 NameOk
LDA.0
BRA promptSayUnknown ; Too long to be a file name, so it is not the name of one.
CALL loadProgram
SETD.0 LoadStatus
LDA.0
BRA runLoaded ; It loaded, and the machine is its now.
; 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 reported in those words. Anything else means a file of that name IS
; there and something is wrong with it, and answering "I do not know: Snake" about a
; Snake.sbx that is sitting on the disk would send somebody looking in the wrong place.
INIB 0d2
CCF
SUB
BNQ loadFailed
promptSayUnknown:
; Saying which word was not understood is worth the four instructions: it tells somebody
; who mistyped what they actually typed.
SETD.0 Unknown
CALL printString
SETD.0 CommandLine
@@ -329,6 +360,93 @@ widthDone:
ADD
RET
; ---- Making a file name out of a typed word ----
;
; Turns what was typed into the name of a file to go and look for. ".sbx" goes on the end
; unless it is already there, so that "Snake" and "Snake.sbx" both find the same file.
;
; THE EXTENSION IS WHAT MAKES A FILE REACHABLE BY NAME. Typing "notes" looks for notes.sbx
; and typing "notes.txt" looks for notes.txt.sbx, so a text file cannot be started by
; typing what it is called, whatever is inside it. Only load reaches a file by its whole
; name, which is why the monitor can still put any file at all in front of itself.
;
; NameOk is one if there is a name in ProgramName and zero if the word could not be made
; into one. A directory entry holds twenty two characters and four of those are spoken for
; by the extension, so eighteen is as long as a bare name can be. Being refused here reads
; as an unknown command, which is the truth: no file of that name can exist.
nameProgram:
RSTA
SETD.0 NameOk
STA.0 ; Not a name until it turns out to be one.
SETD.0 CommandLine
SETD.1 ProgramName
INIB 0d22 ; How much of the twenty two is left.
nameCopy:
LDA.0
BRA nameCopied
STA.1
INCD.0
INCD.1
DECB
BNB nameCopy
RET ; Twenty two characters and still going. Not a name.
nameCopied:
; DP1 is on the byte after the word, which is where an extension would go, and B is what
; is left. B is written down before anything else wants the registers.
RSTA
STA.1
SETD.0 NameLeft
STB.0
; Only a word of four characters or more can already end in ".sbx". Stepping back four to
; look at a shorter one would read whatever happens to sit in front of the buffer.
INIA 0d18
CCF
SUB
BRC nameAppend
PSHD.1
POPD.2
DPDN.2 0d4
SETD.0 SbxSuffix
nameSuffixSame:
LDA.0
BRA nameMade ; The suffix ran out with all of it matched.
LDB.2
CCF
SUB
BNQ nameAppend
INCD.0
INCD.2
BRI nameSuffixSame
nameAppend:
SETD.0 NameLeft
LDA.0
INIB 0d4
CCF
SUB
BRC nameDone ; Not four characters of room, so there is no name to be made.
; The suffix carries its own zero, so copying it to the end of the word ends the word.
SETD.0 SbxSuffix
nameSuffixCopy:
LDA.0
STA.1
BRA nameMade
INCD.0
INCD.1
BRI nameSuffixCopy
nameMade:
INIA 0x01
SETD.0 NameOk
STA.0
nameDone:
RET
; ---- load ----
;
; Reads a program off the disk and puts it where its header asks to go. Nothing relocates
@@ -337,17 +455,78 @@ widthDone:
;
; The whole file is staged at 0x8000 first and then blitted into place, because where the
; pieces belong is not known until the header has been read, and the header is in the file.
;
; The command is a thin thing over loadProgram, which is a subroutine because typing a
; program's name loads it too and neither caller should own the loading. What differs
; between them is only what a fault means: "load Snake.sbx" on a disk without it has been
; given a wrong name, and "Snake" on the same disk has typed a word this shell does not
; know.
doLoad:
SETD.0 DiskReady
LDA.0
BRA loadNoDisk
SETD.1 TextRest
LDD.0.1
LDA.0
BRA loadNothingNamed
; The name exactly as typed. load is how a file is reached by its whole name, so nothing
; is added to it and nothing is assumed about what it ends in.
SETD.1 ProgramName
INIB 0d23
CALL copyText
CALL loadProgram
SETD.0 LoadStatus
LDA.0
BNA loadFailed
SETD.0 LoadedText
CALL printString
SETD.0 LoadedEntry
CALL printWordHex
CALL newLine
BRI prompt
loadFailed:
SETD.1 LoadMessage
LDD.0.1
CALL printString
CALL newLine
BRI prompt
loadNothingNamed:
SETD.0 LoadWhat
CALL printString
CALL newLine
BRI prompt
; ---- loadProgram ----
;
; The name is in ProgramName. LoadStatus says what happened and LoadMessage names the text
; for it, because a subroutine cannot hand anything back in a register that a RET puts
; back, and here there are two things to hand back:
;
; 0 loaded, and LoadedEntry says where it starts
; 1 no filesystem on the disk
; 2 no file of that name
; 3 the disk would not read it
; 4 it is not a program
; 5 a version of the format this loader does not know
; 6 more vectors than there is room to keep
;
; Two is the one worth telling apart from the others. It is the only outcome where nothing
; was wrong with the disk or with a file, and so the only one a caller can fairly report as
; something other than a fault.
loadProgram:
RSTA
SETD.0 LoadStatus
STA.0
SETD.0 DiskReady
LDA.0
BRA loadNoDisk
SETD.0 ProgramName
CALL sbfsFind
BNQ loadMissing
@@ -532,38 +711,40 @@ loadVectorsCopied:
INIA 0x01
SETD.0 LoadedOk
STA.0
RET ; LoadStatus is still the zero it was started at.
SETD.0 LoadedText
CALL printString
SETD.0 LoadedEntry
CALL printWordHex
CALL newLine
BRI prompt
; Which of the seven happened, and where the words for it are. The two are set together so
; that no caller has to know both, and so that adding a way to fail cannot leave one of
; them behind.
loadNoDisk:
INIA 0d1
SETD.0 NoDisk
BRI loadComplain
loadNothingNamed:
SETD.0 LoadWhat
BRI loadComplain
loadTooManyVectors:
SETD.0 TooManyVectors
BRI loadComplain
BRI loadRefuse
loadMissing:
INIA 0d2
SETD.0 NoSuchFile
BRI loadComplain
BRI loadRefuse
loadUnreadable:
INIA 0d3
SETD.0 Unreadable
BRI loadComplain
BRI loadRefuse
loadNotProgram:
INIA 0d4
SETD.0 NotProgram
BRI loadComplain
BRI loadRefuse
loadWrongVersion:
INIA 0d5
SETD.0 WrongVersion
loadComplain:
CALL printString
CALL newLine
BRI prompt
BRI loadRefuse
loadTooManyVectors:
INIA 0d6
SETD.0 TooManyVectors
loadRefuse:
SETD.1 LoadMessage
STD.0.1
SETD.1 LoadStatus
STA.1
RET
; ---- delete and rename ----
;
@@ -658,6 +839,11 @@ doRun:
LDA.0
BRA runNothing
; Where typing a program's name arrives, having loaded it on the way. The argument comes
; out of TextRest either way: after "run" that is what followed the word, and after a
; program's own name it is what followed the name, which is the same thing meaning the
; same thing.
runLoaded:
MVSD.0
SETD.1 SystemStack
STD.0.1
@@ -2538,10 +2724,11 @@ HelpText:
"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
delete <file> take it off the disk
rename <file> <to> call it something else"
<name> [words] load and start that program off the disk"
HelpMoreText:
"monitor look at memory, change it, and jump into 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
help this
exit stop, or leave the monitor if you are in it"
@@ -2556,6 +2743,11 @@ DataWord:
ExecMagic:
"SBEX"
; What every program on the disk is called, and the four characters that make one
; reachable by typing its name.
SbxSuffix:
".sbx"
LoadWhat:
"load what?"
NoSuchFile:
@@ -2673,6 +2865,21 @@ DiskReady:
0x00
LoadedOk:
0x00
; What loadProgram found, and the words for it. See loadProgram for what the numbers mean.
LoadStatus:
0x00
LoadMessage:
0x00 0x00
; The name of the file to load, which load copies out of the line as typed and a typed
; program name is built into. Twenty two characters and the zero that ends them.
ProgramName:
#Reserve 0d23
NameOk:
0x00
NameLeft:
0x00
LoadedEntry:
0x00 0x00
LoadCount: