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
This commit is contained in:
co-authored by
Claude Opus 5
parent
2ad8edf9bc
commit
323d7a0330
@@ -693,6 +693,55 @@ 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.
|
||||||
|
|
||||||
|
### What A Listing Says About The Disk:
|
||||||
|
|
||||||
|
`dir` ends with what is there and what is left:
|
||||||
|
|
||||||
|
```text
|
||||||
|
43 files, 3 directories
|
||||||
|
46 of 64 entries, 1893 blocks free
|
||||||
|
```
|
||||||
|
|
||||||
|
**Entries come 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, and saying both
|
||||||
|
means never having to work out which one is about to bite.
|
||||||
|
|
||||||
|
**The free figure is counted, not asked for.** The superblock keeps a free count and
|
||||||
|
`sbfs.asm` calls it *"a note rather than the truth"* in three separate places; `sbfsSpace`
|
||||||
|
reads the whole directory table instead, which costs a read per directory block and is the
|
||||||
|
answer rather than a guess. SplitDisk reads the note as well and says so when the two
|
||||||
|
disagree, which is the right place for that check: the host tool is what you audit a disk
|
||||||
|
with, and this is what you work on one with.
|
||||||
|
|
||||||
|
**And it is a fact about the disk, not about where you are standing.** The first version of
|
||||||
|
this added the blocks up as the listing walked past them, which cost nothing extra 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`. The two implementations of this format
|
||||||
|
disagreeing is what said so.
|
||||||
|
|
||||||
|
A third line appears when it needs to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
4 of 16 entries, 37 blocks free
|
||||||
|
the longest run is 25
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files are laid down contiguously**, so the free total is not what decides whether a file
|
||||||
|
will fit - the longest unbroken run is. A disk with a thousand blocks free in ten pieces
|
||||||
|
refuses a file of two hundred, and nothing else in the listing would hint at it.
|
||||||
|
|
||||||
|
**It is said only when it differs from the free total**, which on a healthy disk it does not.
|
||||||
|
Deleting is what fragments a contiguous store, and a disk that has only been appended to has
|
||||||
|
one gap, at the end. A line printed under every listing saying the same number twice is noise;
|
||||||
|
one that appears only when something is wrong is a line somebody reads.
|
||||||
|
|
||||||
|
Finding it costs a pass of the directory table per gap. There is no sort on this machine and
|
||||||
|
the entries are in no order, so a candidate walks the disk instead: each pass looks for the
|
||||||
|
used extent nearest at or after it, and anything the candidate is standing inside pushes it to
|
||||||
|
the far end and starts the pass again - the same trick allocating uses, for the same reason.
|
||||||
|
So the measurement costs almost nothing on a disk with one gap and more the more fragmented
|
||||||
|
the disk is, which is the right way round: it is slow exactly when it has something to say.
|
||||||
|
|
||||||
### Starting An Application By Name:
|
### Starting An Application By Name:
|
||||||
|
|
||||||
A word the shell has no command for is not immediately an error. Before saying so, the
|
A word the shell has no command for is not immediately an error. Before saying so, the
|
||||||
|
|||||||
@@ -3938,6 +3938,102 @@ dirFolderCount:
|
|||||||
CALL printString
|
CALL printString
|
||||||
dirNoFolders:
|
dirNoFolders:
|
||||||
CALL newLine
|
CALL newLine
|
||||||
|
|
||||||
|
; ---- And what is left of the disk ----
|
||||||
|
;
|
||||||
|
; A listing that says what is there and not what is left says half of what anybody wants
|
||||||
|
; to know. SplitDisk has printed this since it was written; the machine's own listing had
|
||||||
|
; nothing to say about the disk it was listing.
|
||||||
|
;
|
||||||
|
; COUNTED RATHER THAN ASKED. The superblock keeps a free count, and sbfs.asm calls it "a
|
||||||
|
; note rather than the truth" in three separate places. The truth is what the entries add
|
||||||
|
; up to, and the walk above has just added them up - so this costs no disk read at all,
|
||||||
|
; where believing the note would cost one and be a guess. SplitDisk reads the note as well
|
||||||
|
; and says so when the two disagree, which is the right place for that check: the host tool
|
||||||
|
; is what you audit a disk with, and this is what you work on one with.
|
||||||
|
;
|
||||||
|
; ---- AND IT IS THE DISK, NOT THIS DIRECTORY ----
|
||||||
|
;
|
||||||
|
; The first version of this 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 was called 1,996 blocks free from the root and 2,025 from /Apps. Free space
|
||||||
|
; is a fact about the disk, so sbfsSpace reads the whole directory table for it - and the
|
||||||
|
; two implementations of this format disagreeing is what said so.
|
||||||
|
CALL sbfsSpace
|
||||||
|
BNQ dirNoSpace
|
||||||
|
|
||||||
|
; 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, and saying
|
||||||
|
; both means never having to work out which one is about to bite.
|
||||||
|
SETD.0 DirEntries
|
||||||
|
SETD.2 SbfsDirBlocks
|
||||||
|
CALL sbfsSetWord
|
||||||
|
INIA 0d3
|
||||||
|
SETD.0 DirDoubles
|
||||||
|
STA.0
|
||||||
|
dirEntriesTimes:
|
||||||
|
; Eight entries to a directory block, which is three doublings on a machine with no
|
||||||
|
; multiply. Adding a number to itself is what a doubling is.
|
||||||
|
SETD.0 DirEntries
|
||||||
|
SETD.2 DirEntries
|
||||||
|
CALL sbfsAddWord
|
||||||
|
SETD.0 DirDoubles
|
||||||
|
LDA.0
|
||||||
|
DECA
|
||||||
|
STA.0
|
||||||
|
BNA dirEntriesTimes
|
||||||
|
|
||||||
|
SETD.0 SbfsUsedEntries
|
||||||
|
CALL printWordDecimal
|
||||||
|
SETD.0 OfText
|
||||||
|
CALL printString
|
||||||
|
SETD.0 DirEntries
|
||||||
|
CALL printWordDecimal
|
||||||
|
SETD.0 EntriesText
|
||||||
|
CALL printString
|
||||||
|
|
||||||
|
; What is left is the disk, less where the files begin, less what they hold.
|
||||||
|
SETD.0 DirFree
|
||||||
|
SETD.2 SbfsDiskBlocks
|
||||||
|
CALL sbfsSetWord
|
||||||
|
CALL sbfsFirstData
|
||||||
|
SETD.0 DirFree
|
||||||
|
SETD.2 SbfsCandidate
|
||||||
|
CALL sbfsSubWord
|
||||||
|
SETD.0 DirFree
|
||||||
|
SETD.2 SbfsUsedBlocks
|
||||||
|
CALL sbfsSubWord
|
||||||
|
|
||||||
|
SETD.0 DirFree
|
||||||
|
CALL printWordDecimal
|
||||||
|
SETD.0 FreeText
|
||||||
|
CALL printString
|
||||||
|
CALL newLine
|
||||||
|
|
||||||
|
; ---- And how much of that is in one piece ----
|
||||||
|
;
|
||||||
|
; Said only when it is not all of it, which is the common case and the quiet one. Files are
|
||||||
|
; laid down contiguously, so the free total is not what decides whether a file will fit -
|
||||||
|
; the longest run is. A disk with a thousand blocks free in ten pieces refuses a file of two
|
||||||
|
; hundred, and nothing in the listing would have hinted at it.
|
||||||
|
;
|
||||||
|
; A LISTING THAT SAID "longest run 1893" UNDER "1893 blocks free" WOULD BE NOISE on every
|
||||||
|
; healthy disk, and a line that only appears when something is wrong is a line somebody
|
||||||
|
; reads. It is also what makes the measurement worth its cost: a disk with one gap at the
|
||||||
|
; end, which is what an append-only disk is, walks the table twice and says nothing.
|
||||||
|
CALL sbfsLargestRun
|
||||||
|
BNQ dirNoSpace
|
||||||
|
SETD.0 DirFree
|
||||||
|
SETD.2 SbfsBiggest
|
||||||
|
CALL sbfsCompareWord
|
||||||
|
BRQ dirNoSpace
|
||||||
|
|
||||||
|
SETD.0 RunText
|
||||||
|
CALL printString
|
||||||
|
SETD.0 SbfsBiggest
|
||||||
|
CALL printWordDecimal
|
||||||
|
CALL newLine
|
||||||
|
dirNoSpace:
|
||||||
BRI prompt
|
BRI prompt
|
||||||
|
|
||||||
dirNoDisk:
|
dirNoDisk:
|
||||||
@@ -7927,6 +8023,15 @@ IsDirectory:
|
|||||||
"that is a directory"
|
"that is a directory"
|
||||||
AndText:
|
AndText:
|
||||||
", "
|
", "
|
||||||
|
OfText:
|
||||||
|
" of "
|
||||||
|
EntriesText:
|
||||||
|
" entries, "
|
||||||
|
FreeText:
|
||||||
|
" blocks free"
|
||||||
|
RunText:
|
||||||
|
"the longest run is "
|
||||||
|
|
||||||
FoldersText:
|
FoldersText:
|
||||||
" directories"
|
" directories"
|
||||||
FolderText:
|
FolderText:
|
||||||
@@ -8219,6 +8324,14 @@ DirFolders:
|
|||||||
0x00
|
0x00
|
||||||
DirTaken:
|
DirTaken:
|
||||||
0x00
|
0x00
|
||||||
|
; What the entries add up to, what is left, and how many slots there are to fill. Counted on
|
||||||
|
; the way past rather than asked of the superblock, whose count sbfs.asm calls a note.
|
||||||
|
DirFree:
|
||||||
|
0x00 0x00
|
||||||
|
DirEntries:
|
||||||
|
0x00 0x00
|
||||||
|
DirDoubles:
|
||||||
|
0x00
|
||||||
DiskReady:
|
DiskReady:
|
||||||
0x00
|
0x00
|
||||||
LoadedOk:
|
LoadedOk:
|
||||||
|
|||||||
@@ -1916,6 +1916,278 @@ sbfsEntryBounds:
|
|||||||
sbfsBoundsDone:
|
sbfsBoundsDone:
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
; ---- What the whole disk is holding ----
|
||||||
|
;
|
||||||
|
; SbfsUsedBlocks becomes what every entry on the disk holds between them, and SbfsUsedEntries
|
||||||
|
; how many slots are filled. Q is zero if the disk could be read.
|
||||||
|
;
|
||||||
|
; EVERY ENTRY, NOT EVERY ENTRY IN A DIRECTORY. Free space is a fact about the disk and not
|
||||||
|
; about where you are standing, so this walks the directory table itself the way allocating
|
||||||
|
; does, rather than using sbfsFirst and sbfsNext - which stop only on entries whose parent is
|
||||||
|
; the working directory, and would have called the same disk emptier from one directory than
|
||||||
|
; from another.
|
||||||
|
;
|
||||||
|
; A DIRECTORY HOLDS NO BLOCKS and adds nought, which is true by its fields rather than by a
|
||||||
|
; test here. An unfinished save holds all of its, and is counted, because the blocks are
|
||||||
|
; spoken for whatever the entry is called - and a listing that left them out would promise
|
||||||
|
; room that is not there.
|
||||||
|
;
|
||||||
|
; The count in the superblock is not consulted. sbfs.asm calls it "a note rather than the
|
||||||
|
; truth" in three places, and this is the truth: it costs a read of every directory block,
|
||||||
|
; which is what the honest answer costs.
|
||||||
|
sbfsSpace:
|
||||||
|
RSTA
|
||||||
|
SETD.0 SbfsUsedBlocks
|
||||||
|
STA.0
|
||||||
|
INCD.0
|
||||||
|
STA.0
|
||||||
|
SETD.0 SbfsUsedEntries
|
||||||
|
STA.0
|
||||||
|
INCD.0
|
||||||
|
STA.0
|
||||||
|
|
||||||
|
SETD.0 SbfsDirStart
|
||||||
|
SETD.1 SbfsBlock
|
||||||
|
CALL sbfsCopyWord
|
||||||
|
SETD.0 SbfsDirBlocks
|
||||||
|
INCD.0
|
||||||
|
LDA.0
|
||||||
|
SETD.1 SbfsLeft
|
||||||
|
STA.1
|
||||||
|
|
||||||
|
sbfsSpaceBlock:
|
||||||
|
CALL sbfsReadBlock
|
||||||
|
BNQ sbfsSpaceFailed
|
||||||
|
SETD.1 SbfsBuffer
|
||||||
|
CALL sbfsBufferOut
|
||||||
|
SETD.2 SbfsBuffer
|
||||||
|
INIA 0d8
|
||||||
|
SETD.1 SbfsCount
|
||||||
|
STA.1
|
||||||
|
|
||||||
|
sbfsSpaceEntry:
|
||||||
|
LDA.2
|
||||||
|
INIB 0x01
|
||||||
|
AND
|
||||||
|
BRQ sbfsSpaceNext ; A free slot holds nothing and fills nothing.
|
||||||
|
|
||||||
|
SETD.0 SbfsUsedEntries
|
||||||
|
CALL sbfsStepWord
|
||||||
|
|
||||||
|
; The blocks this entry holds, which are at the same offset in an entry as they are
|
||||||
|
; everywhere else. sbfsEntryBounds wants DP2 on the entry and gives back both ends, and the
|
||||||
|
; length is the difference - worked out that way rather than read directly, so that one
|
||||||
|
; routine owns where in an entry those fields are.
|
||||||
|
PSHD.2
|
||||||
|
CALL sbfsEntryBounds
|
||||||
|
SETD.0 SbfsEntryEnd
|
||||||
|
SETD.2 SbfsEntryStart
|
||||||
|
CALL sbfsSubWord
|
||||||
|
SETD.0 SbfsUsedBlocks
|
||||||
|
SETD.2 SbfsEntryEnd
|
||||||
|
CALL sbfsAddWord
|
||||||
|
POPD.2
|
||||||
|
|
||||||
|
sbfsSpaceNext:
|
||||||
|
DPUP.2 0d32
|
||||||
|
SETD.1 SbfsCount
|
||||||
|
LDA.1
|
||||||
|
DECA
|
||||||
|
STA.1
|
||||||
|
BNA sbfsSpaceEntry
|
||||||
|
|
||||||
|
SETD.0 SbfsBlock
|
||||||
|
CALL sbfsStepWord
|
||||||
|
SETD.1 SbfsLeft
|
||||||
|
LDA.1
|
||||||
|
DECA
|
||||||
|
STA.1
|
||||||
|
BNA sbfsSpaceBlock
|
||||||
|
|
||||||
|
RSTA
|
||||||
|
RSTB
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
RET
|
||||||
|
|
||||||
|
sbfsSpaceFailed:
|
||||||
|
INIA 0x01
|
||||||
|
RSTB
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
RET
|
||||||
|
|
||||||
|
; ---- The longest run of free blocks there is ----
|
||||||
|
;
|
||||||
|
; SbfsBiggest becomes the largest number of blocks that are free AND NEXT TO EACH OTHER.
|
||||||
|
; Q is zero if the disk could be read.
|
||||||
|
;
|
||||||
|
; This is the number that says whether a file will fit. Files are laid down contiguously -
|
||||||
|
; first fit, with the directory itself as the map and no allocation table anywhere - so a disk
|
||||||
|
; with a thousand blocks free in ten scattered pieces will refuse a file of two hundred, and
|
||||||
|
; the free total gives no hint of it.
|
||||||
|
;
|
||||||
|
; ---- The same trick allocating uses, for the same reason ----
|
||||||
|
;
|
||||||
|
; There is no sort on this machine and the entries are in no order, so the gaps cannot simply
|
||||||
|
; be listed. Instead a candidate walks the disk: each pass looks for the used extent nearest
|
||||||
|
; at or after it, and anything the candidate is standing inside pushes the candidate to the
|
||||||
|
; far end of it and starts the pass again.
|
||||||
|
;
|
||||||
|
; So the cost is a pass of the directory table per gap, rather than per file - and it is a
|
||||||
|
; measurement that costs nothing on a disk with one gap at the end, which is what a disk that
|
||||||
|
; has only been appended to looks like, and costs more the more fragmented the disk is. Which
|
||||||
|
; is the right way round: it is slow exactly when it has something to say.
|
||||||
|
sbfsLargestRun:
|
||||||
|
RSTA
|
||||||
|
SETD.0 SbfsBiggest
|
||||||
|
STA.0
|
||||||
|
INCD.0
|
||||||
|
STA.0
|
||||||
|
CALL sbfsFirstData
|
||||||
|
|
||||||
|
sbfsRunPass:
|
||||||
|
; Nothing is in the way yet this time round, and the nearest thing ahead is the end of the
|
||||||
|
; disk - which is the answer when there is nothing ahead at all.
|
||||||
|
RSTA
|
||||||
|
SETD.0 SbfsRunMoved
|
||||||
|
STA.0
|
||||||
|
SETD.0 SbfsRunNext
|
||||||
|
SETD.2 SbfsDiskBlocks
|
||||||
|
CALL sbfsSetWord
|
||||||
|
|
||||||
|
SETD.0 SbfsDirStart
|
||||||
|
SETD.1 SbfsBlock
|
||||||
|
CALL sbfsCopyWord
|
||||||
|
SETD.0 SbfsDirBlocks
|
||||||
|
INCD.0
|
||||||
|
LDA.0
|
||||||
|
SETD.1 SbfsLeft
|
||||||
|
STA.1
|
||||||
|
|
||||||
|
sbfsRunBlock:
|
||||||
|
CALL sbfsReadBlock
|
||||||
|
BNQ sbfsRunFailed
|
||||||
|
SETD.1 SbfsBuffer
|
||||||
|
CALL sbfsBufferOut
|
||||||
|
SETD.2 SbfsBuffer
|
||||||
|
INIA 0d8
|
||||||
|
SETD.1 SbfsCount
|
||||||
|
STA.1
|
||||||
|
|
||||||
|
sbfsRunEntry:
|
||||||
|
LDA.2
|
||||||
|
INIB 0x01
|
||||||
|
AND
|
||||||
|
BRQ sbfsRunNextEntry ; A free slot is in nobody's way.
|
||||||
|
LDA.2
|
||||||
|
INIB 0x02
|
||||||
|
AND
|
||||||
|
BNQ sbfsRunNextEntry ; Nor is a directory, which holds no blocks - see allocating.
|
||||||
|
|
||||||
|
CALL sbfsEntryBounds
|
||||||
|
PSHD.2 ; The comparisons want DP2, and the walk wants it back.
|
||||||
|
|
||||||
|
; Behind the candidate entirely, so it says nothing about what is ahead.
|
||||||
|
SETD.0 SbfsEntryEnd
|
||||||
|
SETD.2 SbfsCandidate
|
||||||
|
CALL sbfsCompareWord
|
||||||
|
BRC sbfsRunClear
|
||||||
|
BRQ sbfsRunClear
|
||||||
|
|
||||||
|
; Beginning at or before the candidate, so the candidate is standing inside it. It moves to
|
||||||
|
; the far end and the pass begins again, exactly as allocating does.
|
||||||
|
SETD.0 SbfsEntryStart
|
||||||
|
SETD.2 SbfsCandidate
|
||||||
|
CALL sbfsCompareWord
|
||||||
|
BRC sbfsRunStraddles
|
||||||
|
BRQ sbfsRunStraddles
|
||||||
|
|
||||||
|
; Ahead of the candidate. Is it the nearest thing ahead so far?
|
||||||
|
SETD.0 SbfsEntryStart
|
||||||
|
SETD.2 SbfsRunNext
|
||||||
|
CALL sbfsCompareWord
|
||||||
|
BNC sbfsRunClear
|
||||||
|
SETD.0 SbfsRunNext
|
||||||
|
SETD.2 SbfsEntryStart
|
||||||
|
CALL sbfsSetWord
|
||||||
|
BRI sbfsRunClear
|
||||||
|
|
||||||
|
sbfsRunStraddles:
|
||||||
|
SETD.0 SbfsCandidate
|
||||||
|
SETD.2 SbfsEntryEnd
|
||||||
|
CALL sbfsSetWord
|
||||||
|
INIA 0x01
|
||||||
|
SETD.0 SbfsRunMoved
|
||||||
|
STA.0
|
||||||
|
|
||||||
|
sbfsRunClear:
|
||||||
|
POPD.2
|
||||||
|
|
||||||
|
sbfsRunNextEntry:
|
||||||
|
DPUP.2 0d32
|
||||||
|
SETD.1 SbfsCount
|
||||||
|
LDA.1
|
||||||
|
DECA
|
||||||
|
STA.1
|
||||||
|
BNA sbfsRunEntry
|
||||||
|
|
||||||
|
SETD.0 SbfsBlock
|
||||||
|
CALL sbfsStepWord
|
||||||
|
SETD.1 SbfsLeft
|
||||||
|
LDA.1
|
||||||
|
DECA
|
||||||
|
STA.1
|
||||||
|
BNA sbfsRunBlock
|
||||||
|
|
||||||
|
; The pass is over. If the candidate moved, everything measured against where it used to be
|
||||||
|
; was measured against the wrong place, so the pass is worth nothing and is done again.
|
||||||
|
SETD.0 SbfsRunMoved
|
||||||
|
LDA.0
|
||||||
|
BNA sbfsRunPass
|
||||||
|
|
||||||
|
; Nothing moved, so the gap from the candidate to the nearest thing ahead is a real one.
|
||||||
|
SETD.0 SbfsRunGap
|
||||||
|
SETD.2 SbfsRunNext
|
||||||
|
CALL sbfsSetWord
|
||||||
|
SETD.0 SbfsRunGap
|
||||||
|
SETD.2 SbfsCandidate
|
||||||
|
CALL sbfsSubWord
|
||||||
|
|
||||||
|
SETD.0 SbfsBiggest
|
||||||
|
SETD.2 SbfsRunGap
|
||||||
|
CALL sbfsCompareWord
|
||||||
|
BNC sbfsRunKept ; What is already remembered is the same or bigger.
|
||||||
|
SETD.0 SbfsBiggest
|
||||||
|
SETD.2 SbfsRunGap
|
||||||
|
CALL sbfsSetWord
|
||||||
|
|
||||||
|
sbfsRunKept:
|
||||||
|
; Past the end of the disk is the end of the walk. Otherwise the candidate goes to the
|
||||||
|
; thing that stopped it, which the next pass finds itself standing inside and steps over.
|
||||||
|
SETD.0 SbfsRunNext
|
||||||
|
SETD.2 SbfsDiskBlocks
|
||||||
|
CALL sbfsCompareWord
|
||||||
|
BNC sbfsRunDone
|
||||||
|
SETD.0 SbfsCandidate
|
||||||
|
SETD.2 SbfsRunNext
|
||||||
|
CALL sbfsSetWord
|
||||||
|
BRI sbfsRunPass
|
||||||
|
|
||||||
|
sbfsRunDone:
|
||||||
|
RSTA
|
||||||
|
RSTB
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
RET
|
||||||
|
|
||||||
|
sbfsRunFailed:
|
||||||
|
INIA 0x01
|
||||||
|
RSTB
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
RET
|
||||||
|
|
||||||
; Finds a run of SbfsWantBlocks free blocks and puts where it begins in SbfsFileStart.
|
; Finds a run of SbfsWantBlocks free blocks and puts where it begins in SbfsFileStart.
|
||||||
; Q is zero if there was room.
|
; Q is zero if there was room.
|
||||||
;
|
;
|
||||||
@@ -3691,6 +3963,24 @@ SbfsScratch1:
|
|||||||
SbfsPathWanted:
|
SbfsPathWanted:
|
||||||
0x00
|
0x00
|
||||||
|
|
||||||
|
; What every entry on the disk holds between them, and how many slots are filled. Worked out
|
||||||
|
; by sbfsSpace, which reads the whole directory table to do it.
|
||||||
|
; The longest run of free blocks, and the sweep that finds it: where it has got to ahead of
|
||||||
|
; the candidate, whether anything pushed the candidate this pass, and the gap being measured.
|
||||||
|
SbfsBiggest:
|
||||||
|
0x00 0x00
|
||||||
|
SbfsRunNext:
|
||||||
|
0x00 0x00
|
||||||
|
SbfsRunGap:
|
||||||
|
0x00 0x00
|
||||||
|
SbfsRunMoved:
|
||||||
|
0x00
|
||||||
|
|
||||||
|
SbfsUsedBlocks:
|
||||||
|
0x00 0x00
|
||||||
|
SbfsUsedEntries:
|
||||||
|
0x00 0x00
|
||||||
|
|
||||||
SbfsFileStart:
|
SbfsFileStart:
|
||||||
0x00 0x00
|
0x00 0x00
|
||||||
SbfsFileBlocks:
|
SbfsFileBlocks:
|
||||||
|
|||||||
@@ -433,6 +433,56 @@ static uint16_t countFree(const Directory *directory, const Superblock *super) {
|
|||||||
return (uint16_t)(super->diskBlocks - overhead - used);
|
return (uint16_t)(super->diskBlocks - overhead - used);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- The longest run of free blocks there is ----
|
||||||
|
//
|
||||||
|
// The number that says whether a file will fit. Files are laid down contiguously - first fit,
|
||||||
|
// with the directory itself as the map - so a disk with a thousand blocks free in ten
|
||||||
|
// scattered pieces refuses a file of two hundred, and the free total gives no hint of it.
|
||||||
|
//
|
||||||
|
// A sweep rather than a sort: a candidate walks the disk, each pass looking for the used
|
||||||
|
// extent nearest at or after it, and anything the candidate is standing inside pushes it to
|
||||||
|
// the far end and starts the pass again. That is what sbfs.asm does, because that machine has
|
||||||
|
// no sort - and the two implementations of this format are worth more agreeing than they are
|
||||||
|
// each being clever separately.
|
||||||
|
static uint16_t largestRun(const Directory *directory, const Superblock *super) {
|
||||||
|
const uint32_t firstData = (uint32_t)super->directoryStart + super->directoryBlocks;
|
||||||
|
uint32_t candidate = firstData;
|
||||||
|
uint32_t biggest = 0;
|
||||||
|
for (;;) {
|
||||||
|
uint32_t next = super->diskBlocks;
|
||||||
|
int moved = 0;
|
||||||
|
for (int i = 0; i < directory->entries; i++) {
|
||||||
|
const uint8_t *entry = entryAt(directory, i);
|
||||||
|
if (!entryInUse(entry) || entryIsDirectory(entry)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uint32_t start = readWord(entry + SBFS_ENTRY_START);
|
||||||
|
const uint32_t end = start + entryBlocksUsed(entry);
|
||||||
|
if (end <= candidate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (start <= candidate) {
|
||||||
|
candidate = end;
|
||||||
|
moved = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (start < next) {
|
||||||
|
next = start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (moved) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (next - candidate > biggest) {
|
||||||
|
biggest = next - candidate;
|
||||||
|
}
|
||||||
|
if (next >= super->diskBlocks) {
|
||||||
|
return (uint16_t)biggest;
|
||||||
|
}
|
||||||
|
candidate = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Commands ----
|
// ---- Commands ----
|
||||||
|
|
||||||
static int commandFormat(const char *path, uint16_t blocks, uint16_t directoryBlocks,
|
static int commandFormat(const char *path, uint16_t blocks, uint16_t directoryBlocks,
|
||||||
@@ -647,6 +697,15 @@ static int commandList(const char *path, const char *within) {
|
|||||||
}
|
}
|
||||||
printf(".\n");
|
printf(".\n");
|
||||||
|
|
||||||
|
// Said only when it is not all of it. On a disk that has only been appended to the two
|
||||||
|
// are the same number, and printing it twice would be noise on every healthy disk - a
|
||||||
|
// line that appears only when something is wrong is a line somebody reads.
|
||||||
|
uint16_t run = largestRun(&directory, &super);
|
||||||
|
if (run != counted) {
|
||||||
|
printf("The longest run of free blocks is %u, so nothing bigger than that will"
|
||||||
|
" fit.\n", run);
|
||||||
|
}
|
||||||
|
|
||||||
// A save that stopped between deleting the old entry and naming the new one. The
|
// A save that stopped between deleting the old entry and naming the new one. The
|
||||||
// bytes are all there under the temporary's name and one rename brings them back,
|
// bytes are all there under the temporary's name and one rename brings them back,
|
||||||
// which is the whole of the recovery this format offers - so the thing that matters
|
// which is the whole of the recovery this format offers - so the thing that matters
|
||||||
|
|||||||
@@ -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`. 217 tests, of which 155 run, 35
|
everything it printed against a file in `Tests/expected`. 218 tests, of which 156 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.
|
||||||
|
|
||||||
@@ -447,7 +447,7 @@ the console's line editing was in when it broke twice in two days.
|
|||||||
|
|
||||||
## Fixture Disks:
|
## Fixture Disks:
|
||||||
|
|
||||||
`Tests/makedisks.sh` builds 28 images with SplitDisk before anything runs, into
|
`Tests/makedisks.sh` builds 29 images with SplitDisk before anything runs, into
|
||||||
`Tests/build/disks`. **That is the point of them.** A SplitBit program reading one of these
|
`Tests/build/disks`. **That is the point of them.** A SplitBit program reading one of these
|
||||||
is being checked against a filesystem written by different code from the same written
|
is being checked against a filesystem written by different code from the same written
|
||||||
specification, rather than against itself.
|
specification, rather than against itself.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ made
|
|||||||
> cd Notes
|
> cd Notes
|
||||||
/Notes> dir
|
/Notes> dir
|
||||||
0 files
|
0 files
|
||||||
|
8 of 32 entries, 308 blocks free
|
||||||
/Notes> exit
|
/Notes> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ across.txt 700
|
|||||||
empty.txt 0
|
empty.txt 0
|
||||||
aName22CharactersLong! 22
|
aName22CharactersLong! 22
|
||||||
12 files
|
12 files
|
||||||
|
12 of 16 entries, 48 blocks free
|
||||||
>
|
>
|
||||||
> frobnicate
|
> frobnicate
|
||||||
I do not know: frobnicate
|
I do not know: frobnicate
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
CosmOS
|
CosmOS
|
||||||
> dir
|
> dir
|
||||||
0 files
|
0 files
|
||||||
|
0 of 16 entries, 125 blocks free
|
||||||
> mkdir Apps
|
> mkdir Apps
|
||||||
made
|
made
|
||||||
> mkdir Apps/Deep
|
> mkdir Apps/Deep
|
||||||
@@ -11,10 +12,12 @@ made
|
|||||||
Apps <dir>
|
Apps <dir>
|
||||||
Notes <dir>
|
Notes <dir>
|
||||||
0 files, 2 directories
|
0 files, 2 directories
|
||||||
|
3 of 16 entries, 125 blocks free
|
||||||
> cd Apps
|
> cd Apps
|
||||||
/Apps> dir
|
/Apps> dir
|
||||||
Deep <dir>
|
Deep <dir>
|
||||||
0 files, 1 directory
|
0 files, 1 directory
|
||||||
|
3 of 16 entries, 125 blocks free
|
||||||
/Apps> mkdir Deep
|
/Apps> mkdir Deep
|
||||||
cannot make that: check the path, the name, and whether it is taken
|
cannot make that: check the path, the name, and whether it is taken
|
||||||
/Apps> mkdir /Notes/Deep
|
/Apps> mkdir /Notes/Deep
|
||||||
@@ -23,6 +26,7 @@ made
|
|||||||
/Notes> dir
|
/Notes> dir
|
||||||
Deep <dir>
|
Deep <dir>
|
||||||
0 files, 1 directory
|
0 files, 1 directory
|
||||||
|
4 of 16 entries, 125 blocks free
|
||||||
/Notes> rmdir /Apps
|
/Notes> rmdir /Apps
|
||||||
cannot remove that: it must be a directory, and empty
|
cannot remove that: it must be a directory, and empty
|
||||||
/Notes> rmdir /Apps/Deep
|
/Notes> rmdir /Apps/Deep
|
||||||
@@ -44,6 +48,7 @@ removed
|
|||||||
removed
|
removed
|
||||||
> dir
|
> dir
|
||||||
0 files
|
0 files
|
||||||
|
0 of 16 entries, 125 blocks free
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ finished
|
|||||||
Claim.sbx 580
|
Claim.sbx 580
|
||||||
claim.dat 266
|
claim.dat 266
|
||||||
2 files
|
2 files
|
||||||
|
2 of 16 entries, 56 blocks free
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -51,6 +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
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ Apps <dir>
|
|||||||
A <dir>
|
A <dir>
|
||||||
B <dir>
|
B <dir>
|
||||||
0 files, 3 directories
|
0 files, 3 directories
|
||||||
|
9 of 32 entries, 240 blocks free
|
||||||
> cd /A
|
> cd /A
|
||||||
/A> dir
|
/A> dir
|
||||||
Say.sbx 53
|
Say.sbx 53
|
||||||
notes.txt 25
|
notes.txt 25
|
||||||
2 files
|
2 files
|
||||||
|
9 of 32 entries, 240 blocks free
|
||||||
/A> Type notes.txt
|
/A> Type notes.txt
|
||||||
these are the notes in A
|
these are the notes in A
|
||||||
finished
|
finished
|
||||||
@@ -29,6 +31,7 @@ Apps <dir>
|
|||||||
A <dir>
|
A <dir>
|
||||||
B <dir>
|
B <dir>
|
||||||
0 files, 3 directories
|
0 files, 3 directories
|
||||||
|
9 of 32 entries, 240 blocks free
|
||||||
> cd /B
|
> cd /B
|
||||||
/B> Type ../A/notes.txt
|
/B> Type ../A/notes.txt
|
||||||
these are the notes in A
|
these are the notes in A
|
||||||
@@ -50,6 +53,7 @@ Apps <dir>
|
|||||||
A <dir>
|
A <dir>
|
||||||
B <dir>
|
B <dir>
|
||||||
0 files, 3 directories
|
0 files, 3 directories
|
||||||
|
9 of 32 entries, 240 blocks free
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -40,6 +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
|
||||||
> drive 1
|
> drive 1
|
||||||
> dir
|
> dir
|
||||||
other.txt 28
|
other.txt 28
|
||||||
@@ -47,6 +48,7 @@ notes <dir>
|
|||||||
2things <dir>
|
2things <dir>
|
||||||
twoblocks.txt 560
|
twoblocks.txt 560
|
||||||
2 files, 2 directories
|
2 files, 2 directories
|
||||||
|
4 of 32 entries, 503 blocks free
|
||||||
> cd /notes
|
> cd /notes
|
||||||
/notes> drive 0
|
/notes> drive 0
|
||||||
> drive 1
|
> drive 1
|
||||||
|
|||||||
@@ -57,6 +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
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -3,17 +3,21 @@ CosmOS
|
|||||||
one.txt 13
|
one.txt 13
|
||||||
two.txt 14
|
two.txt 14
|
||||||
2 files
|
2 files
|
||||||
|
2 of 8 entries, 28 blocks free
|
||||||
> rename one.txt first.txt
|
> rename one.txt first.txt
|
||||||
renamed
|
renamed
|
||||||
> dir
|
> dir
|
||||||
first.txt 13
|
first.txt 13
|
||||||
two.txt 14
|
two.txt 14
|
||||||
2 files
|
2 files
|
||||||
|
2 of 8 entries, 28 blocks free
|
||||||
> delete first.txt
|
> delete first.txt
|
||||||
gone
|
gone
|
||||||
> dir
|
> dir
|
||||||
two.txt 14
|
two.txt 14
|
||||||
1 file
|
1 file
|
||||||
|
1 of 8 entries, 29 blocks free
|
||||||
|
the longest run is 28
|
||||||
> delete first.txt
|
> delete first.txt
|
||||||
no such file
|
no such file
|
||||||
> rename two.txt
|
> rename two.txt
|
||||||
|
|||||||
@@ -47,6 +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
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -47,6 +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
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ notes.sbx 21
|
|||||||
hello.sh 38
|
hello.sh 38
|
||||||
Apps <dir>
|
Apps <dir>
|
||||||
6 files, 1 directory
|
6 files, 1 directory
|
||||||
|
8 of 16 entries, 54 blocks free
|
||||||
> Greet a program with no extension
|
> Greet a program with no extension
|
||||||
it says: a program with no extension
|
it says: a program with no extension
|
||||||
finished
|
finished
|
||||||
|
|||||||
@@ -133,6 +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
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -52,6 +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
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ CosmOS
|
|||||||
> drive 1
|
> drive 1
|
||||||
> dir
|
> dir
|
||||||
0 files
|
0 files
|
||||||
|
0 of 128 entries, 2031 blocks free
|
||||||
> Copy 0:/Say.sbx 1:/Say.sbx
|
> Copy 0:/Say.sbx 1:/Say.sbx
|
||||||
copied
|
copied
|
||||||
finished
|
finished
|
||||||
> dir
|
> dir
|
||||||
Say.sbx 156
|
Say.sbx 156
|
||||||
1 file
|
1 file
|
||||||
|
1 of 128 entries, 2030 blocks free
|
||||||
> drive
|
> drive
|
||||||
1
|
1
|
||||||
> exit
|
> exit
|
||||||
|
|||||||
@@ -40,6 +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
|
||||||
> load
|
> load
|
||||||
load what?
|
load what?
|
||||||
> load nosuch.sbx
|
> load nosuch.sbx
|
||||||
|
|||||||
@@ -38,6 +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
|
||||||
> 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
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
CosmOS
|
||||||
|
> dir
|
||||||
|
greet.sbx 211
|
||||||
|
hello.sbx 53
|
||||||
|
Life.sbx 1396
|
||||||
|
Snake.sbx 2164
|
||||||
|
Keys.sbx 664
|
||||||
|
Say.sbx 156
|
||||||
|
Where.sbx 827
|
||||||
|
Break.sbx 149
|
||||||
|
Grid.sbx 571
|
||||||
|
Press.sbx 872
|
||||||
|
Mode.sbx 48
|
||||||
|
Flip.sbx 173
|
||||||
|
Sprite.sbx 442
|
||||||
|
Depth.sbx 672
|
||||||
|
Packages <dir>
|
||||||
|
Pad.sbx 264
|
||||||
|
Crash.sbx 632
|
||||||
|
vars.script 50
|
||||||
|
blocks.script 343
|
||||||
|
loops.script 272
|
||||||
|
tune.sbx 318
|
||||||
|
Play.sbx 2526
|
||||||
|
notes.txt 21
|
||||||
|
Apps <dir>
|
||||||
|
hi.script 121
|
||||||
|
bad.script 45
|
||||||
|
plain.script 24
|
||||||
|
cross.script 280
|
||||||
|
nonl.script 38
|
||||||
|
outer.script 376
|
||||||
|
inner.script 44
|
||||||
|
loop.script 35
|
||||||
|
hush.script 42
|
||||||
|
aloud.script 59
|
||||||
|
args.script 64
|
||||||
|
pass.script 20
|
||||||
|
holds.script 87
|
||||||
|
35 files, 2 directories
|
||||||
|
46 of 64 entries, 1893 blocks free
|
||||||
|
> drive 1
|
||||||
|
> dir
|
||||||
|
f1.txt 1500
|
||||||
|
f3.txt 1500
|
||||||
|
f5.txt 1500
|
||||||
|
f6.txt 1500
|
||||||
|
4 files
|
||||||
|
4 of 16 entries, 37 blocks free
|
||||||
|
the longest run is 25
|
||||||
|
> drive 0
|
||||||
|
> exit
|
||||||
|
halted
|
||||||
|
Execution halted.
|
||||||
|
[exit 0]
|
||||||
@@ -47,6 +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
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ CosmOS
|
|||||||
Apps <dir>
|
Apps <dir>
|
||||||
rooted.txt 21
|
rooted.txt 21
|
||||||
1 file, 1 directory
|
1 file, 1 directory
|
||||||
|
5 of 32 entries, 248 blocks free
|
||||||
> load /Apps/Say.sbx
|
> load /Apps/Say.sbx
|
||||||
loaded, starting at 5000
|
loaded, starting at 5000
|
||||||
> run the shallow one
|
> run the shallow one
|
||||||
@@ -43,6 +44,8 @@ gone
|
|||||||
Apps <dir>
|
Apps <dir>
|
||||||
rooted.txt 21
|
rooted.txt 21
|
||||||
1 file, 1 directory
|
1 file, 1 directory
|
||||||
|
4 of 32 entries, 249 blocks free
|
||||||
|
the longest run is 248
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ CosmOS
|
|||||||
Folder <dir>
|
Folder <dir>
|
||||||
Edit.sbx 2243
|
Edit.sbx 2243
|
||||||
1 file, 1 directory
|
1 file, 1 directory
|
||||||
|
3 of 16 entries, 244 blocks free
|
||||||
> load Edit.sbx
|
> load Edit.sbx
|
||||||
loaded, starting at 5000
|
loaded, starting at 5000
|
||||||
> run note.txt
|
> run note.txt
|
||||||
@@ -20,6 +21,7 @@ Folder <dir>
|
|||||||
Edit.sbx 2243
|
Edit.sbx 2243
|
||||||
note.txt 67
|
note.txt 67
|
||||||
2 files, 1 directory
|
2 files, 1 directory
|
||||||
|
4 of 16 entries, 243 blocks free
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ finished
|
|||||||
System <dir>
|
System <dir>
|
||||||
Apps <dir>
|
Apps <dir>
|
||||||
0 files, 2 directories
|
0 files, 2 directories
|
||||||
|
8 of 32 entries, 305 blocks free
|
||||||
> exit
|
> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ made
|
|||||||
> cd Notes
|
> cd Notes
|
||||||
/Notes> dir
|
/Notes> dir
|
||||||
0 files
|
0 files
|
||||||
|
7 of 32 entries, 309 blocks free
|
||||||
/Notes> exit
|
/Notes> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ made
|
|||||||
> cd Notes
|
> cd Notes
|
||||||
/Notes> dir
|
/Notes> dir
|
||||||
0 files
|
0 files
|
||||||
|
7 of 32 entries, 309 blocks free
|
||||||
/Notes> exit
|
/Notes> exit
|
||||||
halted
|
halted
|
||||||
Execution halted.
|
Execution halted.
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
dir
|
||||||
|
drive 1
|
||||||
|
dir
|
||||||
|
drive 0
|
||||||
|
exit
|
||||||
@@ -230,6 +230,26 @@ printf '#! script\nfor a in 1 2\n for b in x y\n echo $a$b\n end\nend\nset
|
|||||||
printf 'this is not a program' > notes.txt
|
printf 'this is not a program' > notes.txt
|
||||||
"$TOOL" put "$DISKS/cosmos.img" notes.txt >/dev/null
|
"$TOOL" put "$DISKS/cosmos.img" notes.txt >/dev/null
|
||||||
|
|
||||||
|
# ---- A disk with holes in it ----
|
||||||
|
#
|
||||||
|
# Six files laid down one after another, then the second and the fourth taken away. Files are
|
||||||
|
# laid down CONTIGUOUSLY on this format, so what is left is thirty seven free blocks in three
|
||||||
|
# pieces - two of six between the survivors and twenty five at the end - and the largest file
|
||||||
|
# that will fit is twenty five blocks, not thirty seven.
|
||||||
|
#
|
||||||
|
# That gap between "how much is free" and "how much will fit" is the whole reason a listing
|
||||||
|
# says the longest run, and it is invisible on every other disk here: a disk that has only
|
||||||
|
# been appended to has one gap, at the end, and the two numbers are the same. Deleting is what
|
||||||
|
# fragments a contiguous store, so a disk that has never had anything deleted from it cannot
|
||||||
|
# demonstrate any of this.
|
||||||
|
"$TOOL" format "$DISKS/holes.img" 64 2 >/dev/null
|
||||||
|
python3 -c "open('holey.txt','w').write('x' * 1500)"
|
||||||
|
for i in 1 2 3 4 5 6; do
|
||||||
|
"$TOOL" put "$DISKS/holes.img" holey.txt "f$i.txt" >/dev/null
|
||||||
|
done
|
||||||
|
"$TOOL" delete "$DISKS/holes.img" f2.txt >/dev/null
|
||||||
|
"$TOOL" delete "$DISKS/holes.img" f4.txt >/dev/null
|
||||||
|
|
||||||
# ---- A second disk, for the second drive ----
|
# ---- A second disk, for the second drive ----
|
||||||
#
|
#
|
||||||
# Deliberately nothing like the first: its own file and its own directory, so that a test
|
# Deliberately nothing like the first: its own file and its own directory, so that a test
|
||||||
|
|||||||
@@ -1033,6 +1033,24 @@ cosmosWhere | CosmOS/Source/cosmos.asm | run | cosmosWhe
|
|||||||
# a name twice. Then the same walk in /Apps, because a walk that only ever ran at the root
|
# a name twice. Then the same walk in /Apps, because a walk that only ever ran at the root
|
||||||
# would not have proved it walks where you are.
|
# would not have proved it walks where you are.
|
||||||
cosmosWalk | CosmOS/Source/cosmos.asm | run | cosmosWalk.in | 200000000 | disks/cosmos.img
|
cosmosWalk | CosmOS/Source/cosmos.asm | run | cosmosWalk.in | 200000000 | disks/cosmos.img
|
||||||
|
# ---- What a listing says about the disk it is listing ----
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# COUNTED RATHER THAN ASKED. The superblock keeps a free count and sbfs.asm calls it "a note
|
||||||
|
# rather than the truth" in three places; this reads the whole directory table instead. The
|
||||||
|
# first version added the blocks up as the LISTING walked past them, which cost nothing extra
|
||||||
|
# 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. Listing both directories
|
||||||
|
# here is what would say so again.
|
||||||
|
#
|
||||||
|
# And then a disk with holes in it, which is the case the longest run exists for: thirty seven
|
||||||
|
# blocks free in three pieces, so the largest file that will fit is twenty five. Said only
|
||||||
|
# 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
|
||||||
# ---- 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
|
||||||
|
|||||||
Reference in New Issue
Block a user