* PROGRAM NAME: SUB * AUTHOR: RICHARD CONN (From SuperSUB Ver 1.1 by Ron Fowler) * VERSION: 1.4 * DATE: 10 OCT 81 * PREVIOUS VERSIONS: 1.3 (7 OCT 81) * PREVIOUS VERSIONS: 1.2 (5 OCT 81), 1.1 (3 OCT 81), 1.0 (1 OCT 81) VERS EQU 14 ; Version Number ; ; ;************************************************ ;* EXTENDED SUBMIT FOR * ;* CP/M * ;************************************************ ; ; SUB is derived from Ron's SuperSUB program; it provides a different ; format for the command line, a command-search hierarchy like CCPZ, a ; resetting of the DMA address, several additional functions, and there are ; several other additions/changes. ; ; REVISED 09/13/81 (RGF): added control character translation ; fixed bug in line number reporting ; ; VERSION 1.1 by Ron Fowler ; 2/18/81 (first written) WESTLAND, MICH. ; ; ; This program is intended as a replacement for the ; SUBMIT program provided with CP/M. It provides sev- ; eral new facilities: ; 1) Nestable SUBMIT runs ; 2) Interactive entry of SUBMIT job (no need ; to use an editor for simple SUBMIT runs) ; 3) Command line entry of small SUBMIT jobs ; 4) Ability to enter blank lines in an edited ; SUBMIT file ; 5) User customization of number of parameters ; and drive to send $$$.SUB to ; ; For full details along with examples, see the ac- ; companying documentation file. ; --Ron Fowler ; ; ; DEFINE BOOLEANS ; FALSE EQU 0 TRUE EQU NOT FALSE ; ;************************************************************ ; ; -- User customizable options -- ; FORCE$SUB EQU FALSE ;TRUE IF SUBMITTED FILE MUST BE OF TYPE .SUB TIME$CONST EQU 0C000H ;DELAY FOR RINGING BELL DFLT$USER EQU 0 ;DEFAULT USER NUMBER TO SEARCH FOR .SUB FILE DFLT$DISK EQU 0 ;DEFAULT DISK TO SEARCH FOR .SUB FILE (0=A) NPAR EQU 20 ;NUMBER OF ALLOWABLE PARAMETERS SUBDRV EQU 1 ;MAKE 0 FOR DFLT, 1,2,3,ETC FOR A,B,C QUIET EQU FALSE ;SET TO TRUE TO ELIMATE SIGN-ON MSG CPBASE EQU 0 ;SET TO 4200H FOR HEATH CP/M OPT EQU '/' ;OPTION DELIMITER CHAR PDELIM EQU '$' ;PARAMETER DELIMITER ; ; ; ;************************************************************ ; ; CP/M DEFINITIONS ; FGCHAR EQU 1 ;GET CHAR FUNCTION FPCHAR EQU 2 ;PRINT CHAR FUNCTION DIRIOF EQU 6 ;DIRECT CONSOLE I/O PRINTF EQU 9 ;PRINT STRING FUNCTION RDBUF EQU 10 ;READ CONSOLE BUFFER DETVERS EQU 12 ;GET VERSION NUMBER LOGIN EQU 14 ;LOG IN DISK OPENF EQU 15 ;OPEN FILE FUNCTION CLOSEF EQU 16 ;CLOSE FILE FUNCTION DELETF EQU 19 ;DELETE FILE FUNCTION READF EQU 20 ;READ RECORD FUNCTION WRITEF EQU 21 ;WRITE RECORD FUNCTION MAKEF EQU 22 ;MAKE (CREATE) FILE FUNCTION GETDSK EQU 25 ;RETURN CURRENT DISK SETDMA EQU 26 ;SET DMA ADDRESS UCODE EQU 32 ;GET/SET USER CODE ; BDOS EQU CPBASE+5 ; FCB EQU 5CH ;DEFAULT FILE CONTROL BLOCK FCBEX EQU 12 ;FCB OFFSET TO EXTENT FIELD FCBRC EQU 15 ;FCB OFFSET TO RECORD COUNT FCBNR EQU 32 ;FCB OFFSET TO NEXT RECORD FN EQU 1 ;FCB OFFSET TO FILE NAME FT EQU 9 ;FCB OFFSET TO FILE TYPE TBUF EQU CPBASE+80H ;DEFAULT BUFFER TPA EQU CPBASE+100H ;TRANSIENT PROGRAM AREA ; PUTCNT EQU TBUF ;COUNTER FOR OUTPUT CHARS ; ; DEFINE SOME TEXT CHARACTERS ; CTRLC EQU 3 ;^C BEL EQU 7 ;RING BELL CR EQU 13 ;CARRIAGE RETURN LF EQU 10 ;LINE FEED TAB EQU 9 ; ; START OF PROGRAM CODE ; ORG TPA ; ; Begin Execution of SUB.COM ; SUBMIT: LXI H,0 ;SAVE STACK IN CASE DAD SP ; ONLY HELP REQUESTED SHLD SPSAVE ;(NOT OTHERWISE USED) LXI SP,STACK MVI C,SETDMA ;SET DMA ADDRESS LXI D,TBUF ;SET DMA TO TBUF CALL BDOS CALL START ; ; SIGN ON MESSAGE ; IF NOT QUIET DB 'SUB Version ',VERS/10+'0','.',(VERS MOD 10)+'0' DB ' -- Extended Indirect Command File Facility$' ENDIF ; START: POP D ;RETRIEVE STRING POINTER MVI C,PRINTF CALL BDOS ;PRINT THE SIGN-ON LDA FCB+1 ;ANYTHING ON CMD LINE? CPI ' ' JZ HELP ;NO, GO PRINT HELP CALL INITVAR ;INITIALIZE THE VARIABLE AREA CALL GETPAR ;GET COMMAND LINE PARAMETERS AND EXTRACT OPTION CALL ABORT ;PERFORM ABORT IF FLAG SET CALL SETUP ;SET UP READ OF SUBMIT FILE CALL RDFILE ;READ THE SUBMIT FILE CALL WRSET ;SET UP WRITE OF "$$$.SUB" CALL WRSUB ;WRITE "$$$.SUB" JMP CPBASE ;GO START THE SUBMIT ; ; ; SETUP SETS UP THE FILE CONTROL BLOCK ; FOR READING IN THE .SUB TEXT FILE ; SETUP: LXI H,FCB+FT ;LOOK AT FIRST CHAR OF MOV A,M ;FILE TYPE. IF IT IS CPI ' ' ;BLANK, THEN GO MOVE JZ PUTSUB ;"SUB" INTO FT FIELD IF FORCE$SUB ;FILE TYPE MUST BE OF .SUB LXI D,SUBTYP ;FILE TYPE MUST BE .SUB MVI B,3 ;3 BYTES CALL COMPAR JNZ NOTFND ;FILE NOT FOUND IF NO TYPE MATCH ENDIF RET ; IF NOT BLANK, THEN ACCEPT ANY FILE TYPE ; ; MOVE "SUB" INTO THE FILE TYPE ; PUTSUB: XCHG ;BY CONVENTION, MOVE FROM LXI H,SUBTYP ; @HL TO @DE MVI B,3 CALL MOVE RET ; ; MOVE # BYTES IN B REGISTER FROM @HL TO @DE ; MOVE: MOV A,M ;PICK UP STAX D ;PUT DOWN INX H ;I'M SURE INX D ; YOU'VE SEEN THIS DCR B ; BEFORE... JNZ MOVE ;100 TIMES AT LEAST RET ;I KNOW I HAVE! ; ; GETPAR MOVES THE SUBSTITUTION PARAMETERS SPECIFIED ; IN THE COMMAND LINE INTO MEMORY, AND STORES THEIR ; ADDRESSES IN THE PARAMETER TABLE. THIS ALLOWS ; SUBSTITUTION OF $1, $2, ETC., IN THE SUBMIT COMMANDS ; WITH THEIR ACTUAL VALUES SPECIFED IN THE COMMAND ; LINE. ; GETPAR: XRA A ;A=0 STA AFLAG ;TURN OFF ABORT COMMAND LXI H,TBUF+1 ;WHERE WE FIND THE COMMAND TAIL CALL SCANTO ;SKIP SUBMIT FILE NAME STA OPTION ;FIRST CHAR OF CMD LINE IS OPTION RC ;LINE ENDED? CPI OPT ;NO, CHECK OPTION JNZ GLP0 ;NOT KEYBOARD INP, READ FILE INX H ;POINT PAST '/' MOV A,M ;GET OPTION CHAR CPI 'A' ;ABORT COMMAND JZ GPARABT CPI 'D' ;DO COMMAND JZ GPARDO CPI 'I' ;INTERACTIVE MODE JZ GPARINT ;SKIP TO EOL AND RETURN JMP HELP ;HELP OTHERWISE GPARABT: MVI A,0FFH ;TURN ON ABORT FLAG STA AFLAG INX H ;GET POSSIBLE BELL OPTION MOV A,M CPI 'B' ;BELL OPTION RNZ MVI A,0FFH ; SET BELL FLAG STA BELL$FLAG RET GPARINT: XRA A ;TURN OFF COMMAND LINE INPUT STA CLFLAG RET GPARDO: INX H ;SKIP TO MOV A,M ;GET CHAR CPI ' '+1 ;LOOK FOR OR LESS JNC GPARDO SLSCAN: SHLD CLPTR ;SAVE CMD LINE PTR MOV A,M ;KBD IS SOURCE, GET EOL FLAG STA CLFLAG ;SAVE AS EOL FLAG CPI ' ' ;ALLOW SPACES AFTER '/' RNZ ;GOT NON-BLANK, DONE INX H ;ELSE CONTINUE SCAN JMP SLSCAN GLP0: MOV A,M ;INPUT IS FROM A .SUB FILE..THIS INX H ; CODE SKIPS OVER THE NAME OF ORA A ; THE SUB FILE TO GET TO THE RZ ; COMMAND LINE PARAMETERS CPI ' ' JZ GLP CPI TAB JNZ GLP0 GLP: CALL SCANTO ;PASS UP THE BLANKS RC ;CY RETURNED IF END OF CMD LINE CALL PUTPAR ;NOW PUT THE PARAMETER INTO MEM RC ;CY RETURNED IF END OF CMD LINE JMP GLP ;GET THEM ALL ; ; SCANTO SCANS PAST BLANKS TO THE FIRST NON-BLANK. IF ; END OF COMMAND LINE FOUND, RETURNS CARRY SET. ; SCANTO: MOV A,M INX H ORA A ;SET FLAGS ON ZERO STC ;IN CASE ZERO FOUND (END OF CMD LIN) RZ CPI ' ' JZ SCANTO ;SCAN PAST BLANKS CPI TAB ;DO TABS TOO, JUST FOR JZ SCANTO ; GOOD MEASURE DCX H ;FOUND CHAR, POINT BACK TO IT ORA A ;INSURE CARRY CLEAR RET ; ; PUTPAR PUTS THE PARAMETER POINTED TO BY HL INTO ; MEMORY POINTED TO BY "TXTPTR". ALSO STORES THE ; ADDRESS OF THE PARAMETER INTO THE PARAMETER TABLE ; FOR EASY ACCESS LATER, WHEN WE WRITE $$$.SUB ; PUTPAR: PUSH H ;SAVE POINTER TO PARM LHLD TXTPTR ;NEXT FREE MEMORY XCHG ; INTO DE LHLD TBLPTR ;NEXT FREE AREA OF TABLE MOV A,M ;NON-ZERO IN TABLE ORA A ; INDICATES TABLE JNZ PAROVF ; TABLE OVERFLOW (TOO MANY PARMS) MOV M,E ;STORE THE PARM ADRS INX H MOV M,D INX H SHLD TBLPTR ;SAVE TABLE PNTR FOR NEXT TIME POP H ;GET BACK PARM POINTER PUSH D ;SAVE FREE MEM POINTER BECAUSE ; WE WILL HAVE TO HAVE IT BACK ; LATER TO STORE THE LENGTH INX D ;POINT PAST LENGTH STORAGE MVI B,0 ;INITIALIZE LENGTH OF PARM PPLP: MOV A,M ;GET NEXT BYTE OF PARM INX H ORA A ;TEST FOR END OF CMD LINE JZ PP2 ;JUMP IF END CPI ' ' ;TEST FOR END OF COMMAND JZ PP2 CPI TAB ;TAB ALSO ENDS COMMAND JZ PP2 STAX D ;PUT PARAMETER BYTE-BY-BYTE INX D ;INTO FREE MEMORY INR B ;BUMP LENGTH JMP PPLP PP2: XCHG SHLD TXTPTR ;NEW FREE MEMORY POINTER POP H ;REMEMBER OUR LENGTH POINTER? MOV M,B ;STORE THE LENGTH XCHG ;HAVE TO RETN HL > CMD LINE ORA A ;NOW RETURN END OF LINE FLAG STC RZ ;RETURN CY IF ZERO (EOL MARK) CMC RET ; ; ; ABORT CHECKS TO SEE IF THE ABORT FLAG IS SET AND ; EXECUTES THE ABORT FUNCTION IF SO ; ; ABORT: LDA AFLAG ;GET THE FLAG ORA A ;0=NO RZ LXI D,ABMSG ;PRINT ABORT MESSAGE MVI C,PRINTF CALL BDOS CALL CHARINB ;GET RESPONSE CPI 'A' ;ABORT? JZ ABORT0 ;RETURN TO CP/M CPI CTRLC ;ABORT? JNZ ABORT1 ;RETURN TO CP/M ABORT0: LXI D,SUBFCB ;DELETE SUBMIT FILE MVI C,DELETF CALL BDOS LXI D,ABMSG1 ;PRINT DONE MESSAGE MVI C,PRINTF CALL BDOS JMP CPBASE ;RETURN TO CP/M ABORT1: LXI D,ABMSG2 ;PRINT CONTINUATION MESSAGE MVI C,PRINTF CALL BDOS JMP CPBASE ; RETURN TO CP/M ABMSG: DB CR,LF,'Abort SUBMIT File' DB CR,LF,'Do you wish to abort execution?' DB CR,LF,' Enter A or ^C to Abort or anything else to ' DB 'continue - $' ABMSG1: DB 'Execution Aborted$' ABMSG2: DB 'Continuing Execution$' ; ; INPUT CHAR FROM CON:; RING BELL EVERY SO OFTEN IF FLAG SET ; CHARINB: LDA BELL$FLAG ; GET FLAG ORA A ; 0=NO JZ CHARIN PUSH H ; SAVE HL CHARINB$LOOP: LXI H,TIME$CONST ; GET TIME CONSTANT CHARINB$LOOP1: DCX H ; COUNT DOWN MOV A,H ORA L JNZ CHARINB$LOOP1 MVI E,0FFH ; REQUEST STATUS MVI C,DIRIOF ; DIRECT I/O CALL BDOS ORA A ; ANY INPUT? JNZ CHARINB$DONE MVI E,BEL ; RING BELL MVI C,2 ; OUTPUT TO CON: CALL BDOS JMP CHARINB$LOOP CHARINB$DONE: POP H ; RESTORE HL JMP CHARIN1 ; ; INPUT CHAR FROM CON:; CAPITALIZE IT AND ECHO ; CHARIN: MVI C,FGCHAR ;GET CHAR CALL BDOS CHARIN1: CALL UCASE ;CAPITALIZE PUSH PSW ;SAVE IT CALL CRLF ;NEW LINE POP PSW ;GET IT RET ; ; RDFILE READS THE .SUB FILE SPECIFIED ; IN THE SUBMIT COMMAND INTO MEMORY ; RDFILE: MVI A,0FFH ;SET NO DISK/USER CHANGE STA DDISK STA DUSER LXI H,0 ;INIT LINE NUMBER SHLD LINNUM LDA OPTION ;USING A FILE? CPI OPT ;OPT OPTION TELLS JNZ RDFILE1 ;JUMP IF NOT LXI D,RDLMSG ;READING LINE MESSAGE MVI C,PRINTF CALL BDOS JMP LINE RDFILE1: LXI D,RDFMSG ;READING FILE MESSAGE MVI C,PRINTF CALL BDOS * CHECK FOR .SUB FILE IN CURRENT USER/CURRENT DISK LXI D,FCB ;WE ARE, OPEN IT CALL INITFCB ;INIT FCB MVI C,OPENF CALL BDOS INR A ;IF 0FFH RETURNED, THEN FILE NOT FOUND JNZ LINE ;CONTINUE IF FOUND * DETERMINE IF CP/M 1.4 AND SKIP USER NUMBER CHANGE IF SO MVI C,DETVERS CALL BDOS MOV A,H ;GET RETURNED VALUE ORA L JZ LOG$DEF ;LOG IN DRIVE A: * GET CURRENT DISK FOR LATER MVI C,GETDSK ;BDOS FCT CALL BDOS STA DDISK ;DEFAULT DISK * DETERMINE IF CP/M 1.4 AND SKIP USER NUMBER PROCESSING IF SO MVI C,DETVERS ;GET VERSION NUMBER IN HL CALL BDOS MOV A,H ;CHECK FOR 0 ORA L JZ LOG$DEF ;LOG IN DRIVE A: IF NOT ALREADY LOGGED IN * GET CURRENT USER NUMBER FOR LATER MVI E,0FFH ;GET USER CODE MVI C,UCODE ;BDOS FCT CALL BDOS STA DUSER ;DEFAULT USER CPI DFLT$USER ;SAME AS DEFAULT? JZ LOG$DEF ;GO AHEAD AND GET DEFAULT DRIVE IF SO * LOOK FOR .SUB FILE IN DEFAULT USER/CURRENT DISK MVI E,DFLT$USER ;SET DEFAULT USER MVI C,UCODE ;SET USER CODE CALL BDOS LXI D,FCB ;OPEN .SUB FILE CALL INITFCB ;RESET FCB MVI C,OPENF CALL BDOS INR A ;IF 0FFH RETURN, THEN FILE NOT FOUND JNZ LINE ;CONTINUE IF FOUND * LOOK FOR .SUB FILE IN DEFAULT USER/DEFAULT DISK LOG$DEF: LDA DDISK ;ON DEFAULT DISK? CPI DFLT$DISK JZ NOTFND ;NOT FOUND IF SO MVI E,DFLT$DISK ;LOG IN DEFAULT DISK MVI C,LOGIN CALL BDOS LXI D,FCB ;OPEN .SUB FILE CALL INITFCB ;INIT FCB MVI C,OPENF CALL BDOS INR A ;IF 0FFH RETURN, THEN FILE NOT FOUND JZ NOTFND ;FILE NOT FOUND LINE: LHLD LINNUM ;BUMP LINE NUMBER INX H SHLD LINNUM LHLD PREV ;GET PREV PREVIOUS LINE POINTER XCHG LHLD TXTPTR ;GET CURRENT FREE MEM POINTER SHLD PREV ;MAKE IT THE PREV LINE (FOR NXT PASS) MOV M,E ;STORE AT BEGIN OF CURRENT LINE, INX H ; A POINTER TO THE PREVIOUS MOV M,D INX H PUSH H ;LATER WE WILL PUT LENGTH HERE INX H ;SKIP PAST LENGTH MVI C,0 ;INITIALIZE LENGTH TO ZERO LLP: CALL GNB ;GET NEXT BYTE FROM INPUT SOURCE JC EOF ;CY SET IF END OF FILE FOUND ANI 7FH ;MASK OUT MSB CALL UCASE ;CONVERT TO UPPER CASE CPI 1AH ;SEE IF CPM END OF FILE INDICATOR JZ EOF CPI LF ;IGNORE LINEFEEDS JZ LLP CPI CR ;IF IT'S A CARRIAGE RETURN, JZ EOL ; THEN DO END OF LINE MOV M,A ;STORE ALL OTHERS INTO MEMORY INX H CALL SIZE ;MAKE SURE NO MEMORY OVERFLOW INR C ;BUMP CHAR COUNT JM LENERR ;MAX OF 128 CHARS PER LINE JMP LLP ;GO DO NEXT CHAR RDFMSG: DB CR,LF,'Process SUBMIT File$' RDLMSG: DB CR,LF,'Input SUBMIT File Command Lines$' ; ; DO END OF LINE SEQUENCE ; EOL: SHLD TXTPTR ;SAVE FREE MEMORY POINTER POP H ;CURRENT LINE'S LENGTH POINTER MOV M,C ;STORE LENGTH AWAY JMP LINE ;GO DO NEXT LINE ; ; END OF TEXT FILE ; EOF: SHLD TXTPTR ;SAVE FREE MEMORY POINTER POP H ;CURRENT LINE'S LENGTH POINTER MOV M,C ;STORE LENGTH AWAY RET ;ALL DONE READING SUB FILE ; ; INITIALIZE KEY FIELDS OF FCB PTED TO BY DE ; INITFCB: PUSH H ;SAVE HL XRA A ;A=0 STAX D ;SET DEFAULT DRIVE LXI H,FCBEX ;PT TO EX FIELD DAD D MOV M,A ;SET EX FIELD TO ZERO LXI H,FCBNR ;PT TO CR FIELD DAD D MOV M,A ;SET CR FIELD TO ZERO POP H ;RESTORE HL RET ; ; GET NEXT BYTE FROM INPUT FILE ; GNB: PUSH H ;DON'T ALTER ANYBODY PUSH D PUSH B LDA OPTION ;INPUT FROM .SUB FILE? CPI OPT ;TOLD BY ORIG CMD LINE OPTION JNZ NSLASH ;JUMP IF WE ARE CALL GNBKBD ;NO, GET A BYTE FROM KBD INPUT JMP GNBXIT ;THEN LEAVE NSLASH: LDA IBP ;GET BUFFER POINTER ORA A ;PAST END? CM FILL ;WRAPPED AROUND JNC GNB1 ;NO END OF FILE * RESTORE CURRENT USER/DISK IF CHANGED LDA DDISK ;RESTORE CURRENT DISK MOV E,A MVI C,LOGIN ;BDOS FCT CPI 0FFH ;IF FFH, NO CHANGE CNZ BDOS LDA DUSER ;RESTORE CURRENT USER MOV E,A MVI C,UCODE ;BDOS FCT CPI 0FFH ;IF FFH, NO CHANGE CNZ BDOS MVI A,1AH ;FAKE EOF GNB1: MOV E,A ;PUT IN DE MVI D,0 INR A ;POINT TO NEXT STA IBP ;PUT AWAY LXI H,TBUF ;NOW OFFSET INTO BUFR DAD D MOV A,M ;GET CHAR THERE GNBXIT: POP B ;RESTORE EVERYBODY POP D POP H ORA A ;TURN ON CARRY RET ; ; FILL INPUT BUFFER ; FILL: MVI C,READF LXI D,FCB CALL BDOS ORA A ;GOT GOOD READ? MVI A,0 ;(NEW BUF PTR) STC RNZ ;RETN CY=EOF CMC ;NO EOF, NO CY RET ; ; COME HERE TO GET A .SUB CHARACTER WHEN ; WE'RE NOT USING A .SUB FILE ("/" OPTION) ; GNBKBD: LDA CLFLAG ;USE CP/M CMD LINE? ORA A JNZ GNBCL ;THEN GO DO IT LDA CLCNT ;NOT, CHECK LOCAL ORA A ; CMD LINE CHAR COUNT CM CLFILL ;REFILL WHEN IT WRAPS BACK JC GKEND ;GOT CARRY (FROM CLFILL), RETURN EOF DCR A ;COUNT DOWN STA CLCNT JP GNBCL ;IF PLUS, BUFFER NOT EMPTY MVI A,CR ;OUT OF CHARS, RETURN A CR RET GKEND: MVI A,1AH ;RETURN EOF RET ; ; GET NEXT BYTE OF INPUT FROM CMD LINE @CLPTR ; GNBCL: LHLD CLPTR ;LOAD THE POINTER MOV A,M ;GET THE CHAR INX H ;BUMP POINTER FOR NEXT TIME SHLD CLPTR CPI ';' ;LOGICAL END-OF-LINE? JNZ NSEMI ;JUMP IF NOT MVI A,CR ;YES, TRANSLATE IT RET NSEMI: ORA A ;PHYSICAL END-OF-LINE RNZ ;THIS ONLY NEEDED WHEN INPUT ; SOURCE IS ORIG CPM CMD LINE MVI A,1AH ;TRANSLATE THAT TO END OF FILE RET ; ; SUBROUTINE TO RE-FILL THE LOCAL COMMAND LINE ; CLFILL: LXI D,PROMPT ;PRINT A PROMPT MVI C,PRINTF ;USE CP/M FUNCT 9 CALL BDOS LXI D,CLBUF ;NOW FILL THE BUFFER MVI C,RDBUF CALL BDOS LDA CLCNT ;RETURN WITH COUNT IN A LXI H,CLTEXT ;RESET THE CMD LINE POINTER SHLD CLPTR ORA A ;SET CY ON LEN NZ STC RZ CMC RET ; ; MAKE SURE NO MEMORY OVERFLOW ; SIZE: LDA BDOS+2 ;HIGHEST PAGE POINTER DCR A ;MAKE IT BE UNDER BDOS CMP H ;CHECK IT AGAINST CURRENT PAGE RNC ;NC=ALL OKAY JMP MEMERR ;OTHERWISE ABORT ; ; SET UP THE $$$.SUB FILE ; FOR WRITING ; WRSET: LXI D,WRSUBMSG MVI C,PRINTF CALL BDOS LXI D,SUBFCB MVI C,OPENF CALL BDOS ;OPEN THE FILE INR A ;CHECK CPM RETURN JZ NONE1 ;NONE EXISTS ALREADY ; ; $$$.SUB EXISTS, SO SET ; FCB TO APPEND TO IT ; LDA SUBFCB+FCBRC ;GET RECORD COUNT STA SUBFCB+FCBNR ;MAKE NEXT RECORD RET ; ; COME HERE WHEN NO $$$.SUB EXISTS ; NONE1: LXI D,SUBFCB MVI C,MAKEF CALL BDOS INR A JZ NOMAKE ;0FFH=CAN'T CREATE FILE RET ; WRSUBMSG: DB CR,LF,'Writing SUBMIT Execution File to Disk$' ; ; WRITE THE "$$$.SUB" FILE ; WRSUB: LHLD PREV ;THIS CODE SCANS BACKWARD MOV A,H ; THRU THE FILE STORED IN ORA L ; MEMORY TO THE FIRST NON- JZ NOTEXT ; NULL LINE. IF NONE IS MOV E,M ; FOUND, ABORTS INX H MOV D,M ;HERE, WE PICK UP PNTR TO PREV LINE INX H ;NOW WE POINT TO LENGTH XCHG ;WE NEED TO STORE AWAY SHLD PREV ; POINTER TO PREV LINE XCHG MOV A,M ;NOW PICK UP THE LENGTH ORA A ;SET Z FLAG ON LENGTH JNZ WRNTRY ;GOT LINE W/LENGTH: GO DO IT LHLD LINNUM ;NOTHING HERE, FIX LINE NUMBER DCX H ;(WORKING BACKWARD NOW) SHLD LINNUM JMP WRSUB WRLOP: LHLD PREV ;GET PREV LINE POINTER MOV A,H ORA L ;IF THERE IS NO PREV LINE JZ CLOSE ; THEN WE ARE DONE MOV E,M ;ELSE SET UP PREV FOR NEXT INX H ; PASS THRU HERE MOV D,M INX H XCHG ;NOW STORE IT AWAY SHLD PREV XCHG WRNTRY: CALL PUTLIN ;WRITE THE LINE TO THE FILE LHLD LINNUM ;BUMP THE LINE NUMBER DCX H ;DOWN (WORKING BACK NOW) SHLD LINNUM JMP WRLOP ; ; $$$.SUB IS WRITTEN, CLOSE THE FILE ; CLOSE: LXI D,SUBFCB MVI C,CLOSEF JMP BDOS ; ; THIS SUBROUTINE WRITES A LINE ; TO THE $$$.SUB FILE BUFFER, ; AND FLUSHES THE BUFFER AFTER ; THE LINE IS WRITTEN. ; PUTLIN: MOV A,M ;PICK UP LENGTH BYTE INX H ;POINT PAST IT STA GETCNT ;MAKE A COUNT FOR "GET" SHLD GETPTR ;MAKE A POINTER FOR "GET" LXI H,TBUF+1 ;TEXT GOES AFTER LENGTH SHLD PUTPTR ;MAKE POINTER FOR "PUT" XRA A ;INITIALIZE PUT COUNT STA PUTCNT MOV B,L ;COUNT FOR CLEAR LOOP CLR: MOV M,A ;ZERO OUT BUFFER LOC INX H INR B ;COUNT JNZ CLR ; ; THIS LOOP COLLECTS CHARACTERS ; FROM THE LINE STORED IN MEMORY ; AND WRITES THEM TO THE FILE. ; IF THE "$" PARAMETER SPECIFIER ; IS ENCOUNTERED, PARAMETER SUB- ; STITUTION IS DONE ; PUTLP: CALL GETCHR ;PICK UP A CHARACTER JC FLUSH ;CY = NO MORE CHAR IN LINE CPI '^' ;CONTROL-CHAR TRANSLATE PREFIX? JNZ NOTCX CALL GETCHR ;YES, GET THE NEXT JC CCERR ;ERROR: EARLY END OF INPUT SUI '@' ;MAKE IT A CONTROL-CHAR JC CCERR ;ERROR: TOO SMALL CPI ' ' JNC CCERR ;ERROR: TOO LARGE NOTCX: CPI PDELIM ;PARAMETER SPECIFIER? JNZ STOBYT ;IF NOT, JUST WRITE CHAR LDA OPTION ;CHECK OPTION: '$' DOESN'T CPI OPT ; COUNT IN OPT MODE MVI A,PDELIM ;(RESTORE THE '$') JZ STOBYT CALL LKAHED ;PEEK AT NEXT CHAR JC PARERR ;LINE ENDING MEANS PARAM ERR CPI PDELIM ;ANOTHER "$"? JNZ SUBS ;IF NOT THEN GO DO SUBSTITUTION CALL GETCHR ;GET THE 2ND "$" (WE ONLY LOOKED ; AHEAD BEFORE) STOBYT: CALL PUTCHR ;WRITE CHAR TO FILE JMP PUTLP ; ; PARAMETER SUBSTITUTION...LOOKS UP THE ; PARAMETER # AFTER THE "$" AND PLUGS IT ; IN IF IT EXISTS. ; SUBS: CALL NUMTST ;IT BETTER BE A NUMBER JC PARERR ; OTHERWISE PARAM ERROR MVI B,0 ;INITIALIZE PARM # JMP LPNTRY ;WE JOIN LOOP IN PROGRESS... SUBLP: CALL LKAHED ;LOOK AT NEXT CHAR JC DOSUBS ;IF LINE EMPTY, THEN PLUG IN PARM CALL NUMTST ;CHECK FOR NUMERIC JC DOSUBS ;DONE IF NOT LPNTRY: CALL GETCHR ;NOW REMOVE THE CHAR FROM INPUT STREAM SUI '0' ;REMOVE ASCII BIAS MOV C,A ;SAVE IT MOV A,B ;OUR ACCUMULATED COUNT ADD A ;MULTIPLY BY TEN ADD A ADD B ADD A ADD C ;THEN ADD IN NEW DIGIT MOV B,A ;RESTORE COUNT JMP SUBLP ; ; PERFORM THE SUBSTITUTION ; DOSUBS: MOV A,B ;GET PARM # DCR A ;MAKE ZERO RELATIVE JM PARERR ;OOPS CALL LOOKUP ;LOOK IT UP IN PARM TABLE JC PARERR ;IT'S NOT THERE MOV B,A ;LENGTH IN B SUBLP1: INR B ;TEST B FOR ZERO DCR B JZ PUTLP ;DONE MOV A,M ;GET CHAR OF REAL PARAMETER INX H ;POINT PAST FOR NEXT TIME PUSH H ;SAVE REAL PARM POINTER CALL PUTCHR ;PUT IT IN THE FILE POP H ;GET BACK REAL PARM POINTER DCR B ;COUNTDOWN JMP SUBLP1 ; ; COME HERE WHEN A LINE IS FINISHED, ; AND WE NEED TO WRITE THE BUFFER TO DISK ; FLUSH: LXI D,SUBFCB MVI C,WRITEF CALL BDOS ORA A JNZ WRERR ;CPM RETURNED A WRITE ERROR RET ; ; GETCHR GETS ONE CHAR FROM ; LINE STORED IN MEMORY ; GETCHR: LXI H,GETCNT MOV A,M ;PICK UP COUNT DCR A ;REMOVE THIS CHAR STC ;PRESET ERROR RM ;RETURN CY IF OUT OF CHARS MOV M,A ;UPDATE COUNT LHLD GETPTR ;CURRENT CHAR POINTER MOV A,M ;PICK UP CHAR INX H ;BUMP POINTER SHLD GETPTR ;PUT IT BACK CMC ;TURN CARRY OFF RET ; ; PUTCHR PUTS ONE CHAR TO ; THE OUTPUT BUFFER ; PUTCHR: LXI H,PUTCNT INR M ;INCREMENT COUNT JM LENERR ;LINE WENT TO > 128 CHARS LHLD PUTPTR ;GET BUFFER POINTER ANI 7FH ;MASK OUT MSB MOV M,A ;PUT CHAR THERE INX H ;BUMP POINTER SHLD PUTPTR ;PUT IT BACK RET ;ALL DONE ; ; LOOK AHEAD ONE CHAR IN ; THE INPUT STREAM. SET ; CARRY IF NONE LEFT. ; LKAHED: LDA GETCNT ORA A ;SEE IF COUNT IS DOWN TO ZERO STC ;PRE SET INDICATOR RZ MOV A,M ;PICK UP CHAR CMC ;TURN OFF CARRY FLAG RET ; ; LOOK UP PARAMETER WITH NUMBER IN ; A REG. RETURN A=LENGTH OF PARM, ; AND HL => PARAMETER ; LOOKUP: CPI NPAR JNC PAROVF ;PARM # TOO HIGH MOV L,A MVI H,0 ;NOW HAVE 16 BIT NUMBER DAD H ;DOUBLE FOR WORD OFFSET LXI D,TABLE DAD D ;DO THE OFFSET MOV E,M ;GET ADDRESS OF PARM INX H MOV D,M MOV A,D ;ANYTHING THERE? ORA E JNZ LKUPOK XRA A ;NO, ZERO LENGTH RET LKUPOK: XCHG ;NOW IN DE MOV A,M ;PICK UP LENGTH INX H ;POINT PAST LENGTH RET ; ; UTILITY COMPARE SUBROUTINE ; COMPAR: LDAX D CMP M RNZ INX H INX D DCR B JNZ COMPAR RET ; ; NUMERIC TEST UTILITY SUBROUTINE ; NUMTST: CPI '0' RC CPI '9'+1 CMC RET ; ;DECIMAL OUTPUT ROUTINE ; DECOUT: PUSH B PUSH D PUSH H LXI B,-10 LXI D,-1 ; DECOU2: DAD B INX D JC DECOU2 LXI B,10 DAD B XCHG MOV A,H ORA L CNZ DECOUT MOV A,E ADI '0' CALL TYPE POP H POP D POP B RET ; ; PRINT CR, LF ON CONSOLE ; CRLF: MVI A,CR CALL TYPE MVI A,LF ; ; PRINT CHAR IN A ON CONSOLE ; TYPE: PUSH H ;SAVE REGS PUSH D PUSH B MOV E,A ;PUT IN E FOR CP/M MVI C,FPCHAR CALL BDOS ;PRINT IT POP B ;RESTORE ALL POP D POP H RET ; ; CONVERT CHAR IN A TO UPPER CASE ; UCASE: CPI 'a' ;VALIDATE CASE RC CPI 'z'+1 RNC ANI 5FH ;GOT LC, CONV TO UC RET ; ; ERROR HANDLERS ; WRERR: CALL ERRXIT DB 'Disk Full$' NOMAKE: CALL ERRXIT DB 'Directory Full$' MEMERR: CALL ERRXIT DB 'Memory Full$' NOTFND: CALL ERRXIT DB 'SUBMIT File Not Found$' PARERR: CALL ERRXIT DB 'Parameter$' PAROVF: CALL ERRXIT DB 'Too Many Parameters: $' LENERR: CALL ERRXIT DB 'Line too Long: $' NOTEXT: CALL ERRXIT DB 'SUBMIT File Empty$' CCERR: CALL ERRXIT DB 'Control Character$' ERRXIT: LXI D,SUBERR ;PRINT ERROR HERALD MVI C,PRINTF CALL BDOS POP D MVI C,PRINTF CALL BDOS LXI D,ERRMSG ;PRINT 2ND HALF MSG MVI C,PRINTF CALL BDOS LHLD LINNUM ;TELL LINE NUMBER CALL DECOUT CALL CRLF LXI D,SUBFCB ;DELETE THE $$$.SUB FILE MVI C,DELETF CALL BDOS JMP CPBASE ; SUBERR: DB CR,LF,'SUB Error -- $' ERRMSG: DB ' error on line number: $' ; ; PROMPT FOR COMMAND LINE INPUT ; PROMPT: DB CR,LF,'Command Line? $' ; ; INITIALIZE ALL VARIABLES ; INITVAR: LXI H,VAR LXI B,ENDVAR-VAR INITLP: MVI M,0 ;ZERO ENTIRE VAR AREA INX H DCX B MOV A,B ORA C JNZ INITLP LXI H,TABLE ;INIT PARM TABLE POINTER SHLD TBLPTR LXI H,0FFFFH ;MARK END OF TABLE SHLD ENDTBL LXI H,FREMEM ;FREE MEMORY STARTS TXT AREA SHLD TXTPTR MVI A,80H ;FORCE READ STA IBP STA CLCNT ;FORCE CONSOLE READ RET ; ; PRINT HELP WITH PROGRAM OPTIONS ; HELP: LXI D,HLPMSG ;PRINT THE HELP STUFF MVI C,PRINTF CALL BDOS LHLD SPSAVE ;THEN RETURN W/NO WARM-BOOT SPHL RET ; HLPMSG: DB CR,LF,'How to use SUB --',CR,LF DB CR,LF,'SUB - print this HELP message' DB CR,LF,'SUB /A - Abort of SUBMIT File' DB CR,LF,'SUB /AB - /A and Ring Bell' DB CR,LF,'SUB /D - use SUMMARY (DO) mode' DB CR,LF,'SUB /I - go into Interactive mode' DB CR,LF,'SUB - as in standard SUBMIT.COM' DB CR,LF DB CR,LF,'In "/I" (interactive) mode, SUB will prompt you' DB CR,LF,'a line at a time for the SUBMIT job input...logical' DB CR,LF,'lines may be combined on the same input line by sep-' DB CR,LF,'erating them with semicolons. Example:' DB CR,LF,' A>SUB /D STAT;DIR' DB CR,LF,'specifies two commands on the same input line.',CR,LF DB CR,LF,'Submitted jobs may be nested...SUB does not erase' DB CR,LF,'any existing submit job (appends to them instead).' DB CR,LF DB CR,LF,'To insert a control character into the output, pre-' DB CR,LF,'fix it with a "^" (works in any mode).' DB CR,LF,'$' ; ; VARIABLE STORAGE ; VAR EQU $ ; AFLAG: DB 0 ;ABORT FLAG (0=NO) TXTPTR: DW 0 ;FREE MEMORY POINTER TBLPTR: DW 0 ;POINTER TO PARM TABLE DDISK: DB 0 ;DEFAULT DISK NUMBER DUSER: DB 0 ;DEFAULT USER NUMBER LINNUM: DW 0 ;CURRENT LINE NUMBER PREV: DW 0 ;POINTER TO PREV LINE GETCNT: DB 0 ;COUNTER FOR 'GET' GETPTR: DW 0 ;POINTER FOR 'GET' PUTPTR: DW 0 ;POINTER FOR 'PUT' IBP: DB 0 ;INPUT BUFFER POINTER CLPTR: DW 0 ;COMMAND LINE POINTER CLFLAG: DB 0 ;USE CP/M CMD LINE FLAG BELL$FLAG: DB 0 ;RING BELL ON ABORT FLAG OPTION: DB 0 ;OPT OPTION FLAG STORE TABLE: DS NPAR*3 ;PARAMETER TABLE ENDTBL: DW 0FFFFH ;END OF PARAMETER TABLE ; ENDVAR EQU $ ; ; COMMAND LINE BUFFER...NOT INITIALIZED ; CLBUF: DB 128 ;BUFFER LENGTH CLCNT: DB 0 ;CHARACTER COUNTER CLTEXT: DS 128 ;THE BUFFER ITSELF ; SPSAVE: DW 0 ;STACK POINTER SAVE ; ; ; FCB FOR $$$.SUB ; SUBFCB: DB SUBDRV ;DRIVE SPECIFIER DB '$$$ ' SUBTYP: DB 'SUB' DW 0,0,0,0 ;INITIALIZE REST OF FCB DW 0,0,0,0 DW 0,0,0,0 ; ; STACK AREA ; DS 200 ;WHY NOT? STACK EQU $ FREMEM EQU $ ; END SUB