; Program: EXTEND [ZCPR3-compatible] ; Version: 1.1 (derived from EXTEND.ASM, Version 1.0) ; Author: Ron Fowler ; Last Revised: May 1, 1987 by Bruce Morgen VERSION EQU 1$1 ; Z3-compatible, still 8080 code ; USER-SETABLE EQUATES CPBASE EQU 0 ; Set to 4200h for alt CP/M TPA EQU CPBASE+100H ; Transient program area, Z33 ; and BGii (1.13 or later) ; users may wish to change ; 100H to a higher address. ; If you do this, set ENVTYP ; (just below) to 3. ENVTYP EQU 1 ; Version 1.1z is compatible with ZCPR3-style environments. ; ; Significant enhancements are: ; 1. Accepts "DU:" and "DIR:" form as well as "D:" ; under ZCPR3-compatible environments. This ; is enabled by installing EXTEND with Z3INS or ; Z-RIP, or automatically by later versions of ; the Z3 and BGii command processors. ; 2. Skips terminating CR/LF if the appended string ; begins with a "/". The "/" is discarded. ; 3. A "|" in the appended string is interpreted as a ; mid-string CR/LF. Aborts on a line buffer ; overflow condition, which can happen rather ; easily because of a combination the "|"=CR/LF ; feature and the fact that pre-3.3 versions of ; Z3 command processor do no adequately protect ; the TPA from encroachment by the command tail. ; This situation can't happen under 3.3 or BGii. ; 4. "EXTEND //" gives a ZCPR3-style help message, ; as does any likely syntax error. ; extend.asm: v1.0 ; usage: APPEND ; appends to end of file ; Intended mainly to illustrate a fast file-append ; algorithm, but may be useful as a quick-update ; notations file handler ; 09/13/81 Ron Fowler, Westland MI ; CHARACTER EQUATES CR EQU 13 ; Carriage return LF EQU 10 ; Linefeed TAB EQU 9 ; Horizontal tab EOF EQU 1AH ; End of file ; CPM EQUATES BDOS EQU CPBASE+5 ; BDOS entry point PRINTF EQU 9 ; Print string function OPENF EQU 15 ; Open file function CLOSEF EQU 16 ; Close file function MAKEF EQU 22 ; Make new file function READF EQU 20 ; Read record WRITEF EQU 21 ; Write record SDMAF EQU 26 ; Set DMA function GSUSER EQU 32 ; Get/set logged user area DFCB EQU CPBASE+5CH ; Default file control block FCB2 EQU CPBASE+6CH DBUF EQU CPBASE+80H ; Default disk buffer EX EQU 12 ; Fcb extent offset FRC EQU 15 ; Record count fcb offset NR EQU 32 ; Next record offset ; PROGRAM CODE BEGINS HERE ORG TPA BASE: JMP START DB 'Z3ENV' DB ENVTYP ; Allow high ORG under Z33/BGii Z3EADR: DW 00 DW BASE START: LXI H,0 ; Save the stack DAD SP SHLD STACK LXI SP,STACK MVI A,80H STA BUFPTR ; Initialize buffer pointer STA CRLFLG ; Initialize CRLF flag to true MVI E,0FFH ; Get current user area MVI C,GSUSER CALL BDOS STA OLDUSR ; Stash for tidy exit LDA DFCB+1 ; Check for help query CPI '/' JZ HELP ; Give help if requested LDA FCB2+1 ; Check for ignorant user CPI ' ' ; Check for no append string JNZ NOHELP ; Branch around if not help HELP: CALL ERRXIT DB 'EXTEND, Version ' DB (VERSION/10)+'0','.',(VERSION MOD 10)+'0' DB 'z [ZCPR3-compatible]',CR,LF DB 'Syntax: ',LF DB 'EXTEND [dir: or du:]ufn string',CR,LF DB 'Appends "string" and a CR/LF/EOF to the file "ufn"' DB CR,LF,TAB DB 'EXTEND [dir: or du:]ufn /string',CR,LF DB 'Appends "string" and an EOF only to the file "ufn"' DB CR,LF DB 'A "|" in "string" signifies a CR/LF in "ufn"' DB CR,LF DB 'If "ufn" doesn''t exist, EXTEND will create it.' DB CR,LF DB '"string" is automatically shifted to upper case' DB CR,LF,TAB DB '(by the CCP, not by EXTEND).' DB CR,LF,'$' NOHELP: LDA Z3EADR+1 ; Get MSB of environment address ORA A ; A zero means it's not Z3 JZ NOTZ3 ; So branch past user area stuff LDA DFCB+13 ; Get requested user area from Z MOV E,A MVI C,GSUSER ; Log in CALL BDOS NOTZ3: MVI C,SDMAF ; Set DMA to DBUF LXI D,DBUF CALL BDOS CALL MOVSTR ; Move the string CALL ADVANC ; Open and advance out file CALL WRLINE ; Write line to output file CALL CLOSE ; Close output file CALL ERRXIT ; Exit with message DB CR,LF DB '++ Done ++' DB CR,LF,'$' ERRXIT: POP D MVI C,PRINTF ; Print message CALL BDOS LDA OLDUSR ; Retrieve entry user code MOV E,A MVI C,GSUSER ; Log in to it CALL BDOS EXIT: LHLD STACK ; Restore stackpointer SPHL RET ; MOVE STRING INTO BUFFER MOVSTR: LXI H,DBUF+1 ; Point to start of string CALL SCANB ; Skip leading blanks CALL SCANC ; Skip filename CALL SCANB ; Skip blanks after filename ; NOW AT STRING, TRANSFER TO BUFFER XCHG ; Cmd line ptr in de LXI H,LINBUF ; Point to buffer MVI B,(BUFPTR-LINBUF)-1; Ovfl watchdog counter LDA CRLFLG ORA A JZ MVLN DCR B!DCR B ; Allow room for last CR/LF MVLN: MVI M,EOF ; First mark possible end LDAX D ; Pick up byte of line INX D ORA A ; Terminator? RZ ; Done if so CPI '|' ; CR/LF substitute? JNZ PUTBUF ; If not, branch ahead MVI M,CR ; Otherwise plant a CR INX H ; Bump pointer DCR B ; Decrement safety counter JZ BUFERR MVI A,LF ; LF in accumulator PUTBUF: MOV M,A ; Now move it INX H DCR B JNZ MVLN BUFERR: CALL ERRXIT ; If here, too many characters DB CR,LF DB '++ Line buffer overflow ++' DB CR,LF,'$' ; SKIP BLANKS, CHECK IF FINAL CR/LF WANTED SCANB: MOV A,M ; Get char CPI '/' JNZ CKTERM XRA A STA CRLFLG INX H MOV A,M CKTERM: ORA A ; Terminator? RZ CPI ' ' ; Blank? RNZ INX H JMP SCANB ; No,scan more ; SKIP TO FIRST BLANK SCANC: MOV A,M ; Get char ORA A ; Terminator? RZ CPI ' ' ; Got blank? RZ INX H JMP SCANC ; No, scan more ; OPEN INPUT FILE AND ADVANCE TO END ADVANC: LXI D,DFCB MVI C,OPENF ; Open it CALL BDOS INR A ; Check result JNZ ADV1 ; Exists, go scan it MVI C,MAKEF ; Doesn't exist, create it LXI D,DFCB CALL BDOS INR A ; Test result JNZ MAKEOK CALL ERRXIT ; Bad make DB CR,LF DB '++ Can''t create file ++' DB CR,LF,'$' MAKEOK: MVI A,80H ; New file buf pointer STA BUFPTR RET ADV1: LDA DFCB+FRC ; Get record count CPI 80H ; Full extent? JNZ WIND LXI H,DFCB+EX ; Yes, try next INR M LXI D,DFCB ; Open new extent MVI C,OPENF CALL BDOS INR A ; Good open? JNZ ADV1 ; Yes, keep scanning BACKEX: LXI H,DFCB+EX ; No, re-open prev DCR M JP BACKOK ; Test case of empty file INR M ; Get back to extent 0 JMP MAKEOK ; And pretend we made it BACKOK: MVI C,OPENF LXI D,DFCB CALL BDOS ; Can't get an error here LDA DFCB+FRC WIND: DCR A ; Make zero relative JM BACKEX ; An extent with 0 recs? STA DFCB+NR ; Set record to read MVI C,READF ; Now read it LXI D,DFCB CALL BDOS ; Can't get error here LDA DFCB+NR ; See if new extent ORA A JNZ FIXRC ; No, go reset rec cnt LXI H,DFCB+EX ; Sigh...have to reopen prev DCR M LXI D,DFCB MVI C,OPENF CALL BDOS ; Can't get error here LDA DFCB+FRC FIXRC: DCR A ; Reset next rec STA DFCB+NR ; NOW SCAN SECTOR IN DBUF FOR EOF MARK LXI H,DBUF MVI B,128 ; Scan limit EOFSC: MOV A,M ; Pick up a char CPI EOF JZ GOTEOF INX H ; Not yet DCR B JNZ EOFSC ; Continue scan CALL ERRXIT ; ??? no eof in last sector DB CR,LF DB '++ No EOF in last sector ++' DB CR,LF,'$' GOTEOF: MOV A,L ; Use lo byte for buf ptr STA BUFPTR RET ; WRITE LINE TO OUTPUT FILE WRLINE: LXI H,LINBUF WRLP: MOV A,M CPI EOF ; Line end? JZ WREND PUSH H ; Save line pointer CALL WRBYTE ; Write it POP H INX H JMP WRLP WREND: LDA CRLFLG ORA A RZ ; Terminating CR/LF not wanted MVI A,CR ; Write terminating CR/LF CALL WRBYTE MVI A,LF ; FALL THROUGH ; WRITE A BYTE TO THE OUTPUT FILE WRBYTE: PUSH PSW ; Save output byte LDA BUFPTR ; Get buffer pointer ORA A ; Check for wrap CZ WRSEC ; Wrapped, write record LXI H,DBUF ; Get address of disk buffer MOV L,A ; Offset to pointer INR A ; Bump pointer STA BUFPTR POP PSW ; Retrieve output byte MOV M,A ; Store it RET ; WRITE A DISK SECTOR WRSEC: MVI C,WRITEF ; BDOS write sec function LXI D,DFCB CALL BDOS ORA A ; Test result MVI A,80H ; (load new buf ptr) RZ CALL ERRXIT ; Print diagnostic and exit DB CR,LF DB '++ Disk full ++' DB CR,LF,'$' ; PAD OUT THE EXISTING BUFFER WITH EOF'S, THEN CLOSE CLOSE: MVI A,EOF ; Write an eof CALL WRBYTE LDA BUFPTR ; Check for sector pad ORA A ; Spilled to next sector? JNZ CLOSE ; Not, then keep padding MVI C,WRITEF ; Write the last sector LXI D,DFCB CALL BDOS MVI C,CLOSEF LXI D,DFCB ; Done, close output file CALL BDOS INR A ; Check result RNZ CALL ERRXIT ; Print diagnostic DB CR,LF DB '++ Can''t close output file ++' DB CR,LF,'$' ; DATA AREA LINBUF: DS 253 ; Line buffer (big 'un) BUFPTR: DS 1 ; Disk buffer pointer OLDUSR: DS 1 CRLFLG: DS 1 DS 128 ; Stack area STACK: DS 2 ; Stackpointer save END BASE