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:
Anachronaut
2026-09-06 19:29:02 -04:00
co-authored by Claude Opus 5
parent 323d7a0330
commit a13bbeb4db
19 changed files with 486 additions and 12 deletions
+369
View File
@@ -0,0 +1,369 @@
; A listing laid out to be read.
;
; > ls
; Copy.sbx Say.sbx Where.sbx
; Walk.sbx Lander where.sh
;
; > ls /Apps
;
; dir says what is there, one line each, with sizes and a tally of the disk. This says the
; names and nothing else, in columns, which is what you want ninety times in a hundred - and
; the two are separate programs rather than one with a switch because they answer different
; questions and neither answer is a worse version of the other.
;
; ---- Why this is a program and dir is not ----
;
; dir is built into the shell, and that is what makes it the one to trust when the disk is
; the thing being doubted: it is already in memory. This has to be loaded FROM the disk, so it
; cannot list a disk too broken to load it. The daily driver and the diagnostic, and it is
; worth having both.
;
; ---- Two passes, and no buffer at all ----
;
; Columns need the longest name before the first line can be printed, which usually means
; holding every name in memory - twenty four bytes each, and this format allows 1,024 entries
; on a disk. So instead the directory is walked TWICE: once to find the longest name and count
; them, once to print. A walk costs a read of each directory block and the disk buffer is
; already there, where a buffer for the names would be memory this program has to have whether
; the directory is large or not.
;
; ---- Across and then down, rather than down and then across ----
;
; Real listings go down the columns, so that names next to each other alphabetically are next
; to each other on the screen. THAT IS A REASON THAT DEPENDS ON SORTING, and nothing here
; sorts: entries come back in the order the directory holds them, which is the order they were
; made. With the order arbitrary, down-and-across buys nothing and costs a division to work
; out how many rows there are - on a machine that cannot divide.
;
; So this goes across. If sorting ever arrives, that is the moment to change it, and this
; comment is the reason why.
#Include services.asm
#Program
#Base 0x5000
start:
; A directory to list, if one was named. The shell puts the working directory back when a
; program exits, so this can walk off somewhere and not tidy up after itself.
SETD.0 Given
INIB 0d64
SWI osArgument
SETD.0 Given
LDA.0
BRA lsHere
SWI osChangeDir
BNQ lsNoSuchPlace
lsHere:
; ---- Pass one: how wide the widest of them is ----
RSTA
SETD.0 Longest
STA.0
SETD.0 Seen
STA.0
SETD.0 Name
INIB 0d24
SWI osDirFirst
BRI lsMeasure
lsMeasureNext:
SETD.0 Name
INIB 0d24
SWI osDirNext
lsMeasure:
MVQA
SETD.0 Kind
STA.0
INIB 0xFF
CCF
SUB
BRQ lsMeasured
SETD.0 Seen
LDA.0
INCA
STA.0
CALL nameLength
MVQA
; A directory wears a separator, which is a character of width like any other.
SETD.0 Kind
LDB.0
DECB
BNB lsMeasureWidth
INCA
lsMeasureWidth:
SETD.0 Longest
LDB.0
CCF
SUB
BRC lsMeasureKeep ; Borrowed, so this one is not the longest.
STA.0
lsMeasureKeep:
BRI lsMeasureNext
lsMeasured:
; Nothing at all is said with nothing at all, which is what an empty directory looks like.
SETD.0 Seen
LDA.0
BRA lsDone
; ---- How many of them fit across ----
;
; Two spaces between columns, so that names never touch. Worked out by taking the cell width
; off the screen width until there is not room for another, because this machine cannot
; divide and the answer is never more than a handful.
SETD.0 Longest
LDA.0
INCA
INCA
SETD.0 Cell
STA.0
INA 0x32
SETD.1 Across
STA.1 ; What is left of the line, counted down.
RSTA
SETD.1 Columns
STA.1
lsFitting:
SETD.1 Across
LDA.1
SETD.1 Cell
LDB.1
CCF
SUB
BRC lsFitted ; Borrowed, so there is not room for another column.
MVQA
SETD.1 Across
STA.1
SETD.1 Columns
LDA.1
INCA
STA.1
BRI lsFitting
lsFitted:
; A name wider than the screen still gets a column of its own. One that runs over is better
; than one that is not shown.
SETD.0 Columns
LDA.0
BNA lsPrint
INIA 0d1
STA.0
lsPrint:
RSTA
SETD.0 At
STA.0
SETD.0 Name
INIB 0d24
SWI osDirFirst
BRI lsSay
lsSayNext:
SETD.0 Name
INIB 0d24
SWI osDirNext
lsSay:
MVQA
SETD.0 Kind
STA.0
INIB 0xFF
CCF
SUB
BRQ lsLastLine
; ---- The cell the last name started, finished now rather than then ----
;
; Padding AFTER a name puts spaces at the end of every line that does not fill its last
; column, and trailing whitespace on a line is the kind of thing that is invisible until
; something else reads the output. Padding before the next name instead means the spaces
; only ever go between two things, and a line ends on the last character of a name.
SETD.0 At
LDA.0
BRA lsColour ; First on its line, so there is nothing to finish.
CALL padOut
lsColour:
; ---- What it is, said in colour ----
;
; The attribute reaches a terminal as well as the screen now, so this is one mechanism and
; not two. A directory in blue and an unfinished save in red; everything else plain, because
; 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.
SETD.0 Kind
LDA.0
BRA lsPlain
DECA
BRA lsBlue
INIA 0d1 ; An unfinished save, which is worth looking at.
BRI lsInk
lsBlue:
INIA 0d4
BRI lsInk
lsPlain:
RSTA
lsInk:
OUTA 0x06
SETD.0 Name
SWI osPrintString
CALL nameLength
MVQA
SETD.0 Kind
LDB.0
DECB
BNB lsWidth
PSHA
SETD.0 Slash
SWI osPrintString
POPA
INCA
lsWidth:
; How much of the cell this name took, for whoever finishes it.
SETD.0 Used
STA.0
; Back to plain before anything else is written, so that a scheme with paper in it would
; not paint the gap between the columns.
RSTA
OUTA 0x06
SETD.0 At
LDA.0
INCA
STA.0
SETD.0 Columns
LDB.0
CCF
SUB
BNQ lsSayNext ; Room for another on this line.
RSTA
SETD.0 At
STA.0
CALL newLine
BRI lsSayNext
lsLastLine:
; A line with something on it needs ending. One that ended on its last column does not, and
; a blank line under a listing looks like a listing with a gap in it.
SETD.0 At
LDA.0
BRA lsDone
CALL newLine
lsDone:
RSTA
SWI osExit
lsNoSuchPlace:
SETD.0 NoSuchPlace
SWI osPrintString
SETD.0 Given
SWI osPrintString
CALL newLine
INIA 0x01
SWI osExit
; ---- How wide the name in the buffer is ----
;
; Q COMES BACK THE COUNT, not A. A RET puts A back the way the caller had it, so a subroutine
; that answers in A answers with nothing - and this one did, which made every gap between the
; columns 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 on this machine
; comes home in it.
nameLength:
SETD.0 Name
RSTA
lengthNext:
LDB.0
BRB lengthDone
INCA
INCD.0
BRI lengthNext
lengthDone:
RSTB
CCF
ADD
RET
; A is how much of the cell the name took. Fills the rest of it with spaces.
padOut:
; The cell less what the name took, and IN THAT ORDER. Written the other way round first,
; which borrowed on every name that was not the longest - so every one of them took the
; "wider than its cell" path below and the whole listing came out separated by one space.
SETD.0 Cell
LDA.0
SETD.0 Used
LDB.0
CCF
SUB
BRC padDone ; Wider than its cell, so one space keeps the names apart.
MVQA
BRA padOne
padLoop:
PSHA
SETD.0 Space
SWI osPrintString
POPA
DECA
BNA padLoop
RET
padOne:
padDone:
SETD.0 Space
SWI osPrintString
RET
newLine:
SETD.0 NewLine
SWI osPrintString
RET
#Data
#Base 0x3000
Slash:
"/"
Space:
" "
NoSuchPlace:
"there is no such directory: "
NewLine:
0x0A 0x00
Kind:
0x00
Longest:
0x00
Cell:
0x00
Columns:
0x00
Across:
0x00
At:
0x00
Seen:
0x00
Used:
0x00
Name:
#Reserve 0d24
Given:
#Reserve 0d64
+38
View File
@@ -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 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. 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: ### What A Listing Says About The Disk:
`dir` ends with what is there and what is left: `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. | | 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. | | 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. | | 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. | | 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. | | 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. | | 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. |
+1 -1
View File
@@ -125,7 +125,7 @@ from `make`, not from here.
### 1. Recorded output ### 1. Recorded output
`Tests/run.sh` assembles each program named in `Tests/manifest`, runs it, and compares `Tests/run.sh` assembles each program named in `Tests/manifest`, runs it, and compares
everything it printed against a file in `Tests/expected`. 218 tests, of which 156 run, 35 everything it printed against a file in `Tests/expected`. 219 tests, of which 157 run, 35
only assemble, 16 are expected to fail to assemble, and 11 boot from ROM with no image only assemble, 16 are expected to fail to assemble, and 11 boot from ROM with no image
given at all. given at all.
+1 -1
View File
@@ -51,7 +51,7 @@ pass.script 20
holds.script 87 holds.script 87
crossed.txt 560 crossed.txt 560
36 files, 2 directories 36 files, 2 directories
47 of 64 entries, 1890 blocks free 48 of 64 entries, 1887 blocks free
> exit > exit
halted halted
Execution halted. Execution halted.
+1 -1
View File
@@ -40,7 +40,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> drive 1 > drive 1
> dir > dir
other.txt 28 other.txt 28
+1 -1
View File
@@ -57,7 +57,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> exit > exit
halted halted
Execution halted. Execution halted.
+1 -1
View File
@@ -47,7 +47,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> exit > exit
halted halted
Execution halted. Execution halted.
+1 -1
View File
@@ -47,7 +47,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> exit > exit
halted halted
Execution halted. Execution halted.
+32
View File
@@ -0,0 +1,32 @@
CosmOS
> ls
greet.sbx hello.sbx Life.sbx Snake.sbx Keys.sbx
Say.sbx Where.sbx Break.sbx Grid.sbx Press.sbx
Mode.sbx Flip.sbx Sprite.sbx Depth.sbx Packages/
Pad.sbx Crash.sbx vars.script blocks.script loops.script
tune.sbx Play.sbx notes.txt Apps/ hi.script
bad.script plain.script cross.script nonl.script outer.script
inner.script loop.script hush.script aloud.script args.script
pass.script holds.script
finished
> cd /Apps
/Apps> ls
Copy.sbx Say.sbx Where.sbx Walk.sbx ls.sbx Lander where.sh
finished
/Apps> cd /
> ls /Packages/app.Lander
Lander.sbx splash.tune
finished
> mkdir /empty
made
> cd /empty
/empty> ls
finished
/empty> cd /
> ls nowhere
there is no such directory: nowhere
finished
> exit
halted
Execution halted.
[exit 0]
+1 -1
View File
@@ -133,7 +133,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> exit > exit
halted halted
Execution halted. Execution halted.
+1 -1
View File
@@ -52,7 +52,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> exit > exit
halted halted
Execution halted. Execution halted.
+1 -1
View File
@@ -40,7 +40,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> load > load
load what? load what?
> load nosuch.sbx > load nosuch.sbx
+1 -1
View File
@@ -38,7 +38,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> load Say.sbx > load Say.sbx
loaded, starting at 5000 loaded, starting at 5000
> run the disk took its time > run the disk took its time
+1 -1
View File
@@ -38,7 +38,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> drive 1 > drive 1
> dir > dir
f1.txt 1500 f1.txt 1500
+1 -1
View File
@@ -47,7 +47,7 @@ args.script 64
pass.script 20 pass.script 20
holds.script 87 holds.script 87
35 files, 2 directories 35 files, 2 directories
46 of 64 entries, 1893 blocks free 47 of 64 entries, 1890 blocks free
> exit > exit
halted halted
Execution halted. Execution halted.
+1
View File
@@ -44,6 +44,7 @@ f Copy.sbx 7
f Say.sbx 1 f Say.sbx 1
f Where.sbx 4 f Where.sbx 4
f Walk.sbx 1 f Walk.sbx 1
f ls.sbx 3
f Lander 4 f Lander 4
f where.sh 3 f where.sh 3
finished finished
+11
View File
@@ -0,0 +1,11 @@
ls
cd /Apps
ls
cd /
ls /Packages/app.Lander
mkdir /empty
cd /empty
ls
cd /
ls nowhere
exit
+6
View File
@@ -283,6 +283,12 @@ python3 -c "open('twoblocks.txt','w').write('the second disk, at length. ' * 20)
# Walk goes in /Apps and nowhere else, so that it is reachable from any directory - which is # Walk goes in /Apps and nowhere else, so that it is reachable from any directory - which is
# what lets the same program list the root and then list /Apps. # what lets the same program list the root and then list /Apps.
"$TOOL" put "$DISKS/cosmos.img" "$WORK/Walk.sbx" /Apps/Walk.sbx >/dev/null "$TOOL" put "$DISKS/cosmos.img" "$WORK/Walk.sbx" /Apps/Walk.sbx >/dev/null
# ls is the listing you read, where dir is the listing you audit. It is a PROGRAM and 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.
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \
"$ROOT/Programs/CosmOS/Apps/ls.asm" -o "$WORK/ls.sbx" >/dev/null
"$TOOL" put "$DISKS/cosmos.img" "$WORK/ls.sbx" /Apps/ls.sbx >/dev/null
# And the launcher for Lunar Porter, under the bare name somebody types. Put here rather than # And the launcher for Lunar Porter, under the bare name somebody types. Put here rather than
# beside the game above, because /Apps is not made until now. # beside the game above, because /Apps is not made until now.
"$TOOL" put "$DISKS/cosmos.img" "$ROOT/Programs/CosmOS/Launchers/Lander" /Apps/Lander >/dev/null "$TOOL" put "$DISKS/cosmos.img" "$ROOT/Programs/CosmOS/Launchers/Lander" /Apps/Lander >/dev/null
+17
View File
@@ -1051,6 +1051,23 @@ cosmosWalk | CosmOS/Source/cosmos.asm | run | cosmosWal
# when it differs from the free total, so the first listing is silent about it and the second # when it differs from the free total, so the first listing is silent about it and the second
# is not - a line that appears only when something is wrong is a line somebody reads. # is not - a line that appears only when something is wrong is a line somebody reads.
cosmosSpace | CosmOS/Source/cosmos.asm | run | cosmosSpace.in | 200000000 | disks/cosmos.img+disks/holes.img cosmosSpace | CosmOS/Source/cosmos.asm | run | cosmosSpace.in | 200000000 | disks/cosmos.img+disks/holes.img
# ---- A listing laid out to be read ----
#
# dir says what is there one line each with sizes and a tally; ls says the names in columns.
# Two programs rather than one with a switch, because they answer different questions - and ls
# is a PROGRAM where dir is built in, which is the difference that matters when the disk is
# what you are doubting.
#
# TWO PASSES 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. So the directory is walked twice instead, which is the
# first thing osDirFirst made possible that dir could not already do.
#
# Five things in one session: the root, where the directories are coloured and wear a
# separator; a subdirectory; a directory named as an argument, which changes there and lets
# the shell put it back; an EMPTY one, which says nothing at all rather than saying so; and a
# name that is not a directory, which says so and fails.
cosmosLs | CosmOS/Source/cosmos.asm | run | cosmosLs.in | 200000000 | disks/cosmos.img
# ---- Starting itself ---- # ---- Starting itself ----
# #
# /System/Boot/startup.sh runs before anybody can type. This one is also the check on #quiet # /System/Boot/startup.sh runs before anybody can type. This one is also the check on #quiet