diff --git a/Programs/CosmOS/Apps/ls.asm b/Programs/CosmOS/Apps/ls.asm new file mode 100644 index 0000000..9c2f5c4 --- /dev/null +++ b/Programs/CosmOS/Apps/ls.asm @@ -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 diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 66e6818..a080622 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -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 ` 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. | diff --git a/SplitBit Test Manual.md b/SplitBit Test Manual.md index b6651e3..a525fc5 100644 --- a/SplitBit Test Manual.md +++ b/SplitBit Test Manual.md @@ -125,7 +125,7 @@ from `make`, not from here. ### 1. Recorded output `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 given at all. diff --git a/Tests/expected/cosmosCrossDisk.out b/Tests/expected/cosmosCrossDisk.out index b4f5894..80c06a3 100644 --- a/Tests/expected/cosmosCrossDisk.out +++ b/Tests/expected/cosmosCrossDisk.out @@ -51,7 +51,7 @@ pass.script 20 holds.script 87 crossed.txt 560 36 files, 2 directories -47 of 64 entries, 1890 blocks free +48 of 64 entries, 1887 blocks free > exit halted Execution halted. diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index 9688d1b..d66459e 100644 --- a/Tests/expected/cosmosDrives.out +++ b/Tests/expected/cosmosDrives.out @@ -40,7 +40,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > drive 1 > dir other.txt 28 diff --git a/Tests/expected/cosmosFault.out b/Tests/expected/cosmosFault.out index 4d5d63c..666492e 100644 --- a/Tests/expected/cosmosFault.out +++ b/Tests/expected/cosmosFault.out @@ -57,7 +57,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > exit halted Execution halted. diff --git a/Tests/expected/cosmosFlip.out b/Tests/expected/cosmosFlip.out index f317625..63359b3 100644 --- a/Tests/expected/cosmosFlip.out +++ b/Tests/expected/cosmosFlip.out @@ -47,7 +47,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > exit halted Execution halted. diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index 7daa17d..d189bfb 100644 --- a/Tests/expected/cosmosGrid.out +++ b/Tests/expected/cosmosGrid.out @@ -47,7 +47,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > exit halted Execution halted. diff --git a/Tests/expected/cosmosLs.out b/Tests/expected/cosmosLs.out new file mode 100644 index 0000000..c557526 --- /dev/null +++ b/Tests/expected/cosmosLs.out @@ -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] diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index 355eb3b..b9520a8 100644 --- a/Tests/expected/cosmosMonitor.out +++ b/Tests/expected/cosmosMonitor.out @@ -133,7 +133,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > exit halted Execution halted. diff --git a/Tests/expected/cosmosMonitorRun.out b/Tests/expected/cosmosMonitorRun.out index 6a870da..6a04b97 100644 --- a/Tests/expected/cosmosMonitorRun.out +++ b/Tests/expected/cosmosMonitorRun.out @@ -52,7 +52,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > exit halted Execution halted. diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index 5c6dd4a..c86594b 100644 --- a/Tests/expected/cosmosRun.out +++ b/Tests/expected/cosmosRun.out @@ -40,7 +40,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > load load what? > load nosuch.sbx diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index bd4a3a9..09c5d64 100644 --- a/Tests/expected/cosmosSlowDisk.out +++ b/Tests/expected/cosmosSlowDisk.out @@ -38,7 +38,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > load Say.sbx loaded, starting at 5000 > run the disk took its time diff --git a/Tests/expected/cosmosSpace.out b/Tests/expected/cosmosSpace.out index 8ab4f34..9e45035 100644 --- a/Tests/expected/cosmosSpace.out +++ b/Tests/expected/cosmosSpace.out @@ -38,7 +38,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > drive 1 > dir f1.txt 1500 diff --git a/Tests/expected/cosmosSprite.out b/Tests/expected/cosmosSprite.out index 31be263..bd9dfc9 100644 --- a/Tests/expected/cosmosSprite.out +++ b/Tests/expected/cosmosSprite.out @@ -47,7 +47,7 @@ args.script 64 pass.script 20 holds.script 87 35 files, 2 directories -46 of 64 entries, 1893 blocks free +47 of 64 entries, 1890 blocks free > exit halted Execution halted. diff --git a/Tests/expected/cosmosWalk.out b/Tests/expected/cosmosWalk.out index ac95446..5e77624 100644 --- a/Tests/expected/cosmosWalk.out +++ b/Tests/expected/cosmosWalk.out @@ -44,6 +44,7 @@ f Copy.sbx 7 f Say.sbx 1 f Where.sbx 4 f Walk.sbx 1 +f ls.sbx 3 f Lander 4 f where.sh 3 finished diff --git a/Tests/input/cosmosLs.in b/Tests/input/cosmosLs.in new file mode 100644 index 0000000..d7cf6b3 --- /dev/null +++ b/Tests/input/cosmosLs.in @@ -0,0 +1,11 @@ +ls +cd /Apps +ls +cd / +ls /Packages/app.Lander +mkdir /empty +cd /empty +ls +cd / +ls nowhere +exit diff --git a/Tests/makedisks.sh b/Tests/makedisks.sh index b5ba28c..75427cf 100755 --- a/Tests/makedisks.sh +++ b/Tests/makedisks.sh @@ -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 # 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 +# 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 # 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 diff --git a/Tests/manifest b/Tests/manifest index 6023826..aec40c3 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -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 # 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 +# ---- 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 ---- # # /System/Boot/startup.sh runs before anybody can type. This one is also the check on #quiet