CosmOS knows about all four drives
A mounted disk is EIGHT BYTES - where its directory starts, how many
blocks it is, how big the disk is, and where you are on it. They now sit
together in the data segment, and changing drives is one copy out and one
copy in. The other three thousand lines of filesystem go on reading the
same four names they always have and never learn there is more than one
disk, which is the whole reason this was affordable.
The version is not in the record. It is checked at mount and thrown away,
because a version one disk's zero parent already reads as "in the root".
Every drive is mounted at boot: the controller says how many are plugged
in and each is tried in turn. One with nothing in it, or a disk this
cannot read, is left unmounted rather than stopping the others, so a
machine with a good disk in drive 0 and a blank in drive 1 starts.
'drive' says which one, 'drive 1' goes to another, and the working
directory goes with it - where you are on a disk is part of which disk you
are on. A drive the machine has not got is refused, and refused
differently from one that is there with nothing readable in it.
Three things the assembly caught me on, all the same misunderstanding of
what survives a call:
- OR reads A and B, and the bit came back from sbfsDriveBit in Q, which
RET does not disturb - but RET does put A back. The mounted mask never
got set and drive 0 was reported unmountable.
- MVQA then RSTA throws away the copy it just made, so doubling a bit
doubled nothing. SHL does it in one instruction, because A and B are
one register to it.
- There is no move from A to B. INB reads a port straight into B, which
is what the drive count comparison wanted.
run.sh takes more than one image now, separated by a plus, since the
machine has four drives and a test that could only name one could not
check any of this.
The buffer note is forgotten on a drive change and that is DELIBERATELY
kept although nothing can currently reach it: only the file read-ahead
consults it, a directory scan does not, and finding a file requires a
scan which overwrites the note on the way past. Two disks were built with
the same file at the same block to try to catch it and the answer was
right either way. Three instructions to hold an invariant rather than a
story about a bug - and the comment says so instead of claiming a fix.
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
b1538e0618
commit
5644c24113
@@ -234,6 +234,7 @@ CosmOS currently provides these built-in commands:
|
|||||||
| `do <script>` | Run the lines in a file as though they had been typed. See Scripts. |
|
| `do <script>` | Run the lines in a file as though they had been typed. See Scripts. |
|
||||||
| `echo [words]` | Say the rest of the line, or a blank line with nothing after it. |
|
| `echo [words]` | Say the rest of the line, or a blank line with nothing after it. |
|
||||||
| `clear` | Empty the screen. |
|
| `clear` | Empty the screen. |
|
||||||
|
| `drive [n]` | Say which disk the shell is on, or go to another. See Several Disks. |
|
||||||
| `cd [path]` | Go to a directory, or to the root with nothing after it. |
|
| `cd [path]` | Go to a directory, or to the root with nothing after it. |
|
||||||
| `mkdir <path>` | Make a directory. |
|
| `mkdir <path>` | Make a directory. |
|
||||||
| `rmdir <path>` | Remove one, if it is empty. |
|
| `rmdir <path>` | Remove one, if it is empty. |
|
||||||
@@ -797,6 +798,33 @@ An application may also include its own libraries or access hardware ports direc
|
|||||||
The services are an interface offered by the system, not the only way software is allowed
|
The services are an interface offered by the system, not the only way software is allowed
|
||||||
to use the computer.
|
to use the computer.
|
||||||
|
|
||||||
|
### Several Disks:
|
||||||
|
|
||||||
|
The machine has four drives behind one controller, and `drive` says which one the shell is
|
||||||
|
standing on. `drive 1` goes to another, and **the working directory goes with it** - where you
|
||||||
|
are on a disk is part of which disk you are on, not something the shell keeps on the side. Go
|
||||||
|
back and you are where you were.
|
||||||
|
|
||||||
|
Every drive is mounted at boot: the controller says how many are plugged in and each is tried
|
||||||
|
in turn. A drive with nothing in it, or a disk this cannot read, is left unmounted rather than
|
||||||
|
stopping the others, so a machine with a good disk in drive 0 and a blank in drive 1 starts
|
||||||
|
normally. `drive 1` then says there is nothing readable there, which is a different answer
|
||||||
|
from there being no such drive.
|
||||||
|
|
||||||
|
**What a mounted disk is, is eight bytes**: where its directory starts, how many blocks it is,
|
||||||
|
how big the disk is, and where you are on it. They sit together in the data segment on purpose,
|
||||||
|
because changing drives is one copy out and one copy in - and the other three thousand lines
|
||||||
|
of filesystem go on reading the same four names they always have and never learn that more
|
||||||
|
than one disk exists. That is the whole reason this was affordable.
|
||||||
|
|
||||||
|
The version is not among them. It is checked at mount and thrown away, because a version one
|
||||||
|
disk's zero parent already reads as "in the root", which is where all of its files are.
|
||||||
|
|
||||||
|
**Paths do not name a drive yet.** `drive 1` then a path is how you reach the other disk, so
|
||||||
|
copying between two of them is not possible in one command. A prefix like `1:/notes` is the
|
||||||
|
obvious next thing and it touches the path walker, which is why it is not in the same change
|
||||||
|
as the record.
|
||||||
|
|
||||||
### Bank Numbers Are One Namespace:
|
### Bank Numbers Are One Namespace:
|
||||||
|
|
||||||
A program that wants a device's memory registers it as a bank, and **bank numbers belong to
|
A program that wants a device's memory registers it as a bank, and **bank numbers belong to
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ boot:
|
|||||||
; Find out whether there is a filesystem to talk to. Doing this once at boot rather than
|
; Find out whether there is a filesystem to talk to. Doing this once at boot rather than
|
||||||
; once per command means a disk swapped underneath us is not noticed, which is honest
|
; once per command means a disk swapped underneath us is not noticed, which is honest
|
||||||
; for a machine whose disk is a file named on the command line.
|
; for a machine whose disk is a file named on the command line.
|
||||||
CALL sbfsMount
|
CALL sbfsMountAll
|
||||||
SETD.0 DiskReady
|
SETD.0 DiskReady
|
||||||
BNQ bootNoDisk
|
BNQ bootNoDisk
|
||||||
INIA 0x01
|
INIA 0x01
|
||||||
@@ -259,6 +259,11 @@ promptWhere:
|
|||||||
CALL textSame
|
CALL textSame
|
||||||
BRQ doRename
|
BRQ doRename
|
||||||
|
|
||||||
|
SETD.0 CommandLine
|
||||||
|
SETD.1 DriveName
|
||||||
|
CALL textSame
|
||||||
|
BRQ doDrive
|
||||||
|
|
||||||
SETD.0 CommandLine
|
SETD.0 CommandLine
|
||||||
SETD.1 ClearName
|
SETD.1 ClearName
|
||||||
CALL textSame
|
CALL textSame
|
||||||
@@ -1658,6 +1663,67 @@ fileComplain:
|
|||||||
CALL newLine
|
CALL newLine
|
||||||
BRI commandFailed
|
BRI commandFailed
|
||||||
|
|
||||||
|
; ---- drive ----
|
||||||
|
;
|
||||||
|
; Which disk the shell is standing on. With nothing after it, says which; with a number, goes
|
||||||
|
; there - and the working directory goes with it, because where you are on a disk is part of
|
||||||
|
; which disk you are on rather than something the shell keeps on the side.
|
||||||
|
doDrive:
|
||||||
|
SETD.1 TextRest
|
||||||
|
LDD.0.1
|
||||||
|
LDA.0
|
||||||
|
BRA driveSay
|
||||||
|
|
||||||
|
; One digit. Anything else is not a drive number, and the machines this is imitating never
|
||||||
|
; had ten drives either.
|
||||||
|
INIB 0d48
|
||||||
|
CCF
|
||||||
|
SUB
|
||||||
|
MVQA
|
||||||
|
INIB 0d10
|
||||||
|
CCF
|
||||||
|
SUB
|
||||||
|
BNC driveNoSuch ; Ten or more, so it was not a digit at all.
|
||||||
|
|
||||||
|
; Is there such a drive on this machine?
|
||||||
|
SETD.1 DriveWanted
|
||||||
|
STA.1
|
||||||
|
INB 0x25 ; How many drives, straight into B: there is no move from A to it.
|
||||||
|
LDA.1
|
||||||
|
CCF
|
||||||
|
SUB ; Borrows when the wanted one is inside the count.
|
||||||
|
BNC driveNoSuch
|
||||||
|
|
||||||
|
; And is there anything readable in it? A drive with no disk is a real drive and an empty
|
||||||
|
; one, so this is a different answer from "there is no such drive".
|
||||||
|
CALL sbfsDriveBit
|
||||||
|
MVQA
|
||||||
|
SETD.1 SbfsMounted
|
||||||
|
LDB.1
|
||||||
|
AND
|
||||||
|
BRQ driveNotReadable
|
||||||
|
|
||||||
|
SETD.1 DriveWanted
|
||||||
|
LDA.1
|
||||||
|
CALL sbfsUse
|
||||||
|
BRI prompt
|
||||||
|
|
||||||
|
driveSay:
|
||||||
|
INA 0x24
|
||||||
|
INIB 0d48
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
OUTQ 0x00
|
||||||
|
CALL newLine
|
||||||
|
BRI prompt
|
||||||
|
|
||||||
|
driveNoSuch:
|
||||||
|
SETD.0 DriveNoSuch
|
||||||
|
BRI fileComplain
|
||||||
|
driveNotReadable:
|
||||||
|
SETD.0 DriveNotReadable
|
||||||
|
BRI fileComplain
|
||||||
|
|
||||||
; ---- clear ----
|
; ---- clear ----
|
||||||
;
|
;
|
||||||
; The console has done this since before there was a screen to do it on: writing 1 to the
|
; The console has done this since before there was a screen to do it on: writing 1 to the
|
||||||
@@ -3703,6 +3769,8 @@ PromptText:
|
|||||||
; not what went wrong, and what went wrong has already been said in words.
|
; not what went wrong, and what went wrong has already been said in words.
|
||||||
LineFailed:
|
LineFailed:
|
||||||
0x00
|
0x00
|
||||||
|
DriveWanted:
|
||||||
|
0x00
|
||||||
|
|
||||||
|
|
||||||
LastStatus:
|
LastStatus:
|
||||||
@@ -3721,6 +3789,10 @@ ScriptNotOne:
|
|||||||
"do: that is not a script - it wants #! on the first line"
|
"do: that is not a script - it wants #! on the first line"
|
||||||
ScriptTooDeep:
|
ScriptTooDeep:
|
||||||
"do: scripts are only four deep"
|
"do: scripts are only four deep"
|
||||||
|
DriveNoSuch:
|
||||||
|
"drive: this machine has no such drive"
|
||||||
|
DriveNotReadable:
|
||||||
|
"drive: nothing this can read is in that drive"
|
||||||
StartupName:
|
StartupName:
|
||||||
"/System/Boot/startup.sh"
|
"/System/Boot/startup.sh"
|
||||||
StartupNotOne:
|
StartupNotOne:
|
||||||
@@ -3916,6 +3988,8 @@ EchoName:
|
|||||||
"echo"
|
"echo"
|
||||||
ClearName:
|
ClearName:
|
||||||
"clear"
|
"clear"
|
||||||
|
DriveName:
|
||||||
|
"drive"
|
||||||
LoadName:
|
LoadName:
|
||||||
"load"
|
"load"
|
||||||
RunName:
|
RunName:
|
||||||
|
|||||||
@@ -136,6 +136,232 @@ sbfsMountTooBig:
|
|||||||
ADD ; Q is not zero: not a disk this will mount.
|
ADD ; Q is not zero: not a disk this will mount.
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
; ---- Every drive the machine has ----
|
||||||
|
;
|
||||||
|
; Asked for rather than assumed: the controller says how many are plugged in, and each is
|
||||||
|
; selected and mounted in turn. A drive with nothing in it, or a disk this cannot read, is
|
||||||
|
; left unmounted rather than stopping the others - a machine with a good disk in drive 0 and
|
||||||
|
; a blank in drive 1 should start.
|
||||||
|
;
|
||||||
|
; Q is zero if drive 0 mounted, because that is the one the system came off and the one the
|
||||||
|
; shell will be standing in when it gets a prompt.
|
||||||
|
sbfsMountAll:
|
||||||
|
RSTA
|
||||||
|
SETD.1 SbfsMounted
|
||||||
|
STA.1
|
||||||
|
SETD.1 SbfsDrive
|
||||||
|
STA.1
|
||||||
|
OUTA 0x24 ; Drive 0, whatever the controller was left on.
|
||||||
|
|
||||||
|
; The live record belongs to nobody yet, so every slot starts empty and a drive that fails
|
||||||
|
; to mount keeps an empty one.
|
||||||
|
SETD.0 SbfsMountTable
|
||||||
|
INIB 0d32
|
||||||
|
sbfsMountClear:
|
||||||
|
RSTA
|
||||||
|
STA.0
|
||||||
|
INCD.0
|
||||||
|
DECB
|
||||||
|
BNB sbfsMountClear
|
||||||
|
|
||||||
|
INA 0x25
|
||||||
|
SETD.1 SbfsDriveCount
|
||||||
|
STA.1
|
||||||
|
RSTA
|
||||||
|
SETD.1 SbfsDriveAt
|
||||||
|
STA.1
|
||||||
|
|
||||||
|
sbfsMountEach:
|
||||||
|
SETD.1 SbfsDriveAt
|
||||||
|
LDA.1
|
||||||
|
SETD.1 SbfsDriveCount
|
||||||
|
LDB.1
|
||||||
|
CCF
|
||||||
|
SUB
|
||||||
|
BRQ sbfsMountAllDone ; Past the last one.
|
||||||
|
|
||||||
|
; Selected directly rather than through sbfsUse: there is nothing in the live record worth
|
||||||
|
; putting back yet, and sbfsUse would copy eight bytes of nothing into a slot.
|
||||||
|
SETD.1 SbfsDriveAt
|
||||||
|
LDA.1
|
||||||
|
OUTA 0x24
|
||||||
|
SETD.1 SbfsDrive
|
||||||
|
STA.1
|
||||||
|
RSTA
|
||||||
|
SETD.1 SbfsBufferKnown
|
||||||
|
STA.1
|
||||||
|
|
||||||
|
CALL sbfsMount
|
||||||
|
BNQ sbfsMountNext ; Nothing readable in that drive, and its slot stays empty.
|
||||||
|
|
||||||
|
; Mounted: remember which, and put the live record where it belongs.
|
||||||
|
SETD.1 SbfsDriveAt
|
||||||
|
LDA.1
|
||||||
|
CALL sbfsDriveBit
|
||||||
|
MVQA ; The bit is in Q, and RET put the old A back. OR reads A and B.
|
||||||
|
SETD.1 SbfsMounted
|
||||||
|
LDB.1
|
||||||
|
OR
|
||||||
|
STQ.1
|
||||||
|
|
||||||
|
SETD.1 SbfsDriveAt
|
||||||
|
LDA.1
|
||||||
|
CALL sbfsSlotAt
|
||||||
|
PSHD.3
|
||||||
|
POPD.1
|
||||||
|
SETD.0 SbfsMountLive
|
||||||
|
CALL sbfsCopyMount
|
||||||
|
|
||||||
|
sbfsMountNext:
|
||||||
|
SETD.1 SbfsDriveAt
|
||||||
|
LDA.1
|
||||||
|
INCA
|
||||||
|
STA.1
|
||||||
|
BRI sbfsMountEach
|
||||||
|
|
||||||
|
sbfsMountAllDone:
|
||||||
|
; Back to drive 0 and its record, which is where a shell starts. A stays nought throughout,
|
||||||
|
; which is the drive, the note on the buffer, and the slot to fetch.
|
||||||
|
RSTA
|
||||||
|
OUTA 0x24
|
||||||
|
SETD.1 SbfsDrive
|
||||||
|
STA.1
|
||||||
|
SETD.1 SbfsBufferKnown
|
||||||
|
STA.1
|
||||||
|
CALL sbfsSlotAt
|
||||||
|
PSHD.3
|
||||||
|
POPD.0
|
||||||
|
SETD.1 SbfsMountLive
|
||||||
|
CALL sbfsCopyMount
|
||||||
|
|
||||||
|
; Is drive 0 one of the ones that mounted?
|
||||||
|
INIA 0x01
|
||||||
|
SETD.1 SbfsMounted
|
||||||
|
LDB.1
|
||||||
|
AND
|
||||||
|
BRQ sbfsMountAllNone
|
||||||
|
RSTA
|
||||||
|
RSTB
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
RET
|
||||||
|
sbfsMountAllNone:
|
||||||
|
RSTA
|
||||||
|
INIB 0d1
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
RET
|
||||||
|
|
||||||
|
; A = a drive number. Q is the bit that stands for it in SbfsMounted.
|
||||||
|
sbfsDriveBit:
|
||||||
|
INIB 0x01
|
||||||
|
sbfsDriveBitStep:
|
||||||
|
BRA sbfsDriveBitDone
|
||||||
|
PSHA
|
||||||
|
RSTA
|
||||||
|
SHL ; A and B are one register to SHL, so with A nought this is B
|
||||||
|
POPA ; doubled - which for four drives never reaches the top.
|
||||||
|
DECA
|
||||||
|
BRI sbfsDriveBitStep
|
||||||
|
sbfsDriveBitDone:
|
||||||
|
RSTA
|
||||||
|
CCF
|
||||||
|
ADD ; Q = B, which is the bit.
|
||||||
|
RET
|
||||||
|
|
||||||
|
; ---- Changing which disk is the disk ----
|
||||||
|
;
|
||||||
|
; A holds the drive. Q is zero if it is now the one in use.
|
||||||
|
;
|
||||||
|
; The eight live bytes go back to the drive they belong to and the wanted drive's come in.
|
||||||
|
; Everything below this line in the file goes on reading the same four names it always has and
|
||||||
|
; never learns that more than one disk exists - which is the whole of why this is affordable.
|
||||||
|
;
|
||||||
|
; The buffer is FORGOTTEN, and that is not tidiness. The controller has one buffer shared by
|
||||||
|
; every drive, so the note of which block is in it is wrong the moment the drive changes.
|
||||||
|
; Leaving it would mean the next read of that block number quietly skipping the disk and
|
||||||
|
; handing back the other drive's data.
|
||||||
|
sbfsUse:
|
||||||
|
SETD.1 SbfsDrive
|
||||||
|
LDB.1
|
||||||
|
CCF
|
||||||
|
SUB
|
||||||
|
BRQ sbfsUseAlready ; Already there, and swapping would be a long way round to nothing.
|
||||||
|
|
||||||
|
PSHA ; The drive that was asked for, kept across the copying.
|
||||||
|
|
||||||
|
; The live record back to the drive it belongs to. DP1 is still SbfsDrive, from the
|
||||||
|
; comparison above.
|
||||||
|
LDA.1
|
||||||
|
CALL sbfsSlotAt
|
||||||
|
PSHD.3
|
||||||
|
POPD.1
|
||||||
|
SETD.0 SbfsMountLive
|
||||||
|
CALL sbfsCopyMount
|
||||||
|
|
||||||
|
; And the wanted drive's record into the live eight.
|
||||||
|
POPA
|
||||||
|
PSHA
|
||||||
|
CALL sbfsSlotAt
|
||||||
|
PSHD.3
|
||||||
|
POPD.0
|
||||||
|
SETD.1 SbfsMountLive
|
||||||
|
CALL sbfsCopyMount
|
||||||
|
|
||||||
|
POPA
|
||||||
|
SETD.1 SbfsDrive
|
||||||
|
STA.1
|
||||||
|
OUTA 0x24
|
||||||
|
|
||||||
|
; ---- The note on the buffer belongs to the drive that is leaving ----
|
||||||
|
;
|
||||||
|
; One buffer serves every drive, so "block 31 is in the buffer" stops being true the moment
|
||||||
|
; the drive changes, and a read of block 31 that trusted it would hand back the other disk.
|
||||||
|
;
|
||||||
|
; IT CANNOT CURRENTLY BE REACHED, and that is worth writing down rather than leaving as an
|
||||||
|
; implied claim. Only the file read-ahead consults the note - a directory scan deliberately
|
||||||
|
; does not - and finding a file requires a scan, which overwrites the note on the way past.
|
||||||
|
; Two disks were built with the same file at the same block to try to catch it and the
|
||||||
|
; answer was right either way.
|
||||||
|
;
|
||||||
|
; Kept because it is three instructions and it holds an invariant rather than patching a
|
||||||
|
; symptom: the note describes the selected drive. The day something reads two files without
|
||||||
|
; a directory between them, this is already true instead of being a bug with a story.
|
||||||
|
RSTA
|
||||||
|
SETD.1 SbfsBufferKnown
|
||||||
|
STA.1
|
||||||
|
|
||||||
|
sbfsUseAlready:
|
||||||
|
RSTA
|
||||||
|
RSTB
|
||||||
|
CCF
|
||||||
|
ADD
|
||||||
|
RET
|
||||||
|
|
||||||
|
; DP3 = the eight bytes belonging to the drive in A. Stepped rather than multiplied, because
|
||||||
|
; this machine cannot multiply and there are at most three steps.
|
||||||
|
sbfsSlotAt:
|
||||||
|
SETD.3 SbfsMountTable
|
||||||
|
BRA sbfsSlotThere
|
||||||
|
sbfsSlotStep:
|
||||||
|
DPUP.3 0d08
|
||||||
|
DECA
|
||||||
|
BNA sbfsSlotStep
|
||||||
|
sbfsSlotThere:
|
||||||
|
RET
|
||||||
|
|
||||||
|
; Eight bytes, DP0 to DP1.
|
||||||
|
sbfsCopyMount:
|
||||||
|
INIB 0d8
|
||||||
|
sbfsCopyMountByte:
|
||||||
|
LDA.0
|
||||||
|
STA.1
|
||||||
|
INCD.0
|
||||||
|
INCD.1
|
||||||
|
DECB
|
||||||
|
BNB sbfsCopyMountByte
|
||||||
|
RET
|
||||||
|
|
||||||
; ---- Finding something by path ----
|
; ---- Finding something by path ----
|
||||||
;
|
;
|
||||||
; DP0 points at a path ending in a zero byte: names with '/' between them. A path that
|
; DP0 points at a path ending in a zero byte: names with '/' between them. A path that
|
||||||
@@ -3118,10 +3344,44 @@ SbfsStateWants:
|
|||||||
SbfsMagic:
|
SbfsMagic:
|
||||||
"SBFS"
|
"SBFS"
|
||||||
|
|
||||||
|
; ---- Which disk this is, in eight bytes ----
|
||||||
|
;
|
||||||
|
; THE ORDER AND THE ADJACENCY ARE LOAD BEARING. These four are everything that distinguishes
|
||||||
|
; one mounted disk from another, and they are together so that changing drives is one copy
|
||||||
|
; out and one copy in. Nothing else may be put between them.
|
||||||
|
;
|
||||||
|
; Everything else in this file is either a constant or scratch for the operation being done
|
||||||
|
; now, and only one operation is ever being done - which is why a filesystem of 3,300 lines
|
||||||
|
; needs an eight byte record to know more than one disk. The rest never learns there is more
|
||||||
|
; than one.
|
||||||
|
;
|
||||||
|
; The version is not here. It is checked at mount and thrown away, because a version one
|
||||||
|
; disk's zero parent already reads as "in the root", which is where all of its files are.
|
||||||
|
SbfsMountLive:
|
||||||
SbfsDirStart:
|
SbfsDirStart:
|
||||||
0x00 0x00
|
0x00 0x00
|
||||||
SbfsDirBlocks:
|
SbfsDirBlocks:
|
||||||
0x00 0x00
|
0x00 0x00
|
||||||
|
SbfsDiskBlocks:
|
||||||
|
0x00 0x00
|
||||||
|
SbfsCwd:
|
||||||
|
0x00 0x00
|
||||||
|
|
||||||
|
; One record a drive, and the live eight above are whichever is selected. Four, because the
|
||||||
|
; controller has four.
|
||||||
|
SbfsMountTable:
|
||||||
|
#Reserve 0d32
|
||||||
|
; Which drive the live record belongs to, and which drives were found to have a disk on them
|
||||||
|
; that this can read. A bit a drive, so drive n is bit n.
|
||||||
|
SbfsDrive:
|
||||||
|
0x00
|
||||||
|
SbfsMounted:
|
||||||
|
0x00
|
||||||
|
SbfsDriveCount:
|
||||||
|
0x00
|
||||||
|
SbfsDriveAt:
|
||||||
|
0x00
|
||||||
|
|
||||||
SbfsFileStart:
|
SbfsFileStart:
|
||||||
0x00 0x00
|
0x00 0x00
|
||||||
SbfsFileBlocks:
|
SbfsFileBlocks:
|
||||||
@@ -3130,8 +3390,6 @@ SbfsFileTail:
|
|||||||
0x00
|
0x00
|
||||||
SbfsBlock:
|
SbfsBlock:
|
||||||
0x00 0x00
|
0x00 0x00
|
||||||
SbfsDiskBlocks:
|
|
||||||
0x00 0x00
|
|
||||||
SbfsWantBlocks:
|
SbfsWantBlocks:
|
||||||
0x00 0x00
|
0x00 0x00
|
||||||
SbfsCandidate:
|
SbfsCandidate:
|
||||||
@@ -3165,8 +3423,6 @@ SbfsLeft:
|
|||||||
; thing that resolves a path is here. Keeping it in the shell would mean either handing it
|
; thing that resolves a path is here. Keeping it in the shell would mean either handing it
|
||||||
; down on every call or having the shell paste it onto the front of every name, and the
|
; down on every call or having the shell paste it onto the front of every name, and the
|
||||||
; second of those is how a name that is already absolute gets ruined.
|
; second of those is how a name that is already absolute gets ruined.
|
||||||
SbfsCwd:
|
|
||||||
0x00 0x00
|
|
||||||
|
|
||||||
; ---- What walking a path keeps ----
|
; ---- What walking a path keeps ----
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -79,7 +79,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`. 179 tests, of which 117 run, 35
|
everything it printed against a file in `Tests/expected`. 180 tests, of which 118 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.
|
||||||
|
|
||||||
@@ -365,7 +365,7 @@ presenting frames. It tests the console, which is where the logic is.
|
|||||||
|
|
||||||
## Fixture Disks:
|
## Fixture Disks:
|
||||||
|
|
||||||
`Tests/makedisks.sh` builds 26 images with SplitDisk before anything runs, into
|
`Tests/makedisks.sh` builds 27 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.
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
CosmOS
|
||||||
|
> 0
|
||||||
|
> greet.sbx 211
|
||||||
|
hello.sbx 53
|
||||||
|
Life.sbx 1396
|
||||||
|
Snake.sbx 2164
|
||||||
|
Keys.sbx 664
|
||||||
|
Say.sbx 156
|
||||||
|
Break.sbx 149
|
||||||
|
Grid.sbx 543
|
||||||
|
notes.txt 21
|
||||||
|
hi.script 121
|
||||||
|
bad.script 45
|
||||||
|
plain.script 24
|
||||||
|
cross.script 280
|
||||||
|
nonl.script 38
|
||||||
|
outer.script 376
|
||||||
|
inner.script 44
|
||||||
|
loop.script 35
|
||||||
|
17 files
|
||||||
|
> > other.txt 28
|
||||||
|
notes <dir>
|
||||||
|
1 file, 1 directory
|
||||||
|
> /notes> > /notes> > /notes> drive: this machine has no such drive
|
||||||
|
/notes> halted
|
||||||
|
Execution halted.
|
||||||
|
[exit 0]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
drive
|
||||||
|
dir
|
||||||
|
drive 1
|
||||||
|
dir
|
||||||
|
cd /notes
|
||||||
|
drive 0
|
||||||
|
drive 1
|
||||||
|
drive 0
|
||||||
|
drive 1
|
||||||
|
drive 3
|
||||||
|
exit
|
||||||
@@ -115,6 +115,16 @@ for i in 1 2 3 4 5 6 7 8; do "$TOOL" put "$DISKS/sbfs.img" "filler$i.txt" >/dev/
|
|||||||
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 second disk, for the second drive ----
|
||||||
|
#
|
||||||
|
# Deliberately nothing like the first: its own file and its own directory, so that a test
|
||||||
|
# looking at one cannot be looking at the other by accident. Small, because what is on it
|
||||||
|
# matters and how much does not.
|
||||||
|
"$TOOL" format "$DISKS/other.img" 512 4 >/dev/null
|
||||||
|
printf 'this lives on the other disk' > other.txt
|
||||||
|
"$TOOL" put "$DISKS/other.img" other.txt >/dev/null
|
||||||
|
"$TOOL" mkdir "$DISKS/other.img" /notes >/dev/null
|
||||||
|
|
||||||
# ---- Scripts, including the ones that are meant to go wrong ----
|
# ---- Scripts, including the ones that are meant to go wrong ----
|
||||||
#
|
#
|
||||||
# Built here rather than committed, because two of them are about BYTE POSITIONS and a
|
# Built here rather than committed, because two of them are about BYTE POSITIONS and a
|
||||||
|
|||||||
@@ -808,6 +808,18 @@ cosmosGrid | CosmOS/Source/cosmos.asm | run | cosmosGri
|
|||||||
# drive that exists with nothing in it - selectable, and failing to read, like an empty
|
# drive that exists with nothing in it - selectable, and failing to read, like an empty
|
||||||
# floppy drive.
|
# floppy drive.
|
||||||
driveSelectTest | testPrograms/driveSelectTest.asm | run | - | 200000 | disks/sbfs.img
|
driveSelectTest | testPrograms/driveSelectTest.asm | run | - | 200000 | disks/sbfs.img
|
||||||
|
# ---- Two disks ----
|
||||||
|
#
|
||||||
|
# The first thing on this machine to have more than one. drive says which, and going to the
|
||||||
|
# other shows a disk with nothing in common with the first - so a listing that looked right
|
||||||
|
# could not have come from the wrong one.
|
||||||
|
#
|
||||||
|
# THE WORKING DIRECTORY GOES WITH THE DRIVE, which is the whole point of the mount record.
|
||||||
|
# Drive 1 is left standing in /notes and drive 0 at the root, and the prompt says which on
|
||||||
|
# every trip between them - three times each way, because a record that is copied one way
|
||||||
|
# and not the other would still look right once. A drive this machine has not got is
|
||||||
|
# refused.
|
||||||
|
cosmosDrives | CosmOS/Source/cosmos.asm | run | cosmosDrives.in | 60000000 | disks/cosmos.img+disks/other.img
|
||||||
printDecimalTest | testPrograms/printDecimalTest.asm | xfail | - | -
|
printDecimalTest | testPrograms/printDecimalTest.asm | xfail | - | -
|
||||||
printDigitTest | testPrograms/printDigitTest.asm | xfail | - | -
|
printDigitTest | testPrograms/printDigitTest.asm | xfail | - | -
|
||||||
printHexTest | testPrograms/printHexTest.asm | xfail | - | -
|
printHexTest | testPrograms/printHexTest.asm | xfail | - | -
|
||||||
|
|||||||
+10
-3
@@ -251,10 +251,16 @@ while IFS='|' read -r name src mode stdin limit disk keys; do
|
|||||||
# A disk starts fresh for every run, so a test cannot pass because of what a
|
# A disk starts fresh for every run, so a test cannot pass because of what a
|
||||||
# previous one left lying on it. The emulator makes the image if it is
|
# previous one left lying on it. The emulator makes the image if it is
|
||||||
# missing, which is what removing it first arranges for.
|
# missing, which is what removing it first arranges for.
|
||||||
if [ "$disk" != "-" ]; then
|
# ---- More than one, separated by a plus ----
|
||||||
|
#
|
||||||
|
# The machine has four drives, so a test may name up to four images and they
|
||||||
|
# become drives 0 upwards in the order written. Each keeps its own :ro and @N,
|
||||||
|
# because those are properties of a disk rather than of the machine.
|
||||||
|
for onedisk in ${disk//+/ }; do
|
||||||
|
if [ "$onedisk" != "-" ]; then
|
||||||
# A trailing :ro attaches the image write protected, so that a test can
|
# A trailing :ro attaches the image write protected, so that a test can
|
||||||
# check the device bars writes rather than the filesystem asking nicely.
|
# check the device bars writes rather than the filesystem asking nicely.
|
||||||
DISKFILE="${disk%:ro}"
|
DISKFILE="${onedisk%:ro}"
|
||||||
# And a trailing @N gives the disk a latency, so that a test can check the
|
# And a trailing @N gives the disk a latency, so that a test can check the
|
||||||
# filesystem waits for it. Every other test runs with the answer there
|
# filesystem waits for it. Every other test runs with the answer there
|
||||||
# before the next instruction, which is the one condition under which not
|
# before the next instruction, which is the one condition under which not
|
||||||
@@ -273,8 +279,9 @@ while IFS='|' read -r name src mode stdin limit disk keys; do
|
|||||||
esac
|
esac
|
||||||
EMUARGS+=(--disk "$BUILD/$DISKFILE")
|
EMUARGS+=(--disk "$BUILD/$DISKFILE")
|
||||||
[ -n "$DISKWAIT" ] && EMUARGS+=(--disk-cycles "$DISKWAIT")
|
[ -n "$DISKWAIT" ] && EMUARGS+=(--disk-cycles "$DISKWAIT")
|
||||||
case "$disk" in *:ro) EMUARGS+=(--write-protect) ;; esac
|
case "$onedisk" in *:ro) EMUARGS+=(--write-protect) ;; esac
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
# A rom test names no image. The emulator then shadows its built in stage
|
# A rom test names no image. The emulator then shadows its built in stage
|
||||||
# one into Program Memory and reads the disk for everything else, which is
|
# one into Program Memory and reads the disk for everything else, which is
|
||||||
# what a machine with no debugger attached does.
|
# what a machine with no debugger attached does.
|
||||||
|
|||||||
Reference in New Issue
Block a user