Fixed assembler bug that caused crash on IR array resize. Added line editor app.
This commit is contained in:
@@ -109,6 +109,16 @@ prompt:
|
||||
CALL textSame
|
||||
BRQ doDump
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 DeleteName
|
||||
CALL textSame
|
||||
BRQ doDelete
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 RenameName
|
||||
CALL textSame
|
||||
BRQ doRename
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 HelpName
|
||||
CALL textSame
|
||||
@@ -195,7 +205,16 @@ dirDone:
|
||||
SETD.0 DirSeen
|
||||
LDA.0
|
||||
CALL printByteDecimal
|
||||
; One file is not one files. Cheap to get right and it reads as carelessness otherwise.
|
||||
SETD.0 DirSeen
|
||||
LDA.0
|
||||
DECA
|
||||
BRA dirOne
|
||||
SETD.0 FilesText
|
||||
BRI dirCount
|
||||
dirOne:
|
||||
SETD.0 FileText
|
||||
dirCount:
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI prompt
|
||||
@@ -470,6 +489,84 @@ loadComplain:
|
||||
CALL newLine
|
||||
BRI prompt
|
||||
|
||||
; ---- delete and rename ----
|
||||
;
|
||||
; The two things a disk needs that reading and writing do not provide, and the two that
|
||||
; anything editing a document will want from the shell as well as from a program. Deleting
|
||||
; frees an entry and its blocks; renaming changes twenty two bytes and moves nothing.
|
||||
|
||||
doDelete:
|
||||
SETD.0 DiskReady
|
||||
LDA.0
|
||||
BRA fileNoDisk
|
||||
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
LDA.0
|
||||
BRA deleteWhat
|
||||
|
||||
CALL sbfsDelete
|
||||
BNQ deleteFailed
|
||||
SETD.0 Deleted
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI prompt
|
||||
|
||||
deleteWhat:
|
||||
SETD.0 DeleteWhat
|
||||
BRI fileComplain
|
||||
deleteFailed:
|
||||
SETD.0 NoSuchFile
|
||||
BRI fileComplain
|
||||
|
||||
doRename:
|
||||
SETD.0 DiskReady
|
||||
LDA.0
|
||||
BRA fileNoDisk
|
||||
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
LDA.0
|
||||
BRA renameWhat
|
||||
|
||||
; Two names, so the rest of the line is split again. textSplit writes a zero over the
|
||||
; space it cuts at, so what was one string becomes two without anything being copied.
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
SETD.1 RenameFrom
|
||||
STD.0.1
|
||||
CALL textSplit
|
||||
|
||||
SETD.1 TextRest
|
||||
LDD.1.1
|
||||
LDA.1
|
||||
BRA renameWhat ; Only one name was given, and this needs both.
|
||||
|
||||
SETD.2 RenameFrom
|
||||
LDD.0.2
|
||||
CALL sbfsRename
|
||||
BNQ renameFailed
|
||||
SETD.0 Renamed
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI prompt
|
||||
|
||||
renameWhat:
|
||||
SETD.0 RenameWhat
|
||||
BRI fileComplain
|
||||
renameFailed:
|
||||
; Either there is no such file or the new name is already taken. Which of the two is not
|
||||
; worth another message: both mean the disk does not have room for that name to move.
|
||||
SETD.0 RenameNo
|
||||
BRI fileComplain
|
||||
|
||||
fileNoDisk:
|
||||
SETD.0 NoDisk
|
||||
fileComplain:
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI prompt
|
||||
|
||||
; ---- run ----
|
||||
;
|
||||
; Hands the machine to whatever was loaded. Where the Stack is now is written down first,
|
||||
@@ -485,6 +582,15 @@ doRun:
|
||||
SETD.1 SystemStack
|
||||
STD.0.1
|
||||
|
||||
; Whatever followed the word "run" is kept where the program can ask for it. Copied
|
||||
; rather than pointed at, because what it is pointing at is the line the shell typed
|
||||
; into, and a program is entitled to outlive the shell's opinion of that.
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
SETD.1 RunArgument
|
||||
INIB 0d64
|
||||
CALL copyText
|
||||
|
||||
CALL installVectors
|
||||
|
||||
; The entry address is a number until BRD makes it a place. DP3 is the one to build it
|
||||
@@ -499,6 +605,26 @@ runNothing:
|
||||
CALL newLine
|
||||
BRI prompt
|
||||
|
||||
; DP0 is a string, DP1 is where it should go, and B is how much room there is counting
|
||||
; the zero on the end. What does not fit is left behind, and what is written is a string
|
||||
; either way.
|
||||
copyText:
|
||||
BRB copyTextDone ; No room at all, so nothing is written, not even the zero.
|
||||
copyTextLoop:
|
||||
DECB
|
||||
BRB copyTextEnd ; Only room for the terminator now.
|
||||
LDA.0
|
||||
STA.1
|
||||
BRA copyTextDone
|
||||
INCD.0
|
||||
INCD.1
|
||||
BRI copyTextLoop
|
||||
copyTextEnd:
|
||||
RSTA
|
||||
STA.1
|
||||
copyTextDone:
|
||||
RET
|
||||
|
||||
; ---- Putting a program's vectors in, and taking them out again ----
|
||||
;
|
||||
; The vector table lives in Program Memory, which no instruction can write, so both of
|
||||
@@ -635,6 +761,20 @@ handleReadLine:
|
||||
CALL readLine
|
||||
RETI
|
||||
|
||||
; What the program was asked to work on. DP0 says where to put it and B how much room
|
||||
; there is, counting the zero on the end, which is the same bargain readLine offers.
|
||||
;
|
||||
; Being asked for rather than left at an agreed address is deliberate. The two sides of
|
||||
; this already have to agree on a vector number and nothing else, and that number is
|
||||
; written down once in services.asm; an address would be a second thing to agree about, in
|
||||
; a memory map that is a convention rather than anything enforced.
|
||||
handleArgument:
|
||||
PSHD.0
|
||||
POPD.1
|
||||
SETD.0 RunArgument
|
||||
CALL copyText
|
||||
RETI
|
||||
|
||||
; Giving the machine back. This is the one place MVDS earns its keep. The program's Stack,
|
||||
; and the frame this very interrupt arrived on, are both abandoned where they lie, because
|
||||
; nothing is going to return through either of them.
|
||||
@@ -893,13 +1033,17 @@ Farewell:
|
||||
"halted"
|
||||
FilesText:
|
||||
" files"
|
||||
FileText:
|
||||
" file"
|
||||
|
||||
; Two strings rather than one, because a string literal stops at 255 characters and each
|
||||
; one carries its own zero byte, so they are printed in turn rather than joined.
|
||||
HelpText:
|
||||
"dir list what is on the disk
|
||||
load <file> read a program off the disk
|
||||
run start what was loaded"
|
||||
run [words] start what was loaded, and tell it those words
|
||||
delete <file> take it off the disk
|
||||
rename <file> <to> call it something else"
|
||||
HelpMoreText:
|
||||
"dump sixty four bytes of memory, and again for more
|
||||
dump <program|data|bank> <address>
|
||||
@@ -923,6 +1067,16 @@ LoadWhat:
|
||||
"load what?"
|
||||
NoSuchFile:
|
||||
"no such file"
|
||||
Deleted:
|
||||
"gone"
|
||||
Renamed:
|
||||
"renamed"
|
||||
DeleteWhat:
|
||||
"delete what?"
|
||||
RenameWhat:
|
||||
"rename what to what?"
|
||||
RenameNo:
|
||||
"there is no such file, or that name is taken"
|
||||
TooManyVectors:
|
||||
"that program wants more vectors than there is room for"
|
||||
Unreadable:
|
||||
@@ -944,6 +1098,10 @@ LoadName:
|
||||
"load"
|
||||
RunName:
|
||||
"run"
|
||||
DeleteName:
|
||||
"delete"
|
||||
RenameName:
|
||||
"rename"
|
||||
HelpName:
|
||||
"help"
|
||||
ExitName:
|
||||
@@ -960,6 +1118,15 @@ LoadCount:
|
||||
LoadVersion:
|
||||
0x00
|
||||
|
||||
; Where the first of rename's two names is, kept while the second is picked out of the
|
||||
; line, since finding that needs the pointers for itself.
|
||||
RenameFrom:
|
||||
0x00 0x00
|
||||
|
||||
; What followed "run", kept for the program to ask for.
|
||||
RunArgument:
|
||||
#Reserve 0d64
|
||||
|
||||
; ---- The vectors a loaded program brought with it ----
|
||||
;
|
||||
; Six bytes each: where it goes, what goes there, and what was there before. The last two
|
||||
@@ -1013,4 +1180,5 @@ CommandLine:
|
||||
osPrintString handlePrintString
|
||||
osReadLine handleReadLine
|
||||
osExit handleExit
|
||||
osArgument handleArgument
|
||||
Device 0x20 diskDone
|
||||
|
||||
@@ -991,6 +991,208 @@ sbfsSameByte:
|
||||
XOR
|
||||
RET
|
||||
|
||||
; ---- Deleting ----
|
||||
;
|
||||
; Frees a file. DP0 names it, and Q is zero if it went.
|
||||
;
|
||||
; A deleted entry and one that was never used are the same thing, which is the whole of
|
||||
; what deleting is here: the entry is zeroed and its blocks stop being spoken for. THE
|
||||
; BLOCKS THEMSELVES ARE LEFT EXACTLY AS THEY WERE, so what was in a file is still on the
|
||||
; disk until something is put over the top of it. Worth knowing if anything is ever meant
|
||||
; to be private. The host tool does the same, so the two agree about what a deleted disk
|
||||
; looks like.
|
||||
;
|
||||
; Nothing is compacted. Deleting leaves a hole, and because files are contiguous a hole is
|
||||
; only usable by something that fits inside it. That is the price of the directory being
|
||||
; the whole allocation map, and tidying it up is an ordinary program somebody can write
|
||||
; rather than anything the format has to say.
|
||||
sbfsDelete:
|
||||
CALL sbfsFind
|
||||
BNQ sbfsDeleteFailed
|
||||
|
||||
; How much room it was taking, worked out before the entry that says so is thrown away.
|
||||
CALL sbfsFileExtent
|
||||
|
||||
; sbfsFind leaves DP3 on the entry and SbfsBlock on the directory block it came out of,
|
||||
; which is everything needed to change it and put it back.
|
||||
PSHD.3
|
||||
POPD.0
|
||||
INIB 0d32
|
||||
sbfsDeleteWipe:
|
||||
RSTA
|
||||
STA.0
|
||||
INCD.0
|
||||
DECB
|
||||
BNB sbfsDeleteWipe
|
||||
|
||||
SETD.1 SbfsBuffer
|
||||
CALL sbfsBufferIn
|
||||
CALL sbfsWriteBlock
|
||||
BNQ sbfsDeleteFailed
|
||||
|
||||
; And the free count goes back up. It is a note rather than the truth, but a note worth
|
||||
; keeping right.
|
||||
RSTA
|
||||
SETD.0 SbfsBlock
|
||||
STA.0
|
||||
INCD.0
|
||||
STA.0
|
||||
CALL sbfsReadBlock
|
||||
BNQ sbfsDeleteFailed
|
||||
SETD.1 SbfsBuffer
|
||||
CALL sbfsBufferOut
|
||||
SETD.0 SbfsBuffer
|
||||
DPUP.0 0d12
|
||||
SETD.2 SbfsWantBlocks
|
||||
CALL sbfsAddWord
|
||||
SETD.1 SbfsBuffer
|
||||
CALL sbfsBufferIn
|
||||
CALL sbfsWriteBlock
|
||||
RET
|
||||
|
||||
sbfsDeleteFailed:
|
||||
RSTA
|
||||
INIB 0d1
|
||||
CCF
|
||||
ADD
|
||||
RET
|
||||
|
||||
; ---- Renaming ----
|
||||
;
|
||||
; DP0 is the name a file has, DP1 is the name it should have. Q is zero if it was renamed.
|
||||
;
|
||||
; Only the twenty two bytes of the name change, so no data moves and no block is touched
|
||||
; but the one holding the entry. THAT IS WHAT MAKES A SAFE SAVE POSSIBLE. Writing a file
|
||||
; that has grown means putting it somewhere else, and the obvious order - throw the old one
|
||||
; away, then write the new one - loses the lot if there turns out to be nowhere to put it.
|
||||
; Renaming is the cheapest thing this filesystem can do and the only one that can be left
|
||||
; until last, so it is what the order is built around.
|
||||
sbfsRename:
|
||||
; Where the old name is, kept somewhere that finding things will not tread on.
|
||||
SETD.2 SbfsSavedName
|
||||
STD.0.2
|
||||
|
||||
PSHD.1
|
||||
POPD.0
|
||||
SETD.1 SbfsNewName
|
||||
CALL sbfsKeepName ; Twenty two bytes, padded, the way an entry holds one.
|
||||
|
||||
; Refused if something already answers to the new name. Two entries with one name is a
|
||||
; disk that cannot be searched sensibly: a search answers with whichever it meets first,
|
||||
; so the other becomes unreachable without ever having been deleted.
|
||||
SETD.0 SbfsNewName
|
||||
CALL sbfsFind
|
||||
BRQ sbfsRenameFailed
|
||||
|
||||
SETD.2 SbfsSavedName
|
||||
LDD.0.2
|
||||
CALL sbfsFind
|
||||
BNQ sbfsRenameFailed
|
||||
|
||||
PSHD.3
|
||||
POPD.1
|
||||
DPUP.1 0d06 ; Past the flags, the start, the block count and the tail.
|
||||
SETD.0 SbfsNewName
|
||||
INIA 0d22
|
||||
SETD.2 SbfsCount
|
||||
STA.2
|
||||
sbfsRenameName:
|
||||
LDA.0
|
||||
STA.1
|
||||
INCD.0
|
||||
INCD.1
|
||||
LDA.2
|
||||
DECA
|
||||
STA.2
|
||||
BNA sbfsRenameName
|
||||
|
||||
SETD.1 SbfsBuffer
|
||||
CALL sbfsBufferIn
|
||||
CALL sbfsWriteBlock
|
||||
RET
|
||||
|
||||
sbfsRenameFailed:
|
||||
RSTA
|
||||
INIB 0d1
|
||||
CCF
|
||||
ADD
|
||||
RET
|
||||
|
||||
; ---- Saving over something that is already there ----
|
||||
;
|
||||
; DP0 names the file, DP1 is the data, and SbfsFileBlocks with SbfsFileTail say how big it
|
||||
; now is. Q is zero if it was saved.
|
||||
;
|
||||
; This is the routine every tool that edits a document wants, and the reason it is here
|
||||
; rather than in each of them is that the careful order is not obvious and getting it wrong
|
||||
; destroys somebody's work:
|
||||
;
|
||||
; make a temporary nothing is lost if there is nowhere to put it
|
||||
; write it
|
||||
; delete the original only once the new one is safely down
|
||||
; rename the temporary
|
||||
;
|
||||
; The obvious order - delete, create, write - looks fine and is a trap. Files here are
|
||||
; contiguous, so a file that has grown may not fit where it was, and a create can be
|
||||
; refused for want of a run long enough even on a disk with plenty of free blocks. Do it
|
||||
; that way round and the original is already gone when that happens.
|
||||
sbfsSaveFile:
|
||||
SETD.2 SbfsSaveName
|
||||
STD.0.2
|
||||
SETD.2 SbfsSaveData
|
||||
STD.1.2
|
||||
|
||||
; How big it is, kept aside: finding and deleting things both overwrite the place the
|
||||
; size is normally said, because both of them describe whatever they last looked at.
|
||||
SETD.0 SbfsSaveBlocks
|
||||
SETD.2 SbfsFileBlocks
|
||||
CALL sbfsSetWord
|
||||
SETD.0 SbfsFileTail
|
||||
LDA.0
|
||||
SETD.1 SbfsSaveTail
|
||||
STA.1
|
||||
|
||||
; A temporary left behind by a save that did not finish would be in the way. Whether
|
||||
; there was one is not worth asking about, since either answer leads here.
|
||||
SETD.0 SbfsTempName
|
||||
CALL sbfsDelete
|
||||
|
||||
SETD.0 SbfsFileBlocks
|
||||
SETD.2 SbfsSaveBlocks
|
||||
CALL sbfsSetWord
|
||||
SETD.0 SbfsSaveTail
|
||||
LDA.0
|
||||
SETD.1 SbfsFileTail
|
||||
STA.1
|
||||
|
||||
SETD.0 SbfsTempName
|
||||
CALL sbfsCreate
|
||||
BNQ sbfsSaveFailed
|
||||
|
||||
SETD.2 SbfsSaveData
|
||||
LDD.1.2
|
||||
CALL sbfsWriteFile
|
||||
BNQ sbfsSaveFailed
|
||||
|
||||
; Now, and not before, the old one goes. It may not exist, which is what saving something
|
||||
; for the first time looks like from here.
|
||||
SETD.2 SbfsSaveName
|
||||
LDD.0.2
|
||||
CALL sbfsDelete
|
||||
|
||||
SETD.0 SbfsTempName
|
||||
SETD.2 SbfsSaveName
|
||||
LDD.1.2
|
||||
CALL sbfsRename
|
||||
RET
|
||||
|
||||
sbfsSaveFailed:
|
||||
RSTA
|
||||
INIB 0d1
|
||||
CCF
|
||||
ADD
|
||||
RET
|
||||
|
||||
#Data
|
||||
|
||||
SbfsMagic:
|
||||
@@ -1050,5 +1252,29 @@ SbfsName:
|
||||
SbfsWanted:
|
||||
#Reserve 0d22
|
||||
|
||||
; ---- What renaming and saving keep ----
|
||||
|
||||
; A name on its way into an entry, padded to the twenty two bytes an entry holds.
|
||||
SbfsNewName:
|
||||
#Reserve 0d22
|
||||
|
||||
; Where a name lives, kept across a search, which needs the pointers for itself.
|
||||
SbfsSavedName:
|
||||
0x00 0x00
|
||||
|
||||
SbfsSaveName:
|
||||
0x00 0x00
|
||||
SbfsSaveData:
|
||||
0x00 0x00
|
||||
SbfsSaveBlocks:
|
||||
0x00 0x00
|
||||
SbfsSaveTail:
|
||||
0x00
|
||||
|
||||
; What a document is called while it is being written and is not yet the real thing. A
|
||||
; name nothing else is likely to want, and short enough to leave room for a long one.
|
||||
SbfsTempName:
|
||||
"sbfs.part"
|
||||
|
||||
SbfsBuffer:
|
||||
#Reserve 0d256
|
||||
|
||||
@@ -24,3 +24,4 @@
|
||||
osPrintString 0d16 ; DP0 names a string. Prints it.
|
||||
osReadLine 0d17 ; DP0 names somewhere to put a line read from the console.
|
||||
osExit 0d18 ; Give the machine back to the system.
|
||||
osArgument 0d19 ; DP0 names somewhere to put the rest of the run command.
|
||||
|
||||
@@ -215,6 +215,82 @@ textHexNo:
|
||||
ADD
|
||||
RET
|
||||
|
||||
; ---- A number written in decimal ----
|
||||
;
|
||||
; DP0 names it. Q is the value, and TextDigits says how many digits were read, which is
|
||||
; zero when there was no number there at all. Stops at the first thing that is not a digit.
|
||||
;
|
||||
; Decimal rather than hex, and one byte rather than two, because this is for the numbers a
|
||||
; person types at a program: a line number, a count, a how many. Nobody counts lines in
|
||||
; hex, and nobody types a line number above 255 on a machine this size. textHexWord is
|
||||
; still the one for an address, where hex is what everybody means.
|
||||
;
|
||||
; Ten times the running total is worked out as eight of it plus two of it, because nothing
|
||||
; on this machine multiplies. Anything past 255 wraps, which is what the same sum does
|
||||
; everywhere else here.
|
||||
textNumber:
|
||||
RSTA
|
||||
SETD.1 TextValue
|
||||
STA.1
|
||||
SETD.1 TextDigits
|
||||
STA.1
|
||||
|
||||
textNumberLoop:
|
||||
LDA.0
|
||||
BRA textNumberDone
|
||||
|
||||
; Below '0' or above '9' ends it.
|
||||
INIB 0d48
|
||||
CCF
|
||||
SUB
|
||||
BRC textNumberDone ; It borrowed, so the character was below '0'.
|
||||
MVQA
|
||||
INIB 0d10
|
||||
CCF
|
||||
SUB
|
||||
BNC textNumberDone ; It did not borrow, so it was ten or more past '0'.
|
||||
|
||||
PSHA ; The digit, while the total is multiplied.
|
||||
SETD.1 TextValue
|
||||
LDA.1
|
||||
LDB.1
|
||||
CCF
|
||||
ADD ; Twice.
|
||||
MVQA
|
||||
MVQB
|
||||
PSHA ; Twice, kept: ten is eight and two.
|
||||
CCF
|
||||
ADD ; Four times.
|
||||
MVQA
|
||||
MVQB
|
||||
CCF
|
||||
ADD ; Eight times.
|
||||
MVQA
|
||||
POPB
|
||||
CCF
|
||||
ADD ; Ten times.
|
||||
MVQA
|
||||
POPB
|
||||
CCF
|
||||
ADD ; And the digit.
|
||||
SETD.1 TextValue
|
||||
STQ.1
|
||||
|
||||
SETD.1 TextDigits
|
||||
LDA.1
|
||||
INCA
|
||||
STA.1
|
||||
INCD.0
|
||||
BRI textNumberLoop
|
||||
|
||||
textNumberDone:
|
||||
SETD.1 TextValue
|
||||
LDA.1
|
||||
RSTB
|
||||
CCF
|
||||
ADD ; Q is the value, the way a routine hands a byte back.
|
||||
RET
|
||||
|
||||
#Data
|
||||
|
||||
; Where the rest of the line begins, after textSplit has taken a word off the front.
|
||||
|
||||
Reference in New Issue
Block a user