diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index ef05f8e..57f3cc7 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -439,7 +439,7 @@ CosmOS divides the two SplitBit address spaces by convention: | Memory | CosmOS | Loaded application | | -- | -- | -- | | Program Memory | `0x0000` through `0x3FFF` | `0x4000` and above | -| Data Memory | `0x0000` through `0x3FFF` | `0x2000` and above | +| Data Memory | `0x0000` through `0x1FFF` | `0x2000` and above | Both of CosmOS's halves were doubled once it outgrew the first ones. **The division is a convention and nothing enforced it**, so CosmOS quietly grew past `0x1FFF` and the next @@ -448,6 +448,12 @@ fails later, in whatever part of the shell the program happened to cover. `make measures both segments against the numbers in this table, so the table is checked rather than merely written down. +**The table is checked against itself as well.** The first version of that check read only +the CosmOS column, and so it passed a table whose Data row gave the system `0x3FFF` and an +application `0x2000` - two columns that cannot both be true, sitting next to each other. +Measuring one number against the code and never against the number beside it is how a +specification contradicts itself in public. + Applications state their actual Program and Data addresses with `#Base`. The SplitBit assembler then writes an SBEX loadable image containing those addresses, the entry point, the segment lengths, and any vectors the application needs. CosmOS does not relocate @@ -475,7 +481,7 @@ A minimal CosmOS application therefore looks like this: #Include services.asm #Program - #Base 0x2000 + #Base 0x4000 start: SETD.0 Message @@ -483,7 +489,7 @@ start: SWI osExit #Data - #Base 0x1000 + #Base 0x2000 Message: "Hello from CosmOS." diff --git a/Programs/CosmOS/Source/cosmos.asm b/Programs/CosmOS/Source/cosmos.asm index 6db1059..252e063 100644 --- a/Programs/CosmOS/Source/cosmos.asm +++ b/Programs/CosmOS/Source/cosmos.asm @@ -10,10 +10,10 @@ ; The system keeps to the bottom of both memories, and everything above is for whatever ; it is running: ; -; Program Memory 0x0000 - 0x1FFF the system -; 0x2000 - a loaded program's code -; Data Memory 0x0000 - 0x0FFF the system -; 0x1000 - a loaded program's data +; Program Memory 0x0000 - 0x3FFF the system +; 0x4000 - a loaded program's code +; Data Memory 0x0000 - 0x1FFF the system +; 0x2000 - a loaded program's data ; ; Nothing enforces that. Nothing can: the fence guards a range, and this is a convention ; about which range belongs to whom rather than a rule about what may be touched. The @@ -25,14 +25,20 @@ ; ; ---- What it can do ---- ; -; dir List what is on the disk. +; dir List what is in the current directory. +; cd Go somewhere else, or to the root when told nothing. +; mkdir Make a directory. rmdir Remove an empty one. +; delete Remove a file. rename Give one another name. ; load Read a program off the disk and put it where it asks to go. ; run Start the program that was loaded. ; help Say what these are. -; exit Stop. +; exit Stop, or leave the monitor if that is where you are. ; -; dump is next. The dispatch below is a chain of comparisons, which is the right shape for -; five commands and the wrong shape for twenty; when it grows, the table that +; Anything else that is not one of those is looked for as a program and run if it is +; found, so most of what the machine does is not in this list at all. +; +; The dispatch below is a chain of comparisons, which is the right shape for five commands +; and the wrong shape for twenty; it is eleven now, and when it grows the table that ; dispatchTest.asm demonstrates is where it should go. ; ; Written by Anachronaut @@ -65,8 +71,6 @@ bootNoDisk: CALL printString CALL newLine -; ---- The loop ---- - ; ---- The loop ---- ; ; The shell has two modes and one prompt that says which. Ordinary mode runs programs; diff --git a/Tests/docs.sh b/Tests/docs.sh index 9f23efe..e0180ad 100755 --- a/Tests/docs.sh +++ b/Tests/docs.sh @@ -467,9 +467,12 @@ for name, text in (("the Programming Manual", pm), ("the Assembler Manual", am), # That is why this reads the numbers out of the table rather than being told them: the # table is the specification, and a table nothing checks is the thing that goes stale. readme = open("Programs/CosmOS/README.md").read() -row = re.compile(r"\|\s*(Program|Data) Memory\s*\|\s*`0x0000` through `0x([0-9A-Fa-f]{4})`") -limits = {kind: int(top, 16) + 1 for kind, top in row.findall(readme)} -if len(limits) != 2: +row = re.compile(r"\|\s*(Program|Data) Memory\s*\|\s*`0x0000` through `0x([0-9A-Fa-f]{4})`" + r"\s*\|\s*`0x([0-9A-Fa-f]{4})` and above\s*\|") +table = {kind: (int(top, 16) + 1, int(base, 16)) for kind, top, base in row.findall(readme)} +limits = {kind: room for kind, (room, base) in table.items()} +bases = {kind: base for kind, (room, base) in table.items()} +if len(table) != 2: problems.append("the CosmOS README no longer states a memory map this can check") else: built = subprocess.run(["./Assembler", "-I", "Programs/CosmOS/Source", @@ -487,6 +490,55 @@ else: " reaches into the %d bytes an application is loaded at, and loading" " one will overwrite it" % (kind, used, room, used - room)) + # ---- And the table against itself ---- + # + # The check above reads the CosmOS column and never the one beside it, so it passed a + # Data row that gave the system 0x0000-0x3FFF and an application 0x2000 and above - + # two halves of one sentence contradicting each other in print. A number checked + # against the code and not against the number next to it is still unchecked. + for kind in ("Program", "Data"): + room, base = table[kind] + if base != room: + problems.append( + "the CosmOS README gives the system %s Memory up to 0x%04X and puts an" + " application at 0x%04X - the two columns of that row disagree" + % (kind, room - 1, base)) + + # ---- And the example under it ---- + # + # The minimal application in the same section is what somebody copies, so it is the + # part of the map most worth being right. It went stale across the doubling while the + # table above it was corrected. + example = re.search(r"```asm\n(.*?)```", readme, re.S) + if not example: + problems.append("the CosmOS README no longer shows a minimal application") + else: + shown = dict(zip(("Program", "Data"), + re.findall(r"#Base 0x([0-9A-Fa-f]{4})", example.group(1)))) + for kind in ("Program", "Data"): + if kind not in shown: + problems.append("the minimal CosmOS application shows no %s #Base" % kind) + elif int(shown[kind], 16) != bases[kind]: + problems.append( + "the minimal CosmOS application is based at 0x%s in %s Memory and the" + " map above it says 0x%04X" % (shown[kind].upper(), kind, bases[kind])) + + # ---- And the copy in the source ---- + # + # cosmos.asm opens with the same map in its own words, because somebody reading the + # system reads that before they read the README. Three copies of one fact, so all + # three are compared. + header = open("Programs/CosmOS/Source/cosmos.asm").read()[:4096] + stated = dict(re.findall( + r"(Program|Data) Memory\s+0x0000 - 0x([0-9A-Fa-f]{4})\s+the system", header)) + for kind in ("Program", "Data"): + if kind not in stated: + problems.append("cosmos.asm no longer opens with a %s Memory map" % kind) + elif int(stated[kind], 16) + 1 != limits[kind]: + problems.append( + "cosmos.asm says the system keeps below 0x%s in %s Memory and the CosmOS" + " README says 0x%04X" % (stated[kind].upper(), kind, limits[kind] - 1)) + if problems: print("The manuals and the code disagree:") for p in problems: