563bc20a75980dbf641d72b39b97d47ebc9c4183
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
563bc20a75 |
Tab lists its matches in columns, and says what it can in colour
Tab's listing was six instructions: print the name, print two spaces. It was the crown of this shell for a while and looks plain next to ls. Columns cost NOTHING EXTRA. Tab already walks its candidates twice - once to find the answer, once to show the matches, and tabRunSources exists because those are the same walk asked two questions. The longest match is counted on the first, which already visits every one of them, so the listing needs no walk of its own to know how wide a column should be. Padding goes before a name rather than after, so a row ends on a name. Directories are blue and the shell's own commands are green. Both are free: Tab appends the separator itself, and a built-in is not a file at all - which is also the only way anybody could know it will run. A FILE IS NOT COLOURED, and that is a decision. Whether a file will run is a read of its first block, which is what ls does and what makes ls cost twice what it otherwise would. Here it would be worse than slow: candidates are offered from inside a directory walk, and looking a file up would overwrite the very fields holding the walk's own position. Doing it safely means holding every candidate name in memory, which is a buffer the shell would carry whether anybody pressed Tab or not - and ls is one keystroke away. A Tab press already costs about 200,000 cycles, so the read was not the objection. ---- And the machine could no longer build itself ---- Found by make test, not by reading. The native assembler ran out of room for label names on cosmos.asm: 16,758 bytes against 16,384. The index was 1,341 of 1,536 in the same breath. Which is scratch.asm's own warning happening a second time - "two ceilings a hundred bytes apart look like one ceiling until the first is lifted" - so both were raised, out of the seventeen kilobyte page that file deliberately left unclaimed against exactly this. Names to 26,624 and the index to 2,048, both left about a third clear, with 1,792 bytes still unclaimed for the same reason. A name is thirteen bytes on average and an index entry is four, so the arena will always be the one that speaks first. That is now written down where the two numbers are. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW |
||
|
|
afda6ca83b |
Finishing says nothing, which is what finishing looks like
The shell printed "finished" every time a program gave the machine back. That made sense when starting a program and getting the machine back was most of what the machine did and the prompt was the only other thing on the screen. Under a listing, between two commands, it is noise. And it was printed whatever the program made of it, so a program that had just explained what went wrong was answered with the word "finished" - "there is no such directory: nowhere" and then, immediately, "finished". The prompt coming back is what says a program is over. It is the same argument osLastStatus is made of: a program that failed has already said so in words, and anything the shell adds beside that is the shell talking over it. A program that FAULTED still says so, because the fault screen printed in red above and a program that is gone should not look like one that ended. A NEWLINE ONLY IF ONE IS WANTED. A program that stopped part way along a line would leave the prompt sitting in the middle of its last output, which the word used to prevent by accident. The console knows which column the cursor is in, so the shell asks - where a blank line printed every time would be right about half of the time, and an empty directory listed with ls would be followed by a blank line for no reason. Fifty nine recorded sessions lose the word. Two of them move a cursor up a row with it, which is the same fact seen from the screen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW |
||
|
|
323d7a0330 |
A listing says what is left of the disk, and what will fit
dir said what was there and nothing about what was left. SplitDisk has printed the free figure since it was written, so the machine's own listing was the poorer of the two implementations at describing the same disk. 43 files, 3 directories 46 of 64 entries, 1893 blocks free Entries first, because they are the ceiling nobody notices until they hit it: a disk of small files runs out of directory slots long before it runs out of blocks. COUNTED RATHER THAN ASKED. The superblock keeps a free count and this file calls it "a note rather than the truth" in three places. sbfsSpace reads the whole directory table instead, which costs a read per directory block and is the answer rather than a guess. SplitDisk goes on reading the note and saying when it is stale, which is the right place for that check - the host tool is what you audit a disk with. ---- And it is a fact about the disk, not about where you are ---- The first version added the blocks up as the LISTING walked past them, which cost no extra read and was wrong: that walk stops only on entries in the working directory, so the same disk came out as 1,996 blocks free from the root and 2,025 from /Apps. Comparing against SplitDisk is what said so, which is what having two implementations is for. ---- The longest run, which is what decides whether a file fits ---- 4 of 16 entries, 37 blocks free the longest run is 25 Files are laid down contiguously, so the free total does not say whether a file will fit. Both implementations learn it, from one specification. Said only when it differs from the free total. Deleting is what fragments a contiguous store, and a disk that has only been appended to has one gap at the end - so on a healthy disk this is silent, and a line that appears only when something is wrong is a line somebody reads. There is no sort on this machine and the entries are in no order, so a candidate walks the disk: each pass finds the used extent nearest at or after it, and anything the candidate stands inside pushes it to the far end and starts the pass again. The same trick allocating uses. So it costs a pass per gap rather than per file - nearly nothing on a disk with one gap, more the more fragmented the disk is, which is the right way round. holes.img is six files with the second and fourth deleted, because no other disk here can show any of this: none of them has ever had anything deleted from it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW |
||
|
|
736037462e |
The shell edits the line it is given
Three different things used to do this job, and which one you got depended on where the machine was running. On a terminal the host held the line and did the echoing and the backspacing; behind a window the console's own gatherer did it; from a file nothing did it at all. One job, three implementations, none of them in the system - which is why there was no way to move about in a line and nowhere for a history to live. So editLine does it. Key mode while a line is being read and line mode straight after, so nothing else in the system and no program calling osReadLine notices anything changed. Left and Right, Home and End, Backspace for the character before the cursor and Delete for the one under it, and anything typed goes in where the cursor is with the rest of the line moving along. Ctrl-D means the end of input again, on an empty line, because that was a thing the terminal did while it was holding the line and it is not holding it now. Same trade as the echoing. MOST KEYSTROKES DRAW NOTHING BUT THEMSELVES. A character typed at the end of a line needs no cursor moved: printing it is the whole change, and a backspace there is three ordinary bytes. That matters beyond speed - moving the cursor by hand is what a terminal is TOLD about, in an escape sequence, so redrawing on every keypress would fill every recorded transcript in this suite with them. The line is only reprinted when something happened in the middle of it. Where the line STARTS is worked out backwards from where printing ended, rather than trusted from what was remembered. That is what makes it survive the screen scrolling: a line printed on the bottom row moves everything up by one, and a remembered row would be one too low from then on. The command line holds 127 characters, up from 63. The limit started to be felt the moment a line could be moved about in. 58 recordings changed, and every one of them by the echo. THE PROOF IS NOT A HEURISTIC: a CosmOS built with the echo silenced reproduces 187 of the 188 recordings byte for byte. The one exception is cosmosTyped, the backspace test, where the rub-out marks now come from the shell instead of from the console's gatherer - same marks, different author. cosmosEditKeys is the new test, and every line in it is typed wrong and then corrected with a different key. Its last line is eighty six characters at a prompt in column two on an eighty column screen, so the line runs onto the row below and the shell has to find the start of something it can no longer see; breaking either half of that arithmetic fails it. Also: agree.sh looked for "> the same", anchored to a prompt that no longer precedes what a command prints. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW |
||
|
|
546f336823 |
Configuration files, and boot.cfg as the first of them
One setting to a line: a key, a space, the rest of the line is the value. A semicolon starts a comment. The format was noticed rather than designed - textSplit already cuts the first word off a line and leaves the rest, and textSame already insists two strings end together, so reading a setting is those two routines and a loop. It is also what the shell reads, which makes a configuration line a command line the machine reads instead of a person typing one. The format was chosen by asking what the BOOT LOADER could manage, because it is the worst case in every direction: a few kilobytes, no operating system to report to, and if it fails the machine does not start. Two formats would be worse than one and the loader cannot have the richer one. CONFIGURATION IS ADVICE. A missing file, a missing key, an unusable value, a line too long to read: all of them mean use the default and none is a failure. BUT QUIET IS NOT SILENT - a setting somebody meant, which did not take effect, says so. That was the user's addition and it is the better rule: the default alone leaves the only symptom being that the machine did not do what somebody asked. So two routines. cfgGet reads and says nothing, because reading three settings should not report one bad line three times. cfgCheck reads the file once and reports, and is handed the caller's list of keys - whether a key means anything is the only part of this a shared reader cannot judge. /System/Boot/ holds the boot files, and stage two reads boot.cfg for what to start, with a fallback to try if it does not work and a name compiled in for when the file says nothing. THE TEST FOUND A REAL BUG, and it is the interaction I would not have thought to look for. First-match-wins met an empty value: a file with system system /System/Boot/bare.bin matched the first line, handed back nothing, and the machine tried to start a file with no name while a good setting sat underneath. An unusable value is an absent one - which is what "configuration is advice" says, and this is where it earns its keep. cfgBare starts an image with no operating system in it at all, which is what loading an ordinary boot image buys: a program wanting the whole machine is a file like any other, chosen the same way the system is. Three disks differing ONLY in boot.cfg, so each is a test of the file rather than of the machinery under it. |