home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
SIMTEL
/
CPMUG
/
CPMUG052.ARK
/
VARBATCH.1
< prev
next >
Wrap
Text File
|
1985-02-10
|
23KB
|
760 lines
$This is program BATCH.
$Operation of this program is described in the BATCH Reference Manual.
$This program is entered into the public domain by its author, Daniel
$Ross. Please do not remove this notice.
$The mixed-upper-lower-case version of this file constitutes the 1st
$module of program BATCH. The upper-case-only version of this file
$constitutes the 2nd module of program BATCH.
$Coded by Daniel Ross, in the computer language SIL80Z80, starting
$Jan. 20, 1981.
$Revised Feb. 12, 1981
$For more information contact:
$Daniel Ross
$Succinct Systems
$1346 River St.
$Santa Cruz, Calif. 95060
$Phone (408) 426-4197
DEF FALSE = 0;
DEF TRUE = 1;
IGNORE1STSPACE 1;
DEF OPTIMIZED_COMPILATION = TRUE;
DEF NoInitialValue = 0;
XREF OF NoInitialValue : 0;
DEF PatchMe = 0;
DEF NrOfVariables = 10;
DEF MaxDepthOfBatchStack = NrOfVariables;
$ASCII characters
DEF EndOfText = #03; $Ctrl-C
DEF Bell = #07;
DEF LineFeed = #0A;
DEF CarriageReturn = #0D;
DEF DataLinkEscape = #10; $Ctrl-P
DEF NegativeAck = #15; $Ctrl-U
DEF Substitute = #1A; $Ctrl-Z
DEF FileSeparator = #1C;
DEF RecordSeparator = #1E;
DEF NonexistentChar = #80;
DEF TextTerminator = NonexistentChar + RecordSeparator;
DEF FileTerminator = NonexistentChar + FileSeparator;
DEF AdrOf_JMP_WBOOT = #0001;$Addr. of the JMP WBOOT
$instruction in the table at
$the beginning of BIOS
DEF WarmBoot = #0000;$Entry addr. of procedure WBOOT
DEF CPMProgramStart = #0100;$Starting addr. of all CP/M
$user programs
ASM IF ^ = 0; $Assemble if this is the model which will be copied
$into safe memory
DEF ^ = CPMProgramStart;
:Start GO. Main;
:SafeMemory WORD PatchMe;
$Addr. of the start of safe
$memory
:SizeOfSafeMemory WORD PatchMe;
$Size in bytes of safe memory
:SizeOfCodeInSafeMemory WORD SizeOfModel;
$Do not alter the contents of
$this word; just examine it.
$This is the size of safe
$memory needed just to hold the
$code, not including any batch
$text. If you have insufficient
$safe memory, this program will
$not execute correctly.
:AdrOfModels WORD Model, MODEL;
$Do not alter the contents of
$these words; just examine them.
$They contain the starting
$addresses for corresponding
$patches in each of the models.
:AdrOfYourPatchProc WORD YourPatchProc;
$Do not alter the contents of
$this word; just examine it to
$determine where your patch
$procedure should be located
:DiagnosticInfo WORD 0,0,0,0;
$This space may be used as
$desired, to hold diagnostic
$info. in case of error
:JUST_BEFORE_MODEL
END ASMIF;
$If this is the model which is used to discover relocatable addresses,
$make sure that both bytes of the addresses will differ from those of
$the model which will be copied into safe memory.
ASM IF
(<>(^ - JUST_BEFORE_MODEL) ~= 0) & (><(^ - JUST_BEFORE_MODEL) = 0);
SKIP OVER 1; $Force the LSB of the addresses
$to differ
END ASMIF;
$**********************************************************************
:Model
SHOW ^; $'Start of a model module
$The following variables and/or constants may be patched to change their
$initial/permanent values
:BatchCONSTReport BYTE 0;
$This byte contains the value of
$the output param from CONST,
$when batched chars. are being
$read
:RingForAttn BYTE FALSE;
$0 means quiet, any nonzero
$value means ring for attention
$when the user must start typing
$in chars.
:Escape BYTE #01; $StartOfHeading, Ctrl-A
$This must be the 1st char. of
$any 2-char. command to BATCH
$Table of valid 2nd chars., for 2-char. commands to BATCH, and the
$addresses of their interpretation procedures
:TableOf2ndChars
BYTE "I"; WORD RequestTypedInput;
BYTE "B"; WORD RequestBatchInput;
BYTE "R"; WORD RequestRingForAttn;
BYTE "Q"; WORD RequestQuiet;
BYTE "Y"; WORD RequestYesFromCONST;
BYTE "N"; WORD RequestNoFromCONST;
BYTE "X"; WORD NoOp;
BYTE "Z"; WORD TerminateBatchProcessing;
BYTE "F"; WORD RequestFetchFromVariable;
BYTE "O"; WORD RequestTypedOutput;
BYTE "A"; WORD RequestAnswer;
BYTE "V"; WORD RequestVariableVariable;
BYTE "S"; WORD RequestStoreIntoVariable;
BYTE FileTerminator; WORD TerminateBatchProcessing;
:AfterTableOf2ndChars
:AdrOf_JMP_CONST WORD NoInitialValue;
:GO_OriginalCONST GO. NoInitialValue;
:GO_OriginalCONIN GO. NoInitialValue;
:GO_OriginalCONOUT GO. NoInitialValue;
:AfterGO_Original
:CommandErrRecoverySP WORD NoInitialValue;
:ControlPByte BYTE DataLinkEscape, CarriageReturn;
:ControlZByte BYTE Substitute, CarriageReturn;
:ControlCByte BYTE EndOfText;
:CarriageReturnByte BYTE CarriageReturn;
:SelectedVariable BYTE NoInitialValue;
:AdrsOfVariables ASM TIMES NrOfVariables - 3;
WORD CarriageReturnByte;
$Addr. of the value string of a
$variable, initialized to the
$addr. of the empty string
END ASMTIMES;
WORD ControlPByte;
$Addr. of the value string of
$the 3rd-highest-numbered
$variable, initialized to the
$addr. of a string consisting
$of just Ctrl-P
WORD ControlZByte;
$Addr. of the value string of
$the 2nd-highest-numbered
$variable, initialized to the
$addr. of a string consisting
$of just Ctrl-Z
WORD ControlCByte;
$Addr. of the value string of
$the highest-numbered variable,
$initialized to the addr. of a
$string consisting of just
$Ctrl-C
:CurrentDepthOfBatchStack BYTE 0;
BYTE 0;$This is a constant byte
$NOTE: The conceptually single "BatchStack" actually is implemented as
$2 stacks which are pushed simultaneously, or popped simultaneously.
$The 2 stacks are: NormalSourceForCurrentLine, and BatchInputCursor.
DEF BatchStack = 0; $The definition merely provides
$a symbol which can be cross-
$referenced
:MiddleOfInputLine BYTE FALSE;
$0 means input is at the start
$of a line, any nonzero value
$means input is in the middle of
$a line
:UnsolicitedCharWasTypedIn BYTE FALSE;
$0 means there has not been an
$unsolicited char. typed in, any
$nonzero value means an
$unsolicited char. was typed in
:UnsolicitedChar BYTE NonexistentChar;
$If an unsolicited char. ~= the
$escape char. was typed in, and
$detected during a call of
$CONST, this byte will contain
$the char.; otherwise this byte
$will contain NonexistentChar
MENTION BatchStack; $The following is part of the
$conceptual BatchStack
:NormalSourceForCurrentLine BYTE 0;$0 means batch input, any
$nonzero value means typed
$input
ASM TIMES MaxDepthOfBatchStack - 1;
BYTE NoInitialValue;
END ASMTIMES;
:NormalSourceForNextLine BYTE 0;$0 means batch input, any
$nonzero value means typed
$input
:AfterBatchText WORD NoInitialValue;
$1st addr. after the end of
$batch text, updated after
$storing into a variable if the
$source of char. input is the
$console terminal
:AfterSafeMemory WORD NoInitialValue;
MENTION BatchStack; $The following is part of the
$conceptual BatchStack
:BatchInputCursor WORD AfterModel;
$Addr. of the next batch input
$char.
ASM TIMES MaxDepthOfBatchStack - 1;
WORD NoInitialValue;
END ASMTIMES;
:BatchCONST
PROCEDURE;
CALL DiscoverNormalSourceForCurrentLine;
IF 0 THEN;
CALL GO_OriginalCONST; A >> ?;
IF 0 THEN;
A <- . BatchCONSTReport;
ELSE;
CALL Input1Char;
HL <- Escape; A - .HL >> ?;
IF = THEN;
CALL InputAndInterpretCommandChar;
A <- . BatchCONSTReport;
ELSE;
A -> . UnsolicitedChar;
A <- #FF;
END IF;
END IF;
ELSE;
CALL GO_OriginalCONST;
END IF;
A >> ?;
END PROCEDURE;
:BatchCONIN
PROCEDURE;
? << A <- . MiddleOfInputLine;
IF 0 THEN;
$Starting a new input line
IF;
CALL DiscoverNormalSourceForCurrentLine;
IS 0 AND;
CALL DiscoverNormalSourceForNextLine;
IS ~0 AND;
CALL GO_OriginalCONST; A >> ?;
IS 0 THEN;
CALL RingBellIfEnabled;
END IF;
CALL AdrOfTopOfSourceStackToHL;
A <- . NormalSourceForNextLine; A -> .HL;
END IF;
CALL Input1Char;
HL <- Escape; A - .HL >> ?;
IF = THEN;
CALL InputAndInterpretCommandChar;
REPEAT;
END IF;
A -> B; $Copy the char. that was input
CALL TestWhetherUnsolicitedCharForcesSourceToBeConsole;
IF ~0 THEN;
CALL AdrOfTopOfSourceStackToHL; A -> .HL;
A -> . NormalSourceForNextLine;
0 -> A; A -> . UnsolicitedCharWasTypedIn;
END IF;
CALL DiscoverNormalSourceForCurrentLine;
IF ~0 THEN;
B -> A; $Copy the char. that was typed
$in
? << A - CarriageReturn;
IF = THEN;
0 -> A; A -> . NormalSourceForNextLine;
END IF;
? << A - LineFeed;
IF = THEN;
B <- CarriageReturn; $Change the input char.
END IF;
END IF;
IF;
B -> A; ? << A - LineFeed;
IS = AND;
CALL DiscoverCurrentDepthOfBatchStack;
IS ~0 THEN;
B <- CarriageReturn; $Change the input char.
END IF;
B -> A; $Copy the char. that was input
IF;
? << A - CarriageReturn;
IS = OR;
? << A - NegativeAck;
IS = OR;
? << A - EndOfText;
IS = THEN;
$The next char. will start an input line
0 -> A;
ELSE;
$The next char. will be in the middle of an input line
A <- TRUE; $Just in case the char. was Null
END IF;
A -> . MiddleOfInputLine;
B -> A; $Copy the char. that was input
END PROCEDURE;
:TypeOutChar_rC
PROCEDURE;
BC\/;
CALL GO_OriginalCONOUT;
/\BC; C -> A; ? << A - CarriageReturn;
$Set the condition codes
C <- LineFeed; IF = CALL GO_OriginalCONOUT;
END PROCEDURE;
:Input1CharFromConsoleAndEcho
PROCEDURE;
CALL GO_OriginalCONIN;
A -> C; BC\/;
CALL TypeOutChar_rC;
/\BC; C -> A;
END PROCEDURE;
:DiscoverCurrentDepthOfBatchStack
PROCEDURE;
? << A <- . CurrentDepthOfBatchStack;
END PROCEDURE;
:TestWhetherUnsolicitedCharForcesSourceToBeConsole
PROCEDURE;
? << A <- . UnsolicitedCharWasTypedIn;
END PROCEDURE;
:DiscoverNormalSourceForNextLine
PROCEDURE;
? << A <- . NormalSourceForNextLine;
END PROCEDURE;
:AdrOfTopOfSourceStackToHL
PROCEDURE;
HL <- . CurrentDepthOfBatchStack;
DE <- NormalSourceForCurrentLine; HL + DE -> HL;
END PROCEDURE;
:AdrOfTopOfCursorStackToHL
PROCEDURE;
HL <- . CurrentDepthOfBatchStack; HL * 2 -> HL;
DE <- BatchInputCursor; HL + DE -> HL;
END PROCEDURE;
:DiscoverNormalSourceForCurrentLine
PROCEDURE;
CALL AdrOfTopOfSourceStackToHL;
? << A <- .HL;
END PROCEDURE;
:CursorToDE
PROCEDURE;
CALL AdrOfTopOfCursorStackToHL;
.HL -> E; HL + 1 -> HL; .HL -> D;
END PROCEDURE;
:Fetch1BatchCharAndAdvanceTheCursor
PROCEDURE;
CALL CursorToDE;
.DE -> A; $Input char.
DE + 1 -> DE; D -> .HL; HL - 1 -> HL; E -> .HL;
$Advance the cursor
END PROCEDURE;
$Given rA containing the number of the designated variable, return with
$rHL containing the addr. of the table entry for that variable
:VariableNrToEntryAdr
PROCEDURE;
A -> L; H <- 0; HL * 2 -> HL; DE <- AdrsOfVariables;
HL + DE -> HL;
END PROCEDURE;
$Given rA containing the number of the designated variable
:PushTheBatchStack
PROCEDURE;
$There is no test for stack overflow. Overflow is extremely
$unlikely under the anticipated conditions of use, and even if it
$occurs, probably will not cause serious damage.
HL <- CurrentDepthOfBatchStack; .HL + 1 -> .HL;
CALL AdrOfTopOfSourceStackToHL; .HL <- 0;
$Source of input will be batch
$text
CALL VariableNrToEntryAdr;
.HL -> C; HL + 1 -> HL; .HL -> B;
$Addr. of start of the
$designated variable -> BC
CALL AdrOfTopOfCursorStackToHL;
C -> .HL; HL + 1 -> HL; B -> .HL;
$Addr. of start of the
$designated variable -> top of
$cursor stack
END PROCEDURE;
:PopTheBatchStack
PROCEDURE;
$If this code is free of bugs, stack underflow is impossible
HL <- CurrentDepthOfBatchStack; .HL - 1 -> .HL;
END PROCEDURE;
:Input1Char
PROCEDURE;
HL <- UnsolicitedChar; .HL -> A; ? << A - NonexistentChar;
IF ~= THEN;
.HL <- NonexistentChar;
? << A - CarriageReturn; IF ~= EXIT;
$Test for the end of a variable
CALL DiscoverCurrentDepthOfBatchStack;
IF ~0 THEN;
CALL PopTheBatchStack;
REPEAT;
END IF;
A <- CarriageReturn;
EXIT;
END IF;
IF;
CALL DiscoverNormalSourceForCurrentLine;
IS 0 AND;
CALL GO_OriginalCONST; A >> ?;
IF ~0 THEN;
A -> . UnsolicitedCharWasTypedIn;
END IF;
IS 0 THEN;
CALL Fetch1BatchCharAndAdvanceTheCursor;
ELSE;
CALL GO_OriginalCONIN;
? << A - EndOfText;
IF = THEN;
CALL TestWhetherUnsolicitedCharForcesSourceToBeConsole;
IF ~0 CALL TerminateBatchProcessing;
A <- EndOfText;
END IF;
END IF;
? << A - CarriageReturn; IF ~= EXIT;
$Test for the end of a variable
CALL DiscoverCurrentDepthOfBatchStack;
IF ~0 THEN;
CALL PopTheBatchStack;
REPEAT;
END IF;
A <- CarriageReturn;
END PROCEDURE;
$This procedure is callable only during interpretation of a command
:Input1CharFromSameSourceAsLastChar
PROCEDURE;
IF;
CALL DiscoverNormalSourceForCurrentLine;
IS 0 AND;
CALL TestWhetherUnsolicitedCharForcesSourceToBeConsole;
IS 0 THEN;
CALL Fetch1BatchCharAndAdvanceTheCursor;
ELSE;
CALL GO_OriginalCONIN;
END IF;
END PROCEDURE;
:SelectAVariable
PROCEDURE;
CALL Input1CharFromSameSourceAsLastChar;
A <- A - "0"; IF - GO. ErrInCommand;
? << A - NrOfVariables; IF |>=| GO ErrInCommand;
END PROCEDURE;
:ErrInCommand $*** Not a procedure ***
HL <- . CommandErrRecoverySP; HL -> SP;
GO ReportAndRecoverFromCommandErr;
:InputAndInterpretCommandChar
PROCEDURE;
HL <- 0; HL + SP -> HL; HL -> . CommandErrRecoverySP;
$The char. must be input from the same source as the escape char.,
$regardless of any new unsolicited typed input
CALL Input1CharFromSameSourceAsLastChar;
$Look up the char. in the table
HL <- TableOf2ndChars;
B <- (AfterTableOf2ndChars - TableOf2ndChars) / 3;
DO;
A - .HL >> ?;
IF = THEN;
$Interpret the char.
HL + 1 -> HL; .HL -> E; HL + 1 -> HL; .HL -> D;
HL <-> DE; $Addr. of interpretation
$procedure -> HL
CALL GO_rHL;
CALL TestWhetherUnsolicitedCharForcesSourceToBeConsole;
IF ~0 THEN;
0 -> A; A -> . UnsolicitedCharWasTypedIn;
END IF;
EXIT InputAndInterpretCommandChar;
END IF;
HL + 1 -> HL; HL + 1 -> HL; HL + 1 -> HL;
B - 1 -> B; IF ~0 REPEAT;
END DO;
$If execution passes through here, no match was found
:ReportAndRecoverFromCommandErr
CALL RingBell;
C <- "E"; CALL GO_OriginalCONOUT;
C <- "R"; CALL GO_OriginalCONOUT;
C <- "R"; CALL GO_OriginalCONOUT;
$Test whether the erroneous command char. was input from batch
$text
IF;
CALL DiscoverNormalSourceForCurrentLine;
IS 0 AND;
CALL TestWhetherUnsolicitedCharForcesSourceToBeConsole;
IS 0 THEN;
CALL TerminateBatchProcessing;
END IF;
REPEAT;
END PROCEDURE;
:GO_rHL GO .HL;
:RingBell
PROCEDURE;
C <- Bell; CALL GO_OriginalCONOUT;
END PROCEDURE;
:RingBellIfEnabled
PROCEDURE;
? << A <- . RingForAttn; IF ~0 CALL RingBell;
END PROCEDURE;
:RingBellIfEnabledAndSourceWasBatchText
PROCEDURE;
CALL DiscoverNormalSourceForCurrentLine;
IF 0 THEN;
CALL TestWhetherUnsolicitedCharForcesSourceToBeConsole;
IF 0 CALL RingBellIfEnabled;
END IF;
END PROCEDURE;
:MakeSourceBeConsoleAndRingBellIfChangingAndEnabled
PROCEDURE;
CALL RingBellIfEnabledAndSourceWasBatchText;
CALL AdrOfTopOfSourceStackToHL; .HL <- 1;
END PROCEDURE;
:RequestTypedInput
PROCEDURE;
A <- 1; A -> . NormalSourceForNextLine;
CALL MakeSourceBeConsoleAndRingBellIfChangingAndEnabled;
END PROCEDURE;
:RequestBatchInput
PROCEDURE;
CALL AdrOfTopOfSourceStackToHL;
0 -> A; A -> .HL; A -> . NormalSourceForNextLine;
END PROCEDURE;
:FirstPartOfRequestStoreIntoVariable
PROCEDURE;
$It is forbidden to fetch from a variable and store into a
$variable simultaneously. Otherwise, there would be no guarantee
$that the source of character input is unchanging. If the source
$could change, then any value string which is fetched from batch
$text, would have to be stored a 2nd time in the batch text.
CALL DiscoverCurrentDepthOfBatchStack; IF ~0 GO. ErrInCommand;
$Error if fetching from a
$variable
CALL SelectAVariable; A -> . SelectedVariable;
END PROCEDURE;
:SecondPartOfRequestStoreIntoVariable
PROCEDURE;
A <- . SelectedVariable;
CALL VariableNrToEntryAdr; HL\/; MENTION AdrsOfVariables;
$Addr. of entry in table
$Determine the source of character input
IF;
CALL DiscoverNormalSourceForCurrentLine;
IS 0 AND;
CALL TestWhetherUnsolicitedCharForcesSourceToBeConsole;
IS 0 THEN;
$The source of character input is batch text
CALL CursorToDE; /\HL; E -> .HL; HL + 1 -> HL; D -> .HL;
MENTION AdrsOfVariables; $Copy the current value of the
$cursor, into the entry in
$table AdrsOfVariables for the
$designated variable
$Input and disregard all chars. until the carriage return
$that terminates the variable, except that it is necessary to
$test for the end of batch text
DO;
CALL Fetch1BatchCharAndAdvanceTheCursor;
? << A - CarriageReturn; IF = EXIT;
HL <- Escape; A - .HL >> ?; IF ~= REPEAT;
CALL Fetch1BatchCharAndAdvanceTheCursor;
? << A - FileTerminator; IF ~= REPEAT;
CALL TerminateBatchProcessing;
END DO;
ELSE;
$The source of character input is the console terminal
HL <- . AfterBatchText; HL\/; HL\/;
$Addr. where the 1st char. of
$the variable will be stored
$Input and store all chars. until the carriage return that
$terminates the variable. Do not interpret any chars.
DO;
CALL Input1CharFromConsoleAndEcho;
$Verify that there is at least 1 available byte remaining
$in safe memory
/\DE; $Addr. where the char. will be
$stored
HL <- . AfterSafeMemory;
IF;
E -> A; A - L >> ?;
IS = AND;
D -> A; A - H >> ?;
IS = THEN;
GO. ErrInCommand;
END IF;
C -> A; A -> .DE; $Copy the char. into safe
$memory
DE + 1 -> DE; DE\/; $Addr. where next char. will
$be stored
? << A - CarriageReturn; IF ~= REPEAT;
/\HL; HL -> . AfterBatchText;
/\DE; /\HL; E -> .HL; HL + 1 -> HL; D -> .HL;
MENTION AdrsOfVariables;$Copy the addr. of the 1st
$char. of the value, into the
$entry in table AdrsOfVariables
$for the designated variable
END DO;
END IF;
0 -> A; A -> . MiddleOfInputLine;
END PROCEDURE;
:RequestStoreIntoVariable
PROCEDURE;
CALL FirstPartOfRequestStoreIntoVariable;
CALL SecondPartOfRequestStoreIntoVariable;
END PROCEDURE;
:RequestAnswer
PROCEDURE;
CALL FirstPartOfRequestStoreIntoVariable;
CALL MakeSourceBeConsoleAndRingBellIfChangingAndEnabled;
CALL SecondPartOfRequestStoreIntoVariable;
END PROCEDURE;
:RequestFetchFromVariable
PROCEDURE;
CALL SelectAVariable;
CALL PushTheBatchStack;
END PROCEDURE;
:RequestVariableVariable
PROCEDURE;
CALL RingBellIfEnabledAndSourceWasBatchText;
CALL Input1CharFromConsoleAndEcho;
IF;
A <- A - "0";
IS - OR;
? << A - NrOfVariables;
IS |>=| THEN;
CALL RingBell;
REPEAT;
END IF;
CALL PushTheBatchStack;
END PROCEDURE;
:RequestTypedOutput
PROCEDURE;
CALL Input1Char; A -> C;
IF;
? << A - LineFeed;
IS = AND;
CALL DiscoverCurrentDepthOfBatchStack;
IS ~0 THEN;
C <- CarriageReturn; $Change the input char.
END IF;
BC\/;
CALL TypeOutChar_rC; $Echo the char.
/\BC; C -> A; $Copy the char.
IF;
HL <- Escape; A - .HL >> ?;
IS = OR;
IF;
? << A - EndOfText;
IS = AND;
IF;
CALL DiscoverNormalSourceForCurrentLine;
IS ~0 OR;
CALL TestWhetherUnsolicitedCharForcesSourceToBeConsole;
IS ~0 THEN;
IF THE ABOVE THEN;
IF THE ABOVE THEN;
C -> A; A -> . UnsolicitedChar;
$This will cause the char. to
$be input again
ELSE;
REPEAT;
END IF;
END PROCEDURE;
:TerminateBatchProcessing
PROCEDURE;
CALL RingBellIfEnabled;
HL <- . AdrOf_JMP_CONST; DE <- GO_OriginalCONST; HL <-> DE;
B <- AFTER_GO_BATCH - GO_BATCH_CONST;
DO;
.HL -> A; A -> .DE;
HL + 1 -> HL; DE + 1 -> DE;
B - 1 -> B; IF ~0 REPEAT;
END DO;
A <- 1; A