"do build.sh cosmos.asm" looked for a file called "build.sh cosmos.asm", because scriptOpen copied the whole rest of the line into the name. So a script could be told nothing, and a launcher - a script whose entire job is to hand on what it was told - could not exist. Now the name is cut off the front and what follows is kept whole. $1 to $9 are the words of it, walked out on demand, and $args is all of them. Nothing is stored per parameter, so there is no limit on how many a script may be handed and no second number to keep in step. Both ways of starting a script pass them on: "do" and typing the name. A word that was not given is an error that stops the script, like every other name this shell does not know. Expanding it to nothing would let a command run with an argument missing and then report success, which is what stop-on-failure exists to prevent. $args is always set inside a script, empty if it was given nothing, so "if same $args" can be asked. ---- And the count that describes the block was already wrong ---- Found while adding a field to it. The state one script keeps for another is saved by a single copy of a fixed number of bytes, and that number was 71 against a block of 77: six bytes of line position had been added in the middle of it years after the count was written. So the tail of every saved script was never saved, and #quiet in a helper stayed behind in the script that called it - the opposite of what this file's own comment promises and the README documents. The unsaved line position turned out not to matter, because a loop keeps its own copy in the block record. Nothing said so. Three numbers describe this block and all three now say so in a comment, and cosmosScriptNest ends on a helper that goes quiet and a caller that must not stay that way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
676 lines
18 KiB
NASM
676 lines
18 KiB
NASM
; Reading shell lines out of a file.
|
|
;
|
|
; DP0 = the file's name
|
|
; CALL scriptOpen Q = 0 and a script is running, or Q says what was wrong:
|
|
; 1 there is no such file
|
|
; 2 it is not a script - no #! on the front
|
|
; 3 too many scripts inside each other
|
|
; DP0 = a buffer, B = how much room
|
|
; CALL scriptLine Q = 0 and there is a line in the buffer, or nonzero at the end
|
|
;
|
|
; Written by Anachronaut
|
|
;
|
|
; ---- Why the shell reads files and not the other way round ----
|
|
;
|
|
; fileStream.asm does almost this and is deliberately not used. It is application machinery:
|
|
; More, Type and Wander each include it and each get their own copy in their own Data
|
|
; Segment. The shell reading a script through it would be a fourth copy, and the shell is the
|
|
; one place where that copy has to survive running a program - which is exactly the thing
|
|
; those programs are. Here the state belongs to the system and nothing a program does can
|
|
; reach it.
|
|
;
|
|
; ---- A block, and a nought on the end of it ----
|
|
;
|
|
; The reader walks a Data Pointer along the block and stops at a nought. A block is 256 bytes
|
|
; and a count of them does not fit in a byte, so every other way of knowing where the block
|
|
; ends costs sixteen bit arithmetic on every character. Writing a nought after the last byte
|
|
; costs one store per block and turns the whole question into "is this byte zero".
|
|
;
|
|
; The buffer is 257 bytes for that reason: a full block leaves the nought at the end of it.
|
|
; A script cannot contain a nought, which is not a restriction anybody will notice - it is
|
|
; text, and the #! on the front is what stops a program being read as one.
|
|
|
|
#Program
|
|
|
|
; ---- One script inside another ----
|
|
;
|
|
; A build script calling a setup script is the first thing anybody tries, so what is saved
|
|
; when one script starts another is a POSITION AND NOT A BUFFER. The whole state of a
|
|
; running script is its name, which block comes next, how many are left, and where in the
|
|
; block it is - seventy bytes, laid out next to each other below so that saving it is one
|
|
; copy. The block itself is read again on the way back, which costs one disk read per return
|
|
; and saves 257 bytes a level.
|
|
;
|
|
; Four levels. Deep enough for a script calling a script that calls a helper, and shallow
|
|
; enough that a script which runs itself says so instead of filling memory.
|
|
scriptPush:
|
|
CALL scriptSlotAt
|
|
SETD.0 ScriptName
|
|
PSHD.3
|
|
POPD.1
|
|
CALL scriptCopyState
|
|
RET
|
|
|
|
scriptPop:
|
|
CALL scriptSlotAt
|
|
PSHD.3
|
|
POPD.0
|
|
SETD.1 ScriptName
|
|
CALL scriptCopyState
|
|
; ScriptAt points into the block buffer, which now holds somebody else's block. Reading
|
|
; it back is what makes the saved pointer mean what it meant.
|
|
CALL scriptReread
|
|
RET
|
|
|
|
; DP3 = where the script one level up is remembered. Reached by stepping rather than by
|
|
; multiplying, because this machine cannot multiply and the depth is never more than three
|
|
; steps. DP3 because RET puts the others back.
|
|
scriptSlotAt:
|
|
SETD.3 ScriptSaved
|
|
SETD.2 ScriptDepth
|
|
LDA.2
|
|
DECA
|
|
BRA scriptSlotDone
|
|
scriptSlotStep:
|
|
DPUP.3 0d205
|
|
DECA
|
|
BNA scriptSlotStep
|
|
scriptSlotDone:
|
|
RET
|
|
|
|
; Seventy bytes, DP0 to DP1.
|
|
scriptCopyState:
|
|
INIB 0d205
|
|
scriptCopyByte:
|
|
LDA.0
|
|
STA.1
|
|
INCD.0
|
|
INCD.1
|
|
DECB
|
|
BNB scriptCopyByte
|
|
RET
|
|
|
|
; The block that is meant to be in the buffer, back in the buffer. ScriptIndex is the NEXT
|
|
; one, so the one being read from is the one before it.
|
|
scriptReread:
|
|
SETD.1 ScriptIndex
|
|
LDA.1
|
|
INCD.1
|
|
LDB.1
|
|
DECB
|
|
BNC scriptRereadGo
|
|
DECA
|
|
scriptRereadGo:
|
|
SETD.0 ScriptName
|
|
SETD.1 ScriptBlock
|
|
SWI osFileBlock
|
|
SETD.1 ScriptBlock
|
|
PSHD.3
|
|
POPB
|
|
POPA
|
|
DPUW.1
|
|
RSTA
|
|
STA.1
|
|
RET
|
|
|
|
; ---- Opening ----
|
|
;
|
|
; The name is COPIED rather than remembered by address. osFileBlock is given the name again
|
|
; for every block, and the caller's copy is CommandLine, which the next line typed will
|
|
; overwrite. fileStream remembers an address and says in its own comment that the address
|
|
; must stay valid; here it cannot, because the thing that reads the next line is the reason
|
|
; the name is needed.
|
|
scriptOpen:
|
|
; ---- Four deep and no further ----
|
|
SETD.1 ScriptDepth
|
|
LDA.1
|
|
INIB 0d4
|
|
CCF
|
|
SUB
|
|
BRQ scriptOpenTooDeep
|
|
|
|
; ---- The one already running is put somewhere safe FIRST ----
|
|
;
|
|
; Before anything below overwrites it, and put back again on every way out of here that is
|
|
; not success. Opening writes the name into the live state to ask the disk about it, so by
|
|
; the time the answer is known the caller's place is already gone.
|
|
;
|
|
; A still holds the depth from the check above: SUB writes Q and leaves it alone.
|
|
BRA scriptOpenOutermost
|
|
CALL scriptPush
|
|
BRI scriptOpenFirst
|
|
scriptOpenOutermost:
|
|
; ---- The one place a stale quiet would matter ----
|
|
;
|
|
; A script started from the prompt begins loud whatever the last one left behind, which is
|
|
; what makes this the only place the flag has to be put back. Nested scripts INHERIT
|
|
; instead: a build that asked for quiet meant its helpers too.
|
|
RSTA
|
|
SETD.1 ScriptQuiet
|
|
STA.1
|
|
scriptOpenFirst:
|
|
|
|
SETD.1 ScriptName
|
|
INIB 0d63
|
|
CALL copyText
|
|
|
|
; ---- And what it was given ----
|
|
;
|
|
; After the push, so that what the script one level up was given is already somewhere safe.
|
|
; A script started with nothing gets an EMPTY one rather than none at all, which is what
|
|
; makes "$args" always mean something inside a script while "$1" can be missing.
|
|
SETD.1 ScriptArgsFrom
|
|
LDD.0.1
|
|
SETD.1 ScriptArgs
|
|
INIB 0d128
|
|
CALL copyText
|
|
|
|
CALL scriptKeepDrive
|
|
SETD.0 ScriptName
|
|
SWI osFileInfo
|
|
CALL scriptGiveDriveBack
|
|
BRQ scriptOpenThere
|
|
INIA 0x01
|
|
BRI scriptOpenFailed
|
|
scriptOpenThere:
|
|
; DP3 is how many blocks. A file of none is not a script.
|
|
PSHD.3
|
|
POPB
|
|
POPA
|
|
SETD.1 ScriptBlocks
|
|
STA.1
|
|
INCD.1
|
|
STB.1
|
|
OR
|
|
BRQ scriptOpenNotOne
|
|
|
|
SETD.1 ScriptIndex
|
|
RSTA
|
|
STA.1
|
|
INCD.1
|
|
STA.1
|
|
|
|
; The first block, so that the #! can be looked at before anything is promised.
|
|
CALL scriptFill
|
|
BNQ scriptOpenNotOne
|
|
|
|
; ---- What makes a file a script ----
|
|
;
|
|
; Two bytes, and the rest of the line ignored. A directive rather than a comment, the way
|
|
; #Program is in assembly: the shell reads it and refuses the file without it, so calling
|
|
; it a comment would be a lie about what it does. What follows the #! is where the name of
|
|
; an interpreter goes when there is a second one; today there is one and it is this shell.
|
|
SETD.1 ScriptBlock
|
|
LDA.1
|
|
INIB 0d35 ; '#'
|
|
CCF
|
|
SUB
|
|
BNQ scriptOpenNotOne
|
|
INCD.1
|
|
LDA.1
|
|
INIB 0d33 ; '!'
|
|
CCF
|
|
SUB
|
|
BNQ scriptOpenNotOne
|
|
|
|
; Past the shebang line, wherever it ends.
|
|
CALL scriptSkipLine
|
|
|
|
SETD.1 ScriptDepth
|
|
LDA.1
|
|
INCA
|
|
STA.1
|
|
RSTA
|
|
BRI scriptOpenAnswer ; A is nought, which is the answer for "it opened".
|
|
|
|
scriptOpenTooDeep:
|
|
INIA 0x03
|
|
BRI scriptOpenAnswer ; Nothing was pushed, so there is nothing to put back.
|
|
|
|
scriptOpenNotOne:
|
|
INIA 0x02
|
|
scriptOpenFailed:
|
|
; Whatever was running is still running, and its place is in the slot rather than in the
|
|
; live state. A is the answer and must survive being put back.
|
|
SETD.1 ScriptDepth
|
|
LDB.1
|
|
BRB scriptOpenAnswer
|
|
PSHA
|
|
CALL scriptPop
|
|
POPA
|
|
scriptOpenAnswer:
|
|
; Q is the answer, and A holds it. Adding nought is how a register becomes Q.
|
|
RSTB
|
|
CCF
|
|
ADD
|
|
RET
|
|
|
|
; ---- One line ----
|
|
;
|
|
; Comments and blank lines never reach the shell. The reader drops them, so the echo does not
|
|
; print them and the dispatch never sees a line it would have to know to ignore.
|
|
scriptLine:
|
|
SETD.1 ScriptRoom
|
|
STB.1
|
|
SETD.1 ScriptInto
|
|
STD.0.1
|
|
|
|
scriptLineAgain:
|
|
; ---- Where this line begins ----
|
|
;
|
|
; Kept before it is read, because a loop has to be able to go back to the line that opened
|
|
; it and by the time that line has been read the reader is past it. Three words, and the
|
|
; block itself is read again on the way back, which is what scriptReread is for.
|
|
SETD.0 ScriptIndex
|
|
SETD.1 ScriptLineIndex
|
|
CALL sbfsCopyWord
|
|
SETD.0 ScriptAt
|
|
SETD.1 ScriptLineAt
|
|
CALL sbfsCopyWord
|
|
SETD.0 ScriptBlocks
|
|
SETD.1 ScriptLineBlocks
|
|
CALL sbfsCopyWord
|
|
|
|
SETD.1 ScriptLength
|
|
RSTA
|
|
STA.1
|
|
SETD.0 ScriptInto
|
|
LDD.0.0
|
|
|
|
scriptLineNext:
|
|
CALL scriptByte
|
|
BNQ scriptLineEnded
|
|
|
|
SETD.1 ScriptChar
|
|
LDA.1
|
|
INIB 0d10
|
|
CCF
|
|
SUB
|
|
BRQ scriptLineDone
|
|
; A still holds the character: SUB writes Q and leaves it alone.
|
|
INIB 0d13 ; Carriage return, from a file written on a host that uses them.
|
|
CCF
|
|
SUB
|
|
BRQ scriptLineNext
|
|
|
|
; Room?
|
|
SETD.1 ScriptLength
|
|
LDA.1
|
|
SETD.1 ScriptRoom
|
|
LDB.1
|
|
CCF
|
|
SUB
|
|
BRQ scriptLineNext ; Full. Read on and drop what comes, the way readLine does.
|
|
|
|
SETD.1 ScriptChar
|
|
LDA.1
|
|
STA.0
|
|
INCD.0
|
|
SETD.1 ScriptLength
|
|
LDA.1
|
|
INCA
|
|
STA.1
|
|
BRI scriptLineNext
|
|
|
|
scriptLineDone:
|
|
; Terminate it, then decide whether the shell wants to see it.
|
|
RSTA
|
|
STA.0
|
|
|
|
SETD.0 ScriptInto
|
|
LDD.0.0
|
|
LDA.0
|
|
BRA scriptLineAgain ; Empty.
|
|
INIB 0d59 ; ';' - a comment, the same as everywhere else on this machine.
|
|
CCF
|
|
SUB
|
|
BRQ scriptLineAgain
|
|
|
|
; ---- A directive, which is about the file rather than for the shell ----
|
|
;
|
|
; '#' the way the assembler means it. #quiet stops each line being echoed as it runs, for a
|
|
; script whose own output is the point and which the prompts get in the way of; #loud puts
|
|
; it back.
|
|
;
|
|
; ANYTHING ELSE BEGINNING WITH # IS HANDED TO THE SHELL, which does not know it and says
|
|
; so and stops the script. That is deliberate and it is free: a script that asked for
|
|
; something this shell cannot do should not carry on as though it had been given it, and
|
|
; the machinery for saying so already exists.
|
|
INIB 0d35 ; '#'
|
|
CCF
|
|
SUB
|
|
BNQ scriptLineGive
|
|
|
|
SETD.1 QuietWord
|
|
CALL textSame
|
|
BRQ scriptLineQuiet
|
|
SETD.0 ScriptInto
|
|
LDD.0.0
|
|
SETD.1 LoudWord
|
|
CALL textSame
|
|
BRQ scriptLineLoud
|
|
SETD.0 ScriptInto
|
|
LDD.0.0
|
|
BRI scriptLineGive
|
|
|
|
scriptLineQuiet:
|
|
INIA 0x01
|
|
SETD.1 ScriptQuiet
|
|
STA.1
|
|
BRI scriptLineAgain
|
|
scriptLineLoud:
|
|
RSTA
|
|
SETD.1 ScriptQuiet
|
|
STA.1
|
|
BRI scriptLineAgain
|
|
|
|
scriptLineGive:
|
|
RSTA ; Q = 0: there is a line.
|
|
RSTB
|
|
CCF
|
|
ADD
|
|
RET
|
|
|
|
scriptLineEnded:
|
|
; ---- A last line with no newline on it is still a line ----
|
|
;
|
|
; Text files do not reliably end with one, and a script whose final command silently did
|
|
; not run because somebody's editor left the newline off is a bad way to find that out.
|
|
; If anything has been gathered, finish it the ordinary way; the next call comes back here
|
|
; with nothing gathered and ends for real.
|
|
SETD.1 ScriptLength
|
|
LDA.1
|
|
BRA scriptLineNoMore
|
|
BRI scriptLineDone
|
|
|
|
scriptLineNoMore:
|
|
CALL scriptClose
|
|
INIA 0x01
|
|
RSTB
|
|
CCF
|
|
ADD
|
|
RET
|
|
|
|
; ---- One character, or the end ----
|
|
;
|
|
; Q = 0 and the character is in ScriptChar, or Q is one and there are no more.
|
|
;
|
|
; IN MEMORY RATHER THAN IN A REGISTER, because RET puts A and B back the way the caller had
|
|
; them - only Q and Data Pointer 3 survive a CALL. Handing the character back in A looked
|
|
; right, assembled, and returned the caller's own A every time.
|
|
scriptByte:
|
|
SETD.1 ScriptAt
|
|
LDD.1.1
|
|
LDA.1
|
|
BRA scriptByteRefill ; The nought at the end of the block.
|
|
|
|
; Step the saved pointer past it.
|
|
SETD.1 ScriptChar
|
|
STA.1
|
|
SETD.1 ScriptAt
|
|
LDD.0.1
|
|
INCD.0
|
|
STD.0.1
|
|
RSTA
|
|
RSTB
|
|
CCF
|
|
ADD ; Q = 0.
|
|
RET
|
|
|
|
scriptByteRefill:
|
|
CALL scriptFill
|
|
BNQ scriptByteNoMore
|
|
BRI scriptByte
|
|
scriptByteNoMore:
|
|
INIA 0x01
|
|
RSTB
|
|
CCF
|
|
ADD
|
|
RET
|
|
|
|
; ---- The next block, with a nought written after it ----
|
|
;
|
|
; Q = 0 if there is one.
|
|
scriptFill:
|
|
SETD.1 ScriptBlocks
|
|
LDA.1
|
|
INCD.1
|
|
LDB.1
|
|
OR
|
|
BRQ scriptFillNoMore
|
|
|
|
CALL scriptKeepDrive
|
|
SETD.0 ScriptName
|
|
SETD.1 ScriptBlock
|
|
SETD.2 ScriptIndex
|
|
LDA.2
|
|
INCD.2
|
|
LDB.2
|
|
SWI osFileBlock
|
|
CALL scriptGiveDriveBack
|
|
BNQ scriptFillNoMore
|
|
|
|
; DP3 is how many bytes came back. The nought goes after them.
|
|
SETD.1 ScriptBlock
|
|
PSHD.3
|
|
POPB
|
|
POPA
|
|
DPUW.1
|
|
RSTA
|
|
STA.1
|
|
|
|
SETD.1 ScriptAt
|
|
SETD.0 ScriptBlock
|
|
STD.0.1
|
|
|
|
; Index++, blocks--.
|
|
SETD.1 ScriptIndex
|
|
INCD.1
|
|
LDA.1
|
|
INCA
|
|
STA.1
|
|
BNC scriptFillCount
|
|
DECD.1
|
|
LDA.1
|
|
INCA
|
|
STA.1
|
|
scriptFillCount:
|
|
SETD.1 ScriptBlocks
|
|
INCD.1
|
|
LDA.1
|
|
BRA scriptFillBorrow
|
|
DECA
|
|
STA.1
|
|
BRI scriptFillGot
|
|
scriptFillBorrow:
|
|
INIA 0xFF
|
|
STA.1
|
|
DECD.1
|
|
LDA.1
|
|
DECA
|
|
STA.1
|
|
scriptFillGot:
|
|
RSTA
|
|
RSTB
|
|
CCF
|
|
ADD
|
|
RET
|
|
|
|
scriptFillNoMore:
|
|
INIA 0x01
|
|
RSTB
|
|
CCF
|
|
ADD
|
|
RET
|
|
|
|
; ---- Reading a script must not move the person ----
|
|
;
|
|
; The name is resolved afresh for every block, and a name with a drive in front of it moves
|
|
; the machine to that drive as a side effect of being resolved - sbfsWalk calls sbfsUse on
|
|
; the way past. So a script found in the system's place on drive 0, started by somebody
|
|
; standing on a disk of their own, would run its lines on the system disk instead of theirs.
|
|
;
|
|
; It matters more now than it used to. `do 0:/Apps/setup.sh` was always able to do this;
|
|
; typing a name now finds scripts the same three places a program is looked for, and the
|
|
; third of them is drive-qualified.
|
|
;
|
|
; The answer that survives the CALL: Q into A and the byte count onto the stack, because
|
|
; sbfsUse writes Q and RET puts DP3 back the way it found it.
|
|
scriptKeepDrive:
|
|
INA 0x24
|
|
SETD.1 ScriptDrive
|
|
STA.1
|
|
RET
|
|
|
|
scriptGiveDriveBack:
|
|
PSHD.3
|
|
MVQA
|
|
PSHA
|
|
SETD.1 ScriptDrive
|
|
LDA.1
|
|
CALL sbfsUse
|
|
POPA
|
|
POPD.3
|
|
; A holds what Q held. Adding nought to it is how it becomes Q again.
|
|
RSTB
|
|
CCF
|
|
ADD
|
|
RET
|
|
|
|
; Everything up to and including the next line feed, thrown away. Used for the shebang.
|
|
scriptSkipLine:
|
|
CALL scriptByte
|
|
BNQ scriptSkipDone
|
|
SETD.1 ScriptChar
|
|
LDA.1
|
|
INIB 0d10
|
|
CCF
|
|
SUB
|
|
BNQ scriptSkipLine
|
|
scriptSkipDone:
|
|
RET
|
|
|
|
; One script ending. Whatever asked for it carries on, if anything did.
|
|
scriptClose:
|
|
SETD.1 ScriptDepth
|
|
LDA.1
|
|
BRA scriptCloseNone
|
|
DECA
|
|
STA.1
|
|
BRA scriptCloseNone
|
|
CALL scriptPop
|
|
RET
|
|
|
|
scriptCloseNone:
|
|
; ---- Nothing to put back here ----
|
|
;
|
|
; The flag is only ever READ while a script is running: the console's own path says its
|
|
; prompt whatever this holds, and the first script started from the prompt sets it to loud
|
|
; on the way in. So a stale quiet cannot be observed, and clearing it here would be a line
|
|
; no test could tell the difference about - which is how it was written the first time, with
|
|
; a comment claiming it fixed something. It fixed something that a later change had already
|
|
; made impossible.
|
|
RET
|
|
|
|
; Every script ending at once, which is what a line that did not work means. A build whose
|
|
; helper failed should not carry on in the script that called the helper either.
|
|
scriptAbandon:
|
|
RSTA
|
|
SETD.1 ScriptDepth
|
|
STA.1
|
|
RET
|
|
|
|
#Data
|
|
|
|
QuietWord:
|
|
"#quiet"
|
|
LoudWord:
|
|
"#loud"
|
|
|
|
ScriptDepth:
|
|
0x00
|
|
|
|
; The drive the person is standing on, kept across a block being fetched. Not part of the
|
|
; saved state below: it is only ever live for the length of one read.
|
|
ScriptDrive:
|
|
0x00
|
|
|
|
; Where the arguments are, for the length of one call to scriptOpen. A pointer rather than the
|
|
; text, because the text is still sitting in the line that named the script and copying it
|
|
; twice would buy nothing.
|
|
ScriptArgsFrom:
|
|
0x00 0x00
|
|
|
|
; ---- Two hundred and five bytes, and they are next to each other on purpose ----
|
|
;
|
|
; Name, blocks left, next block, where in the block, what it was given, and whether it is
|
|
; echoing: the whole of a running script. Saving it is one copy because of this order, and
|
|
; nothing else may be put between them.
|
|
;
|
|
; ---- THREE NUMBERS DESCRIBE THIS BLOCK AND ALL THREE MUST AGREE ----
|
|
;
|
|
; scriptCopyState how many bytes one of them is
|
|
; scriptSlotAt how far apart two saved ones are
|
|
; ScriptSaved four of them
|
|
;
|
|
; They did not agree, and that is what this warning is for. Six bytes of line position were
|
|
; added in the middle of the block and the count stayed at 71, so the last six bytes of every
|
|
; saved script were never saved: a nested script's #quiet leaked back out to the script that
|
|
; called it, which is the opposite of what this file's own comment promises. The line position
|
|
; that went unsaved with it turned out not to matter - a loop keeps its own copy in the block
|
|
; record - but nothing said so, and the next field added would have been chosen at random.
|
|
ScriptName:
|
|
#Reserve 0d64
|
|
ScriptBlocks:
|
|
0x00 0x00
|
|
ScriptIndex:
|
|
0x00 0x00
|
|
ScriptAt:
|
|
0x00 0x00
|
|
|
|
; ---- And where the line being read began ----
|
|
;
|
|
; A loop goes back to the line that opened it, and by the time that line has been read the
|
|
; reader is past it. So the position is kept before every line rather than worked out
|
|
; afterwards, which cannot be done: a line is not a fixed size and there is nothing to
|
|
; subtract.
|
|
ScriptLineIndex:
|
|
0x00 0x00
|
|
ScriptLineAt:
|
|
0x00 0x00
|
|
ScriptLineBlocks:
|
|
0x00 0x00
|
|
; Saved with the rest, so that a quiet script calling a loud one gets its quiet back when
|
|
; the loud one finishes. A new script INHERITS it rather than resetting, because a build
|
|
; that asked for quiet meant its helpers too; only the first script started from the prompt
|
|
; begins loud.
|
|
ScriptQuiet:
|
|
0x00
|
|
|
|
; ---- What the script was given ----
|
|
;
|
|
; The rest of the line that started it, kept whole rather than picked apart, because picking
|
|
; it apart costs a place to put every piece and a limit on how many there may be. "$1" walks
|
|
; this and copies out the word it wants; "$args" is this.
|
|
;
|
|
; A WHOLE LINE OF ROOM, so that nothing a person can type can be cut short. The line is 127
|
|
; and the script's own name has already come off the front of it, so this can never be filled
|
|
; - which is worth 128 bytes a level to never have to explain.
|
|
ScriptArgs:
|
|
#Reserve 0d128
|
|
|
|
; Three would do - a save happens on the second script and not the first - but four costs
|
|
; one more and removes an off-by-one from the only place it could hide.
|
|
ScriptSaved:
|
|
#Reserve 0d820
|
|
ScriptInto:
|
|
0x00 0x00
|
|
ScriptRoom:
|
|
0x00
|
|
ScriptLength:
|
|
0x00
|
|
ScriptChar:
|
|
0x00
|
|
ScriptBlock:
|
|
#Reserve 0d257
|