ls, a listing laid out to be read
> ls Copy.sbx Say.sbx Where.sbx Walk.sbx ls.sbx Lander where.sh dir says what is there one line each, with sizes and a tally of the disk. This says the names in columns and nothing else, which is what you want ninety times in a hundred. Two programs rather than one with a switch, because they answer different questions and neither answer is a worse version of the other. AND THIS IS A PROGRAM WHERE DIR IS BUILT IN, which is the difference that matters when the disk is what you are doubting: dir is already in memory and this has to be loaded off the disk it is about to list. The daily driver and the diagnostic. Two passes over the directory and no buffer at all. Columns need the longest name before the first line can be printed, which usually means holding every name - twenty four bytes each against a format that allows 1,024 entries. Walking twice costs a read of each directory block, into a buffer that is already there. Across and not down. Real listings go down the columns so that names next to each other alphabetically are next to each other on the screen, and that reason depends on sorting - which nothing here does. With the order arbitrary, down-and-across buys nothing and costs a division. Directories in blue and wearing a separator, unfinished saves in red, everything else plain: whether a file is runnable cannot be known without opening it, and opening every file in a directory to colour a listing is a price nobody agreed to pay. The colour reaches a terminal as well as the screen, so it is one mechanism and not two. ---- Three bugs, and one of them is this machine's oldest trap ---- nameLength answered in A, and a RET puts A back the way the caller had it. So it answered with nothing, and every gap between the columns came out the same width because the padding was subtracting whatever A happened to hold. Q is the ALU's output and nothing puts it back, which is why every answer here comes home in it. The padding subtracted the other way round - the name from the cell - which borrowed on every name that was not the longest, so all of them took the "wider than its cell" path and the listing came out separated by one space. And padding after a name left trailing spaces on every line that did not fill its last column. It goes before the next name now, so spaces only ever fall between two things and a line ends on a name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
323d7a0330
commit
a13bbeb4db
@@ -693,6 +693,43 @@ compares the images byte for byte. Every field one writes and the other only rea
|
||||
checked there and nowhere else: which entry a thing lands in, which block, what a
|
||||
directory's unused fields hold, the version, the free count.
|
||||
|
||||
### Two Listings:
|
||||
|
||||
`dir` says what is there one line each, with sizes and a tally of the disk. `ls` says the
|
||||
names in columns and nothing else, which is what you want ninety times in a hundred.
|
||||
|
||||
```text
|
||||
> ls
|
||||
Copy.sbx Say.sbx Where.sbx Walk.sbx ls.sbx Lander where.sh
|
||||
```
|
||||
|
||||
They are two programs rather than one with a switch, because they answer different questions
|
||||
and neither answer is a worse version of the other.
|
||||
|
||||
**And `ls` is a program where `dir` is built into the shell.** That is the difference that
|
||||
matters when the disk is the thing being doubted: `dir` is already in memory, and `ls` has to
|
||||
be loaded off the disk it is about to list. The daily driver and the diagnostic, and it is
|
||||
worth having both.
|
||||
|
||||
**Two passes over the directory, and no buffer.** Columns need the longest name before the
|
||||
first line can be printed, which usually means holding every name in memory - twenty four
|
||||
bytes each, against a format that allows 1,024 entries on a disk. So the directory is walked
|
||||
twice instead: once to measure, once to print. That is also the first thing `osDirFirst` made
|
||||
possible which `dir` could not already do.
|
||||
|
||||
**Across, and not down.** Real listings go down the columns so that names next to each other
|
||||
alphabetically are next to each other on the screen - and that is a reason which depends on
|
||||
sorting. Nothing here sorts; entries come back in the order the directory holds them. With the
|
||||
order arbitrary, down-and-across buys nothing and costs a division on a machine that cannot
|
||||
divide. If sorting ever arrives, that is the moment to change it.
|
||||
|
||||
**The width comes from the screen**, port `0x32`, which is 80 while CosmOS is running because
|
||||
CosmOS asks for the wide mode as it starts.
|
||||
|
||||
**Padding goes before a name rather than after it**, so that a line ends on the last character
|
||||
of a name. Trailing spaces on a line are the kind of thing nobody sees until something else
|
||||
reads the output.
|
||||
|
||||
### What A Listing Says About The Disk:
|
||||
|
||||
`dir` ends with what is there and what is left:
|
||||
@@ -840,6 +877,7 @@ from every assembly file in it. Several are old programs written for the bare ma
|
||||
| Keys | The console interrupting rather than being asked. The only one that brings a vector of its own, which is what the version two format exists for. |
|
||||
| Play | Four voices on one clock, which is what music is and one channel cannot be. The timer keeps a tick and every voice keeps its own place in its own track and its own count of how much longer the note it is holding lasts, so the parts move at four different rates and share nothing but the beat. A track is pairs of bytes, what to play and how many ticks it lasts: 1 to 127 is a MIDI note, zero is a rest, and 255 ends it - MIDI stops at 127, so neither of those had to be invented. A note's duration is its whole life and the gate goes down when the count runs out, which means a gap between two notes is written as a rest rather than invented by the player out of some fraction it decided on. Each voice loads an instrument of its own before a note is played - an oboe for the melody, strings under it, a square wave for the bass and a kalimba for the arpeggio - out of the format SoundPatch writes, which the player reads as a count and that many parameter and value pairs and understands nothing else about. Four patches can be up at once because a patch belongs to its channel; Kalimba has an LFO switched off and the other three have one on, and under a device where the LFOs belonged to the whole machine the last patch loaded would have imposed its setting on every part. A voice does not play one long track: it walks an ORDER LIST of its own, a table of sequence addresses, and takes the next one when a sequence runs out. That is where repetition comes from and it costs no notation - the bass plays the same sequence in the first bar and the last, written once. The four columns are how it reads and how a tracker would show it; per voice is how it is stored, because a voice's order cursor is then a pointer it advances by itself. Sequence and patch names are INDICES through two tables, which are the only places an address lives - so a tune read from a file will need its base added to two arrays and nothing else, rather than a loader that walks every sequence looking for addresses to correct. A sequence can also carry commands, which take no time at all: 0x80 plays the rest of that voice on a different patch, which is how the melody's last bar becomes a swell rather than a reed. Nothing keeps the voices together except that their sequences add up to the same length, which is the first thing a compiler should check. The patch each voice starts on is declared rather than assumed, because a voice given no instrument would play on whatever the device woke up with. `Play <file>` reads a tune and plays that; `Play` on its own plays the one built into it. A tune file is "SBTU", a version, the tick in cycles, and offsets to a patch table, a sequence table and four order lists - everything in it an OFFSET from wherever it was put, so loading one is adding the base to two tables and pointing four voices at their order lists. No sequence is walked and nothing inside one is an address, which is what makes a malformed tune something that plays wrongly rather than something that takes the loader with it; the magic is checked first, because the loader follows what the offsets name. The patch each voice starts on is in the header, because a starting instrument is state. The engine that plays it is Libraries/player.asm rather than this program. It spends over ninety nine per cent of its time asleep, because a beat is something to be woken by rather than counted up to. |
|
||||
| Say | Prints whatever it was told, which is the shortest thing that shows osArgument working. |
|
||||
| ls | The listing you read, where dir is the listing you audit: the names in columns, sized to the longest of them, with directories in blue and wearing a separator. Takes a directory to list, and says nothing at all about an empty one. |
|
||||
| Walk | Says what is in the working directory, one line each: what kind of thing it is, its name, and how many blocks. The shortest thing that shows osDirFirst and osDirNext working, and the first program that could see a directory at all. |
|
||||
| Where | Says the path it was loaded from, and with a name after it, the path of that name beside it. The shortest thing that shows osWhereAmI and path.asm working, and the answer follows the program rather than whoever ran it. |
|
||||
| Reboot | Starts the machine again, in 45 bytes. Writes a port rather than asking the system, because a reset has to work when the system does not. |
|
||||
|
||||
Reference in New Issue
Block a user