; LONGSUB: SUBmit utility for ZCPR33 for long submit files. ; by Thomas E. Croley II, 30 June 1987 ; * Requires modification of your ZCPR33 CCP !!!! (see below) ; * Corrects ZCPR33 error in submit processing for $$$.SUB files ; longer than can be accessed with a single directory ; entry. ; * Derived from SUB34. ; ; Submit file processing is (and has always been?) limited to short *.SUB file ; lengths (fewer than 128 lines for disk allocation blocks of 2k, 256 lines ; for disk allocation blocks of 4k, ...). This is because ZCPR33 finds the ; last record in the $$$.SUB file for processing and attempts to close the file ; one record shorter to remove that record (command) from further processing; ; to do this, ZCPR33 decrements the record count in the FCB for the file before ; the close. This works if there is only one directory entry in the directory ; for the $$$.SUB file but it does not work if there are more. Subsequently, ; for longer $$$.SUB files, ZCPR33 neither finds the end of file correctly, nor ; is it able to reduce the file length past the point allocated to an earlier ; directory entry. The proper way around this is to implement direct access ; ("random access" in old CP/M terminology) file manipulations to find the end ; of the file and to keep track of the last record processed. The SUB utility ; must be modified also to use this method so that the last record (first ; command) to be accessed by the CCP is pointed to in the file (the last record ; of the file is used for this purpose here). Also, file append (for nesting ; of SUB commands), must properly maintain this pointer. ; ; If you use this SUB command, you must modify your ZCPR33 CCP by adding the ; following code as instructed below. The PENALTY is that it will slow submit ; file processing (hardly noticeable on a RAM disk but may be significant on a ; floppy) since additional disk reads and writes are required to find and ; maintain the next-command pointer. These reads and writes are not to ; sequential records on the disk since the pointer is maintained in the last ; record of the file while commands are taken successively toward the ; beginning. The ADVANTAGE is that it will allow arbitrary length submit files ; for processing. ; ; If you do not use this SUB command and do not modify your ZCPR33 CCP as ; follows, then BEWARE that submit files with line lengths exceeding the above ; limits will not be possible and there is no error message nor error checking ; for this condition within the CCP. ; ; MODIFICATIONS TO ZCPR33.Z80 (may require reconfiguration in Z33HDR.LIB to ; make room for the additional code on your machine) ; ; Find the label "subfcr:" in ZCPR33.Z80 and add the code denoted by ";TEC" as ; follows: ; ; subfcr: ; defs 1 ; Current record number ; subfr: ;TEC ; defs 3 ;TEC Random record no. (16 bits) + ovrflw byte ; ; Find the label "readbuf:" in ZCPR33.Z80 and modify or add the code as denoted ; by ";TEC" as follows: ; ; readbuf: ; ; ld a,(zexrunfl) ; Get ZEX-running flag ; or a ; jp nz,userinput ; If ZEX running, go directly to user input ; ; if subon ; If submit facility is enabled, check for it ; ; ld a,(subflag) ; Test for submit file running ; or a ; jr z,shellinput ; If not, go on to possible shell input ; ; xor a ; Log into user 0 ; call setuser ; call defltdma ; Initialize DMA pointer ; ld de,subfcb ; Point to submit file FCB ; call open ; Try to open file ; jr z,readbuf1 ; Branch if open failed ; ; ;TEC ld hl,subfrc ; Point to record count in submit FCB ; ;TEC ld a,(hl) ; Get the number of records in file ; ;TEC dec a ; Reduce to number of last record ; ;TEC ld (subfcr),a ; ..and put into current record field ; ld c,23h ;TEC "Compute file size" bdos call no. ; call bdossave ;TEC DE still => subfcb; get file size in subfr ; ld hl,(subfr) ;TEC Get file size ; dec hl ;TEC Decrement to get last record no. ; ld (subfr),hl ;TEC Replace in random record field ; ld c,21h ;TEC "Read random" bdos call no. ; call bdossave ;TEC DE still => subfcb; randm read last record ; ld hl,(tbuff) ;TEC Get current "last record" from last record ; dec hl ;TEC Point to next earlier record to read ; ld (tbuff),hl ;TEC Save pointer in actual last record ; inc c ;TEC ... and write back to file (c=22h) ; call bdossave ;TEC DE still => subfcb; randm write last recrd ; ld (subfr),hl ;TEC Now prepare to read new "last record" ; dec c ;TEC (c=21h) ; call bdossave ;TEC Random read record; DE still => subfcb ; ;TEC call read ; Attempt to read submit file ; ;TEC jr nz,readbuf1 ; Branch if read failed ; ; ;TEC dec (hl) ; Reduce file record cound ; ;TEC dec hl ; Point to S2 byte of FCB (yes, this is req'd!) ; ;TEC ld (hl),a ; Stuff a zero in there (A=0 from call to READ) ; ld (subfrc-1),a ;TEC Zero FCB S2 byte (A=0 from previous read) ; call close ; Close the submit file one record smaller ; jr z,readbuf1 ; Branch if close failed ; ; END of ZCPR33.Z80 changes... ; ; ; The following program is SUB34.Z80 with ALL modifications denoted by ";TEC". ; ; PROGRAM NAME: SUB ; AUTHOR: RICHARD CONN (From SuperSUB Ver 1.1 by Ron Fowler) ; VERSION: 3.4 (Royce Shofner) ; DATE: 8 June 1987 ; ; PREVIOUS VERSION: 3.3 (4 MAY 1987) HOWARD GOLDSTEIN ; PREVIOUS VERSION: 3.2 (27 APRIL 1987) JAY SAGE ; PREVIOUS VERSIONS: 3.1 (19 JAN 1985) JOE WRIGHT ; PREVIOUS VERSIONS: 3.0 (18 MAY 84) RELEASE VERSION ; PREVIOUS VERSIONS: 2.3 (6 Jan 83) - Called SUB2.ASM ; 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 ZCPR3 ; vers equ 34 ; Modified for ZCPR33 compatibility ; z3env defl 0f900h ; ; SUB is derived from Ron's SuperSUB program; it provides a different ; format for the command line, a command-search hierarchy like ZCPR3, a ; resetting of the DMA address, several additional functions, and there are ; several other additions/changes. Additionally, ZCPR3-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. ; ; SuperSUB, VERSION 1.1 (09/13/81) by Ron Fowler ; 2/18/81 (first written) WESTLAND, MICH. ; ; ; Version 3.2 Modified to write $$$.SUB file to user 0 instead of current ; user. ; Version 3.3 Deletes $$$.SUB file from user 0. ALLOWS ; use of "^^" to represent "^". Permits only 126 characters per line ; so that there will always be a trailling null. No longer CP/M ; compatible. Requires ZCPR3 with multiple command line at least 127 ; characters long. Recognizes explicit directory spec for input file ; and does not search along path if so. ; Version 3.4 Modified to take advantage of ZCPR33'S external running ; submit flag. SUB34 will set the flag after writing the submit file. If ; ZCPR33 is resident, SUB will just return to system; else, it will do ; the traditional warm boot. ; ; 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 ; ; ; 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 npar equ 10 ; Number of allowable parameters cpbase equ 0 ; Set to 4200h for heath cp/m opt equ '/' ; Option delimiter char pdelim equ '$' ; Parameter delimiter ; ; SYSLIB AND Z3LIB ROUTINES ; ext z3init,pfind,getcl1,getmsg ext logud ext initfcb ext pstr,print,qprint,cout,crlf,caps,phldc ext codend ; ; Z33LIB ROUTINES ; ext getsrun,subon ; ; CP/M DEFINITIONS ; fgchar equ 1 ; Get char function diriof equ 6 ; Direct console i/o rdbuf equ 10 ; Read console buffer 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 ; curind equ '$' ; Current user/disk indicator 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 'C'-'@' ctrlz equ 'Z'-'@' bel equ 7 ; Ring bell cr equ 13 ; Carriage return lf equ 10 ; Line feed tab equ 9 ; ; Environment Definition ; if z3env ne 0 ; ; External ZCPR3 Environment Descriptor ; jp start db 'Z3ENV' ; This is a zcpr3 utility db 1 ; External environment descriptor z3eadr: dw z3env start: ld hl,(z3eadr) ; Pt to zcpr3 environment ; else ; ; Internal ZCPR3 Environment Descriptor ; maclib z3base.lib maclib sysenv.lib z3eadr: jp start sysenv start: ld hl,z3eadr ; Pt to zcpr3 environment endif ; ; Start of Program -- Initialize ZCPR3 Environment ; call z3init ; Initialize the zcpr3 env and the vlib env ld hl,0 ; Save stack in case add hl,sp ; Only help requested ld (spsave),hl ; (not otherwise used) call qprint db 'SUB Version ',vers/10+'0','.',(vers mod 10)+'0',0 call codend ; Set up external buffers ld (clbuf),hl ; Set ptr ld (hl),128 ; Allow 128 chars ld de,100h ; Free space add hl,de ; Pt to free area ld (fremem),hl ; Set ptr to free memory area ld sp,hl ; Set stack ptr ld a,(fcb+1) ; Anything on cmd line? cp ' ' jp z,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" call getsrun ; Is a submit file already active ? jr nz,subxit ; If so, just return to zcpr33 call getmsg ; else, set the submit running flag ld de,2dh add hl,de ld (hl),0ffh subxit: call subon ; zcpr33 & submit enabled ? jp nz,gozcpr ; Return to zcpr33 if posible jp cpbase ; else, try a warm boot ; ; SETUP SETS UP THE FILE CONTROL BLOCK ; FOR READING IN THE .SUB TEXT FILE ; setup: ld hl,fcb+ft ; Look at first char of ld a,(hl) ; File type. if it is cp ' ' ; Blank, then go move jp z,putsub ; "sub" INTO FT FIELD if force$sub ; File type must be of .sub ld de,subtyp ; File type must be .sub ld b,3 ; 3 bytes call compar jp nz,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: ex de,hl ; By convention, move from ld hl,subtyp ; @hl to @de ld b,3 call move ret ; ; MOVE # BYTES IN B REGISTER FROM @HL TO @DE ; move: ld a,(hl) ; Pick up ld (de),a ; Put down inc hl ; I'm sure inc de ; You've seen this dec b ; Before... jp nz,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: xor a ; A=0 ld (aflag),a ; Turn off abort command ld hl,tbuf+1 ; Where we find the command tail call scanto ; Skip submit file name ld (option),a ; First char of cmd line is option ret c ; Line ended? cp opt ; No, check option jp nz,glp0 ; Not keyboard inp, read file inc hl ; Point past '/' ld a,(hl) ; Get option char cp 'A' ; Abort command jp z,gparabt cp 'I' ; Interactive mode ret z ; Return if so jp help ; Help otherwise gparabt: ld a,0ffh ; Turn on abort flag ld (aflag),a inc hl ; Get possible bell option ld a,(hl) cp 'B' ; Bell option ret nz ld a,0ffh ; Set bell flag ld (bell$flag),a ret glp0: ld a,(hl) ; Input is from a .sub file..this inc hl ; Code skips over the name of or a ; The sub file to get to the ret z ; Command line parameters cp ' ' jp z,glp cp tab jp nz,glp0 glp: call scanto ; Pass up the blanks ret c ; Cy returned if end of cmd line call putpar ; Now put the parameter into mem ret c ; Cy returned if end of cmd line jp glp ; Get them all ; ; SCANTO SCANS PAST BLANKS TO THE FIRST NON-BLANK. IF ; END OF COMMAND LINE FOUND, RETURNS CARRY SET. ; scanto: ld a,(hl) inc hl or a ; Set flags on zero scf ; In case zero found (end of cmd lin) ret z cp ' ' jp z,scanto ; Scan past blanks cp tab ; Do tabs too, just for jp z,scanto ; Good measure dec hl ; Found char, point back to it or 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 hl ; Save pointer to parm ld hl,(txtptr) ; Next free memory ex de,hl ; Into de ld hl,(tblptr) ; Next free area of table ld a,(hl) ; Non-zero in table or a ; Indicates table jp nz,parovf ; Table overflow (too many parms) ld (hl),e ; Store the parm adrs inc hl ld (hl),d inc hl ld (tblptr),hl ; Save table pntr for next time pop hl ; Get back parm pointer push de ; Save free mem pointer because ; We will have to have it back ; Later to store the length inc de ; Point past length storage ld b,0 ; Initialize length of parm pplp: ld a,(hl) ; Get next byte of parm inc hl or a ; Test for end of cmd line jp z,pp2 ; Jump if end cp ' ' ; Test for end of command jp z,pp2 cp tab ; Tab also ends command jp z,pp2 ld (de),a ; Put parameter byte-by-byte inc de ; Into free memory inc b ; Bump length jp pplp pp2: ex de,hl ld (txtptr),hl ; New free memory pointer pop hl ; Remember our length pointer? ld (hl),b ; Store the length ex de,hl ; Have to retn hl > cmd line or a ; Now return end of line flag scf ret z ; Return cy if zero (eol mark) ccf ret ; ; ; ABORT CHECKS TO SEE IF THE ABORT FLAG IS SET AND ; EXECUTES THE ABORT FUNCTION IF SO ; ; abort: ld a,(aflag) ; Get the flag or a ; 0=no ret z call print db cr,lf,' Strike ^C to Abort Command File - ',0 call charinb ; Get response cp ctrlc ; Abort? jp nz,abort1 ; Return to opsys abort0: ld e,0 ; Set user 0 ld c,20h call bdos ld de,subfcb ; Delete submit file ld c,deletf call bdos call print db ' ... Aborted',0 jp cpbase ; Return to cp/m abort1: call print db ' ... Continuing',0 jp cpbase ; Return to cp/m ; ; INPUT CHAR FROM CON:; RING BELL EVERY SO OFTEN IF FLAG SET ; charinb: ld a,(bell$flag) ; Get flag or a ; 0=no jp z,charin push hl ; Save hl charinb$loop: ld hl,time$const ; Get time constant charinb$loop1: ex (sp),hl ; Long delay ex (sp),hl dec hl ; Count down ld a,h or l jp nz,charinb$loop1 ld e,0ffh ; Request status ld c,diriof ; Direct i/o call bdos or a ; Any input? jp nz,charinb$done ld e,bel ; Ring bell ld c,diriof call bdos jp charinb$loop charinb$done: pop hl ; Restore hl jp caps ; Capitalize char ; ; INPUT CHAR FROM CON:; CAPITALIZE IT AND ECHO ; charin: ld c,fgchar ; Get char call bdos jp caps ; Capitalize ; ; RDFILE READS THE .SUB FILE SPECIFIED ; IN THE SUBMIT COMMAND INTO MEMORY ; rdfile: ld hl,0 ; Init line number ld (linnum),hl ld a,(option) ; Using a file? cp opt ; Opt option tells jp nz,rdfile1 ; Jump if not call print db cr,lf,' Input Command Lines',0 call clfill ; Get first line jp line rdfile1: call print db cr,lf,' Processing SUB File',0 ; CHECK FOR .SUB FILE IN SPECIFIED DIRECTORY OR ALONG PATH ld de,fcb ; Pt to FCB ld a,(de) ; Get drive byte or a ; If zero, no dir specified (Z33 feature) jr z,rdfile2 dec a ; Gt drive in range 0..15 ld b,a ; Store drive in B ld a,(fcb+13) ; Get user number ld c,a ; ..into C call logud ; Log in call initfcb ; Initialize FCB jr rdfile3 ; ..and attempt to open file rdfile2: call initfcb ; Init fcb ld a,0ffh ; Search current also call pfind ; Look for file jp z,notfnd ; File not found call logud ; Log into directory ld de,fcb ; Pt to fcb rdfile3: ld c,openf ; Open file call bdos inc a ; Set zero flag if open failed jp z,notfnd call fill ; Read first block jp nz,notext ; Empty file line: ld hl,(linnum) ; Bump line number inc hl ld (linnum),hl ld hl,(prev) ; Get prev previous line pointer ex de,hl ld hl,(txtptr) ; Get current free mem pointer ld (prev),hl ; Make it the prev line (for nxt pass) ld (hl),e ; Store at begin of current line, inc hl ; A pointer to the previous ld (hl),d inc hl push hl ; Later we will put length here inc hl ; Skip past length ld c,1 ; Initialize length to one llp: call gnb ; Get next byte from input source cp ctrlz ; End of file? jp z,eof ; Cy set if end of file found and 7fh ; Mask out msb call caps ; Convert to upper case cp lf ; Ignore linefeeds jp z,llp cp cr ; If it's a carriage return, jp z,eol ; Then do end of line ld (hl),a ; Store all others into memory inc hl call size ; Make sure no memory overflow inc c ; Bump char count jp m,lenerr ; Max of 126 chars per line jp llp ; Go do next char ; ; DO END OF LINE SEQUENCE ; eol: ld (txtptr),hl ; Save free memory pointer pop hl ; Current line's length pointer dec c ; Adjust length ld (hl),c ; Store length away jp line ; Go do next line ; ; END OF TEXT FILE ; eof: ld (txtptr),hl ; Save free memory pointer dec c ; Adjust line length push bc ; Save line length call zmcl ; Load rest of command line pop bc ; Restore line length pop hl ; Current line's length pointer ld (hl),c ; Store length away ret ; All done reading sub file ; ; COPY COMMAND LINE INTO MEMORY BUFFER ; zmcl: ld hl,(linnum) ; Bump line number inc hl ld (linnum),hl ld hl,(prev) ; Get prev previous line pointer ex de,hl ld hl,(txtptr) ; Get current free mem pointer ld (prev),hl ; Make it the prev line (for nxt pass) ld (hl),e ; Store at begin of current line, inc hl ; A pointer to the previous ld (hl),d inc hl push hl ; Later we will put length here inc hl ; Skip past length ld c,1 ; Initialize length to one ex de,hl ; De pts to next place to store a byte call getcl1 ; Get address of command line buffer ld a,(hl) ; Get low inc hl ld h,(hl) ; Get high ld l,a ; Hl pts to first byte of multiple command line ld b,(hl) ; Get first char in line ld (hl),0 ; Clear line ld a,b ; Check to see if first char is a semicolon (cmd sep) cp ';' jp nz,zmcl0 inc hl ; Pt to 2nd char ld a,(hl) ; First was a semicolon, so get second zmcl0: ex de,hl ; Hl pts to next buffer space, de pts to mc line jp zmcl1a ; A=first char in mc line ; ; MAJOR LOOP TO STORE MULTIPLE COMMAND LINE ; zmcl1: ld a,(de) ; Get next byte from multiple command line zmcl1a: or a ; 0=eol jp z,zmcl2 and 7fh ; Mask out msb call caps ; Convert to upper case ld (hl),a ; Store char into memory inc hl ; Pt to next char inc de call size ; Make sure no memory ovfl inc c ; Incr char count jp m,lenerr ; Max of 128 chars in line jp zmcl1 ; ; DONE WITH INPUT OF MULTIPLE COMMAND LINE -- SAVE CHAR CNT AND SET PTR ; zmcl2: ld (txtptr),hl ; Save ptr pop hl ; Pt to char count position dec c ; Adjust char count ld (hl),c ; Store char count ret ; ; GET NEXT BYTE FROM INPUT FILE OR USER ; gnb: push hl ; Don't alter anybody push de push bc ld a,(option) ; Input from .sub file? cp opt ; Told by orig cmd line option jp nz,gnbdisk ; Get next char from disk buffer if not from user call gnbkbd ; Get a byte from kbd input jp gnbxit ; Then leave ; ; GET NEXT BYTE FROM DISK FILE ; gnbdisk: ld a,(ibp) ; Get buffer pointer cp 128 ; Need another block from disk? jp c,gnbd1 ; Continue call fill ; Get next block jp z,gnbd1 ; Continue if not empty ld a,1ah ; Fake eof jp gnbxit gnbd1: ld e,a ; Put offset in de ld d,0 inc a ; Point to next byte ld (ibp),a ; Save for next ld hl,tbuf ; Now offset into buffer add hl,de ld a,(hl) ; Get char gnbxit: pop bc ; Restore everybody pop de pop hl or a ; Turn on carry ret ; ; FILL INPUT BUFFER FROM DISK ; fill: xor a ; Clear input buffer ptr ld (ibp),a ld de,fcb ; Pt to fcb ld c,readf ; Bdos read block function call bdos or a ; Return z if eof ld a,0 ; Set ptr to first char ret ; ; GET NEXT BYTE FROM USER (KEYBOARD INPUT) ; gnbkbd: ld hl,(clptr) ; Pt to next char ld a,(hl) ; Get it inc hl ; Pt to following ld (clptr),hl ; Reset ptr cp cr ; End of line? ret nz call clfill ; Get new line jp z,gkend ; Empty line input - return eof ld a,cr ; Return cr to indicate end of line ret gkend: ld a,1ah ; Return eof ret ; ; FILL THE COMMAND LINE FROM THE USER ; clfill: call print db cr,lf,' Command Line? ',0 ld hl,(clbuf) ; Now fill the buffer ex de,hl ; De pts to it ld c,rdbuf call bdos ld hl,(clbuf) ; Pt to command line buffer inc hl ld a,(hl) ; Get char count inc hl ld (clptr),hl ; Reset the command line ptr or a ; Set zero flag push af ; Save a add a,l ; Pt to after last char ld l,a ld a,h adc 0 ld h,a ld (hl),cr ; Set eol char pop af ; Get char count ret ; ; MAKE SURE NO MEMORY OVERFLOW ; size: ld a,(bdos+2) ; Highest page pointer sub 9 ; Make it be under ccp cp h ; Check it against current page ret nc ; Nc=all okay jp memerr ; Otherwise abort ; ; SET UP THE $$$.SUB FILE ; FOR WRITING ; wrset: call print db cr,lf,' Writing Command File to Disk',0 ld e,0 ; Set user 0 ld c,20h call bdos ld de,subfcb ld c,openf call bdos ; Open the file inc a ; Check cpm return jp z,none1 ; None exists already ; ; $$$.SUB EXISTS, SO SET ; FCB TO APPEND TO IT ; ld de,subfcb ;TEC Point to submit file FCB ld c,23h ;TEC "Compute file size" bdos call no. call bdos ;TEC Get file size in subfr ld hl,(subfr) ;TEC Get file size dec hl ;TEC Decrement to get last record no. ld (oldfsz),hl ;TEC Save present file last record no. ld (subfr),hl ;TEC Replace in random record field ld de,subfcb ;TEC Point to submit file FCB again ld c,21h ;TEC "Read random" bdos call no. call bdos ;TEC Random read last record ld hl,(tbuf) ;TEC Get current "last record" pointer dec hl ;TEC Decr. to point to last unprocessed record ld (subfr),hl ;TEC Now read "last record" ld de,subfcb ;TEC Point to submit file FCB again ld c,21h ;TEC "Read random" bdos call no. call bdos ;TEC Random read record ld de,subfcb ;TEC Prepare for sequential (re)write to update ld c,15h ;TEC ... DOS pointers for further writes call bdos ;TEC Write same info back to set DOS pointers ;TEC ld a,(subfcb+fcbrc) ; Get record count ;TEC ld (subfcb+fcbnr),a ; Make next record ret oldfsz: ds 2 ;TEC space for "old file size" ; ; COME HERE WHEN NO $$$.SUB EXISTS ; none1: xor a ;TEC ld (oldfsz),a ;TEC Zero "old file size" since it did'nt exist ld (oldfsz+1),a ;TEC ld de,subfcb ld c,makef call bdos inc a jp z,nomake ; 0ffh=can't create file ret ; ; WRITE THE "$$$.SUB" FILE ; wrsub: ld hl,(prev) ; This code scans backward ld a,h ; Thru the file stored in or l ; Memory to the first non- jp z,notext ; Null line. if none is ld e,(hl) ; Found, aborts inc hl ld d,(hl) ; Here, we pick up pntr to prev line inc hl ; Now we point to length ex de,hl ; We need to store away ld (prev),hl ; Pointer to prev line ex de,hl ld a,(hl) ; Now pick up the length or a ; Set z flag on length jp nz,wrntry ; Got line w/length: go do it ld hl,(linnum) ; Nothing here, fix line number dec hl ; (working backward now) ld (linnum),hl jp wrsub wrlop: ld hl,(prev) ; Get prev line pointer ld a,h or l ; If there is no prev line jp z,close ; Then we are done ld e,(hl) ; Else set up prev for next inc hl ; Pass thru here ld d,(hl) inc hl ex de,hl ; Now store it away ld (prev),hl ex de,hl wrntry: call putlin ; Write the line to the file ld hl,(linnum) ; Bump the line number dec hl ; Down (working back now) ld (linnum),hl jp wrlop ; ; $$$.SUB IS WRITTEN, CLOSE THE FILE ; close: ld de,subfcb ;TEC ld c,24h ;TEC Set random record number for record after call bdos ;TEC ... last write ld hl,(subfr) ;TEC Get rndm record no. for last record write ld (tbuf),hl ;TEC Place in write buffer ld de,(oldfsz) ;TEC DE = old last record number xor a ;TEC Clear carry bit sbc hl,de ;TEC Is new last record number >= old? jr nc,nfeok ;TEC New file end O.K. ld (subfr),de ;TEC ... else use old last record number nfeok: ld de,subfcb ;TEC ld c,22h ;TEC "Write random" bdos call no. call bdos ;TEC Write current record no. to last record. ld de,subfcb ld c,closef jp bdos ; ; THIS SUBROUTINE WRITES A LINE ; TO THE $$$.SUB FILE BUFFER, ; AND FLUSHES THE BUFFER AFTER ; THE LINE IS WRITTEN. ; putlin: ld a,(hl) ; Pick up length byte inc hl ; Point past it or a ret z ; Don't output 0 length line ld (getcnt),a ; Make a count for "GET" ld (getptr),hl ; Make a pointer for "GET" ld hl,tbuf+1 ; Text goes after length ld (putptr),hl ; Make pointer for "PUT" ld a,1 ; Initialize put count ld (putcnt),a ld b,l ; Count for clear loop clr: ld (hl),0 ; Zero out buffer loc inc hl inc b ; Count jp nz,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 jp c,flush ; Cy = no more char in line cp '^' ; Control-char translate prefix? jp nz,notcx call getchr ; Yes, get the next jp c,ccerr ; Error: early end of input cp '^' ; If "^^" just put "^" in text jp z,notcx sub '@' ; Make it a control-char jp c,ccerr ; Error: too small cp ' ' jp nc,ccerr ; Error: too large notcx: cp pdelim ; Parameter specifier? jp nz,stobyt ; If not, just write char ld a,(option) ; Check option: '$' doesn't cp opt ; Count in opt mode ld a,pdelim ; (restore the '$') jp z,stobyt call lkahed ; Peek at next char jp c,parerr ; Line ending means param err cp pdelim ; Another "$"? jp nz,subs ; If not then go do substitution call getchr ; Get the 2nd "$" (we only looked ; Ahead before) stobyt: call putchr ; Write char to file jp putlp ; ; PARAMETER SUBSTITUTION...LOOKS UP THE ; PARAMETER # AFTER THE "$" AND PLUGS IT ; IN IF IT EXISTS. ; subs: call numtst ; It better be a number jp c,parerr ; Otherwise param error ld b,0 ; Initialize parm # jp lpntry ; We join loop in progress... sublp: call lkahed ; Look at next char jp c,dosubs ; If line empty, then plug in parm call numtst ; Check for numeric jp c,dosubs ; Done if not lpntry: call getchr ; Now remove the char from input stream sub '0' ; Remove ascii bias ld c,a ; Save it ld a,b ; Our accumulated count add a,a ; Multiply by ten add a,a add a,b add a,a add a,c ; Then add in new digit ld b,a ; Restore count jp sublp ; ; PERFORM THE SUBSTITUTION ; dosubs: ld a,b ; Get parm # dec a ; Make zero relative jp m,parerr ; Oops call lookup ; Look it up in parm table jp c,parerr ; It's not there ld b,a ; Length in b sublp1: inc b ; Test b for zero dec b jp z,putlp ; Done ld a,(hl) ; Get char of real parameter inc hl ; Point past for next time push hl ; Save real parm pointer call putchr ; Put it in the file pop hl ; Get back real parm pointer dec b ; Countdown jp sublp1 ; ; COME HERE WHEN A LINE IS FINISHED, ; AND WE NEED TO WRITE THE BUFFER TO DISK ; flush: ld hl,putcnt dec (hl) ; Adjust length ld de,subfcb ld c,writef call bdos or a jp nz,wrerr ; Cpm returned a write error ret ; ; GETCHR GETS ONE CHAR FROM ; LINE STORED IN MEMORY ; getchr: ld hl,getcnt ld a,(hl) ; Pick up count dec a ; Remove this char scf ; Preset error ret m ; Return cy if out of chars ld (hl),a ; Update count ld hl,(getptr) ; Current char pointer ld a,(hl) ; Pick up char inc hl ; Bump pointer ld (getptr),hl ; Put it back ccf ; Turn carry off ret ; ; PUTCHR PUTS ONE CHAR TO ; THE OUTPUT BUFFER ; putchr: ld hl,putcnt inc (hl) ; Increment count jp m,lenerr ; Line went to > 127 chars ld hl,(putptr) ; Get buffer pointer and 7fh ; Mask out msb ld (hl),a ; Put char there inc hl ; Bump pointer ld (putptr),hl ; Put it back ret ; All done ; ; LOOK AHEAD ONE CHAR IN ; THE INPUT STREAM. SET ; CARRY IF NONE LEFT. ; lkahed: ld a,(getcnt) or a ; See if count is down to zero scf ; Pre set indicator ret z ld a,(hl) ; Pick up char ccf ; Turn off carry flag ret ; ; LOOK UP PARAMETER WITH NUMBER IN ; A REG. RETURN A=LENGTH OF PARM, ; AND HL => PARAMETER ; lookup: cp npar jp nc,parovf ; Parm # too high ld l,a ld h,0 ; Now have 16 bit number add hl,hl ; Double for word offset ld de,table add hl,de ; Do the offset ld e,(hl) ; Get address of parm inc hl ld d,(hl) ld a,d ; Anything there? or e jp nz,lkupok xor a ; No, zero length ret lkupok: ex de,hl ; Now in de ld a,(hl) ; Pick up length inc hl ; Point past length ret ; ; UTILITY COMPARE SUBROUTINE ; compar: ld a,(de) cp (hl) ret nz inc hl inc de dec b jp nz,compar ret ; ; NUMERIC TEST UTILITY SUBROUTINE ; numtst: cp '0' ret c cp '9'+1 ccf ret ; ; ERROR HANDLERS ; wrerr: call errxit db 'Disk Full',0 nomake: call errxit db 'Dir Full',0 memerr: call errxit db 'Mem Full',0 notfnd: call errxit db 'SUB File Not Found',0 parerr: call errxit db 'Param',0 parovf: call errxit db 'Too Many Params',0 lenerr: call errxit db 'Line too Long',0 notext: call errxit db 'SUB File Empty',0 ccerr: call errxit db 'Ctrl Char',0 errxit: call crlf ; New line pop hl call pstr ; Print message call print db ' Error on Line ',0 ld hl,(linnum) ; Tell line number call phldc call crlf ld de,subfcb ; Delete the $$$.sub file ld c,deletf call bdos jp cpbase ; ; INITIALIZE ALL VARIABLES ; initvar: ld hl,var ld bc,endvar-var initlp: ld (hl),0 ; Zero entire var area inc hl dec bc ld a,b or c jp nz,initlp ld hl,table ; Init parm table pointer ld (tblptr),hl ld hl,0ffffh ; Mark end of table ld (endtbl),hl ld hl,(fremem) ; Free memory starts txt area ld (txtptr),hl ret ; ; PRINT HELP WITH PROGRAM OPTIONS ; help: call print db cr,lf,'Syntax:' db cr,lf,' SUB - Print this HELP Message' db cr,lf,' SUB /A - Abort of SUBMIT File' db cr,lf,' SUB /AB - Abort and Ring Bell' db cr,lf,' SUB /I - Go into Interactive mode' db cr,lf,' SUB - Standard SUB File' db 0 gozcpr: ld hl,(spsave) ; Return to opsys ld sp,hl ret ; ; 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 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 $ 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 ;TEC dw 0,0,0,0 dw 0,0 ;TEC db 0 ;TEC subfr: dw 0 ;TEC db 0 ;TEC ; clbuf: ds 2 ; Ptr to command line buffer fremem: ds 2 ; Ptr to free memory area ; end