* PROGRAM NAME: SUB * AUTHOR: RICHARD CONN (From SuperSUB Ver 1.1 by Ron Fowler) * VERSION: 2.3 * DATE: 6 JAN 83 * PREVIOUS VERSIONS: 2.2 (7 DEC 82), 2.1 (14 NOV 82), 2.0 (11 OCT 82) * PREVIOUS VERSIONS: 1.4 (10 OCT 81), 1.3 (7 OCT 81) * PREVIOUS VERSIONS: 1.2 (5 OCT 81), 1.1 (3 OCT 81), 1.0 (1 OCT 81) * NOTE: FOR USE WITH ZCPR2 VERSION 2.6 AND LATER VERS EQU 23 ; ; ;************************************************ ;* 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. Additionally, ZCPR2-specific enhancements, ; such as appending the rest of the multiple command line to the command file ; and allowing multiple commands on a single line, are permitted. ; ; 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 -- ; CURIND EQU '$' ;CURRENT USER INDICATOR 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 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 ; UDFLAG EQU CPBASE+4 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 ORG TPA ; ; Branch to Start of Program ; JMP SUBMIT ; ;****************************************************************** ; ; SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format ; ; This data block precisely defines the data format for ; initial features of a ZCPR2 system which are required for proper ; initialization of the ZCPR2-Specific Routines in SYSLIB. ; ; ; EXTERNAL PATH DATA ; EPAVAIL: DB 0FFH ; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES) EPADR: DW 40H ; ADDRESS OF EXTERNAL PATH IF AVAILABLE ; ; INTERNAL PATH DATA ; INTPATH: DB 0,0 ; DISK, USER FOR FIRST PATH ELEMENT ; DISK = 1 FOR A, '$' FOR CURRENT ; USER = NUMBER, '$' FOR CURRENT DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 ; DISK, USER FOR 8TH PATH ELEMENT DB 0 ; END OF PATH ; ; MULTIPLE COMMAND LINE BUFFER DATA ; MCAVAIL: DB 0FFH ; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE? MCADR: DW 0FF00H ; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE ; ; DISK/USER LIMITS ; MDISK: DB 4 ; MAXIMUM NUMBER OF DISKS MUSER: DB 31 ; MAXIMUM USER NUMBER ; ; FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK ; DOK: DB 0FFH ; ALLOW DISK CHANGE? (0=NO, 0FFH=YES) UOK: DB 0FFH ; ALLOW USER CHANGE? (0=NO, 0FFH=YES) ; ; PRIVILEGED USER DATA ; PUSER: DB 10 ; BEGINNING OF PRIVILEGED USER AREAS PPASS: DB 'chdir',0 ; PASSWORD FOR MOVING INTO PRIV USER AREAS DS 41-($-PPASS) ; 40 CHARS MAX IN BUFFER + 1 for ending NULL ; ; CURRENT USER/DISK INDICATOR ; CINDIC: DB '$' ; USUAL VALUE (FOR PATH EXPRESSIONS) ; ; DMA ADDRESS FOR DISK TRANSFERS ; DMADR: DW 80H ; TBUFF AREA ; ; NAMED DIRECTORY INFORMATION ; NDRADR: DW 00000H ; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY NDNAMES: DB 64 ; MAX NUMBER OF DIRECTORY NAMES DNFILE: DB 'NAMES ' ; NAME OF DISK NAME FILE DB 'DIR' ; TYPE OF DISK NAME FILE ; ; REQUIREMENTS FLAGS ; EPREQD: DB 0FFH ; EXTERNAL PATH? MCREQD: DB 0FFH ; MULTIPLE COMMAND LINE? MXREQD: DB 0FFH ; MAX USER/DISK? UDREQD: DB 0FFH ; ALLOW USER/DISK CHANGE? PUREQD: DB 0FFH ; PRIVILEGED USER? CDREQD: DB 0FFH ; CURRENT INDIC AND DMA? NDREQD: DB 0FFH ; NAMED DIRECTORIES? Z2CLASS: DB 0 ; CLASS 0 DB 'ZCPR2' DS 10 ; RESERVED ; ; END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA ; ;****************************************************************** ; ; ; Start of Program ; 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','$' 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 SUB 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: 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 LXI H,INTPATH ;SET ADDRESS OF PATH LDA EPAVAIL ;EXTERNAL PATHS AVAILABLE? ORA A ;0=NO JZ OSB1 ;USE INTERNAL PATH LHLD EPADR ;PT TO EXTERNAL PATH OSB1: CALL FNDFILE ;SEARCH FOR FILE JZ NOTFND ;FILE NOT FOUND LXI D,FCB ;PT TO FCB MVI C,OPENF ;OPEN FILE CALL BDOS 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 SUB File$' RDLMSG: DB CR,LF,'Input SUB 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 PUSH B ;SAVE LINE LENGTH CALL ZMCL ;LOAD REST OF MULTIPLE COMMAND LINE POP B ;RESTORE LINE LENGTH POP H ;CURRENT LINE'S LENGTH POINTER MOV M,C ;STORE LENGTH AWAY RET ;ALL DONE READING SUB FILE ; ; COPY MULTIPLE COMMAND LINE INTO MEMORY BUFFER ; ZMCL: LDA MCAVAIL ;ANY MULTIPLE COMMANDS? ORA A ;0=NO RZ 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 XCHG ;DE PTS TO NEXT PLACE TO STORE A BYTE LHLD MCADR ;GET ADDRESS OF MULTIPLE COMMAND LINE BUFFER MOV A,M ;GET LOW INX H MOV H,M ;GET HIGH MOV L,A ;HL PTS TO FIRST BYTE OF MULTIPLE COMMAND LINE MOV B,M ;GET FIRST CHAR IN LINE MVI M,0 ;CLEAR LINE MOV A,B ;CHECK TO SEE IF FIRST CHAR IS A SEMICOLON (CMD SEP) CPI ';' JNZ ZMCL0 INX H ;PT TO 2ND CHAR MOV A,M ;FIRST WAS A SEMICOLON, SO GET SECOND ZMCL0: XCHG ;HL PTS TO NEXT BUFFER SPACE, DE PTS TO MC LINE JMP ZMCL1A ;A=FIRST CHAR IN MC LINE ; ; MAJOR LOOP TO STORE MULTIPLE COMMAND LINE ; ZMCL1: LDAX D ;GET NEXT BYTE FROM MULTIPLE COMMAND LINE ZMCL1A: ORA A ;0=EOL JZ ZMCL2 ANI 7FH ;MASK OUT MSB CALL UCASE ;CONVERT TO UPPER CASE MOV M,A ;STORE CHAR INTO MEMORY INX H ;PT TO NEXT CHAR INX D CALL SIZE ;MAKE SURE NO MEMORY OVFL INR C ;INCR CHAR COUNT JM LENERR ;MAX OF 128 CHARS IN LINE JMP ZMCL1 ; ; DONE WITH INPUT OF MULTIPLE COMMAND LINE -- SAVE CHAR CNT AND SET PTR ; ZMCL2: SHLD TXTPTR ;SAVE PTR POP H ;PT TO CHAR COUNT POSITION MOV M,C ;STORE CHAR COUNT RET * * FNDFILE -- LOOK FOR FILE ALONG ZCPR2 PATH * INPUT PARAMETERS: HL = BASE ADDRESS OF PATH, DE = PTR TO FCB OF FILE * OUTPUT PARAMETERS: A=0 AND ZERO FLAG SET IF NOT FOUND, NZ IF FOUND * FNDFILE: SHLD PATH ;SAVE PATH BASE ADDRESS MVI C,17 ;SEARCH FOR FIRST CALL BENTRY ;LOOK FOR FILE INR A ;SET FLAG JNZ FF5 ;FOUND IT -- RETURN FOUND FLAG XCHG ;HL=FCB PTR SHLD FCBPTR ;SAVE IT LHLD PATH ;PT TO PATH FOR FAILURE POSSIBILITY MVI C,32 ;GET CURRENT USER MVI E,0FFH CALL BENTRY STA TMPUSR ;SAVE IT FOR LATER ; ; MAIN SEARCH LOOP ; FF1: MOV A,M ;GET DRIVE ANI 7FH ;MASK MSB ORA A ;0=DONE=COMMAND NOT FOUND JNZ FF2 ;NO ERROR ABORT? ; ; FILE NOT FOUND ERROR ; XRA A ;ZERO FLAG MEANS NOT FOUND RET ; ; LOOK FOR COMMAND IN DIRECTORY PTED TO BY HL; DRIVE IN A ; FF2: CPI CURIND ;CURRENT DRIVE SPECIFIED? JNZ FF3 ;SKIP DEFAULT DRIVE SELECTION IF SO LDA UDFLAG ;GET DEFAULT USER/DISK ANI 0FH ;MASK FOR DEFAULT DISK INR A ;PREP FOR FOLLOWING DCR A FF3: DCR A ;ADJUST PATH 1 TO 0 FOR A, ETC MOV E,A ;DISK NUMBER IN E MVI C,14 ;SELECT DISK FCT CALL BENTRY ;SELECT DRIVE INX H ;PT TO USER NUMBER MOV A,M ;GET USER NUMBER ANI 7FH ;MASK OUT MSB INX H ;PT TO NEXT ENTRY IN PATH PUSH H ;SAVE PTR CPI CURIND ;CURRENT USER SPECIFIED? JNZ FF4 ;DO NOT SELECT CURRENT USER IF SO LDA TMPUSR ;GET ORIGINAL USER NUMBER FF4: MOV E,A ;SELECT USER MVI C,32 CALL BENTRY LHLD FCBPTR ;GET PTR TO FCB XCHG ;... IN DE MVI C,17 ;SEARCH FOR FIRST CALL BENTRY ;LOOK FOR FILE POP H ;GET PTR TO NEXT PATH ENTRY INR A ;SET FLAG JZ FF1 ;CONTINUE PATH SEARCH IF SEARCH FAILED ; ; FILE FOUND -- PERFORM SYSTEM TEST AND PROCEED IF APPROVED ; FF5: MVI A,0FFH ;SET OK RETURN ORA A RET ; ; BDOS ROUTINE ; BENTRY: PUSH H ;SAVE REGS PUSH D PUSH B CALL BDOS POP B ;GET REGS POP D POP H RET * BUFFERS FCBPTR: DS 2 ;POINTER TO FCB FOR FILE SEARCH TMPUSR: DS 1 ;CURRENT USER NUMBER PATH: DS 2 ;BASE ADDRESS OF PATH ; ; 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 UDFLAG ;RESTORE CURRENT DISK ANI 0FH ;MASK IN DISK ONLY MOV E,A MVI C,LOGIN ;BDOS FCT CALL BDOS LDA DUSER ;RESTORE CURRENT USER MOV E,A MVI C,UCODE ;BDOS FCT CALL 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 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 SUB 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 ; GET CURRENT USER NUMBER FOR LATER MVI E,0FFH ;GET USER CODE MVI C,UCODE ;BDOS FCT CALL BDOS STA DUSER ;DEFAULT USER 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 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 1 ;DRIVE SPECIFIER (A SELECTED) 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