The shell's own words, as a table and not just a chain

The dispatch is a run of "is the line this name" comparisons. That is fine to
execute and impossible to WALK, and completing a half typed command needs to
walk them - so the names have to be data as well as code.

They nearly were already: DirName through ExitName were fourteen zero
terminated strings sitting back to back, which is a table by accident of layout.
This makes it deliberate. MonitorName joins them, the run is labelled, and a
count goes underneath because a run of strings does not say where it stops.

WHAT MAKES IT A TABLE IS THE ZEROES. Each name ends in one, so the next begins
after it: no pointers, no lengths, and adding a command costs a line.

Tests/docs.sh reads both the dispatch and the run and compares them, because the
two can disagree and every way they do is quiet. A command added to the dispatch
and not to the run simply never completes, which nobody would think to check by
hand. Something put BETWEEN the strings is worse: the walk ends there and takes
every command after it, and the machine goes on working perfectly except that
Tab knows about six things instead of fifteen.

All three break that way and say something useful. Putting one byte in the
middle of the run reports that it holds ten names against the fifteen claimed,
which points at roughly where.

Groundwork for Tab completion. Nothing uses it yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-09-01 17:21:30 -04:00
co-authored by Claude Opus 5
parent b04e4b7d1c
commit 749fef8ce2
2 changed files with 61 additions and 2 deletions
+17 -2
View File
@@ -5568,8 +5568,6 @@ MonitorPrompt:
UnknownText:
" ?
"
MonitorName:
"monitor"
MonitorHelp:
"x examine, d disassemble, a assemble, s set, b bank, g go, exit leaves"
ExamineName:
@@ -5604,6 +5602,17 @@ NoSuchOp:
"no such instruction"
NeedsValue:
"that one needs a value after it"
; ---- The shell's own words, packed ----
;
; Fifteen names with nothing between them, which is a TABLE and not an accident of layout.
; Each is a string ending in a zero, so the next one begins after that zero and walking them
; needs no pointers and no lengths - which is what makes completing a half typed command
; possible at all, since the dispatch below is a chain of comparisons and cannot be walked.
;
; NOTHING MAY BE PUT BETWEEN THEM. Tests/docs.sh checks that this run holds exactly the names
; the dispatch tests and that the count below agrees, so a command added without a name here
; is caught rather than silently left out of what Tab knows about.
ShellNames:
DirName:
"dir"
DoName:
@@ -5632,6 +5641,12 @@ HelpName:
"help"
ExitName:
"exit"
MonitorName:
"monitor"
; How many of them, since a run of strings does not say where it stops.
ShellNameCount:
0d15
; Where the machine is, written out for the prompt, and where in the buffer it begins.
; Built from the end backwards, so it starts somewhere in the middle.
+44
View File
@@ -456,6 +456,50 @@ for name, text in (("the Programming Manual", pm), ("the Assembler Manual", am),
problems.append("%s still says \"%s\", and it has not been true since"
" directories arrived" % (name, claim))
# ---- The shell's words are a table as well as a chain of comparisons ----
#
# The dispatch is a run of "is the line this name" tests, which is fine to execute and
# impossible to WALK - so completing a half typed command needs the names as data too, and
# they are: fifteen strings packed end to end from ShellNames, each ending in the zero that
# says where the next begins.
#
# THE TWO CAN DISAGREE AND THE WAY THEY DO IS QUIET. A command added to the dispatch and not
# to the run simply never completes, which nobody would think to test by hand; something put
# BETWEEN the strings ends the walk early and takes the rest of the commands with it. So this
# reads both and compares them, and reads the count as well, because a run of strings does
# not say where it stops.
cosmosSource = open("Programs/CosmOS/Source/cosmos.asm").read()
# The dispatch, up to the point where the monitor's single letters begin - those are one
# character each and there is nothing to complete about them.
dispatchEnd = cosmosSource.find("SETD.0 Mode")
dispatched = re.findall(r"SETD\.1 (\w+Name)\b", cosmosSource[:dispatchEnd])
# The run, which ends at the first thing that is not a label and a string.
runAt = cosmosSource.find("ShellNames:")
packed = []
if runAt < 0:
problems.append("cosmos.asm has no ShellNames run for the shell's own words")
else:
lines = cosmosSource[runAt:].split("\n")[1:]
while len(lines) >= 2 and re.fullmatch(r"(\w+Name):", lines[0]) \
and re.fullmatch(r'"[^"]*"', lines[1]):
packed.append(lines[0][:-1])
lines = lines[2:]
stated = re.search(r"ShellNameCount:\s*\n\s*0d(\d+)", cosmosSource)
if not stated:
problems.append("cosmos.asm no longer says how many shell names there are")
elif int(stated.group(1)) != len(packed):
problems.append("cosmos.asm says there are %s shell names and the run holds %d"
% (stated.group(1), len(packed)))
if runAt >= 0 and sorted(packed) != sorted(dispatched):
missing = sorted(set(dispatched) - set(packed))
extra = sorted(set(packed) - set(dispatched))
problems.append("the shell's dispatch and its packed names disagree:%s%s"
% ("".join(" %s is dispatched and not in the run;" % n for n in missing),
"".join(" %s is in the run and not dispatched;" % n for n in extra)))
# ---- CosmOS fits in the half of the machine it says it does ----
#
# The memory map in the CosmOS README is a CONVENTION. Nothing in the assembler, the