; Program: EXTEND [ZCPR3-compatible] ; Version: 1.3 VERSION equ 13 ; Date: 26 Jan 90 ; Assembly: Z80ASM, SLRNK, Z3LIB ; Author: Carson Wilson ; Sysop, Antelope Freeway RAS, Chicago, 312/764-5162. ; Changes: Fixed for CP/M Plus. RC byte of FCB can be set to values ; > 80h under CP/M Plus (high bit set). Values > 80h imply ; RC = 80h under CP/M Plus. ; Zeroed out (Z3EADR), which may have prevented EXTEND from ; responding to the QUIET patch byte under some non-ZCPR ; systems. ; Version: 1.2 ; Date: 7 May 89 ; Assembly: Z80ASM, SLRNK, Z3LIB ; Author: Carson Wilson ; Sysop, Antelope Freeway RAS, Chicago, 312/764-5162. ; Changes: Uses Z80 codes. ; Uses DSEG for smaller program size. ; Senses ZCPR quiet flag and displays no informative messages ; if Quiet flag active. Patchable byte at [QUIET> reverses ; sense of ZCPR quiet flag, showing messages only if Quiet is ; active. Under CP/M, [QUIET> always gives quiet operation ; if set to a nonzero value. ; Program ID included in error messages. ; Allows lower case: "%>" in string sets to lower case, ; "%<" to upper case. "%c" sends other characters "c" using ; current case, "%%" sends a single "%" to file (same as ; ZCPR ECHO command). Patchable byte at [CASE> makes lower ; case the default if set to 20 hex. ; Now uses "%|" to imbed CRLF within a line of input text. ; ; Version: 1.1 (derived from EXTEND.ASM, Version 1.0) ; Author: Ron Fowler ; Last Revised: May 1, 1987 by Bruce Morgen ; 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 extrn z3init,getquiet ; Z3LIB ; CHARACTER EQUATES CR equ 13 ; Carriage return LF equ 10 ; Linefeed TAB equ 9 ; Horizontal tab bell equ 7 EOF equ 1Ah ; End of file escchr equ '%' ; For U/L case ucchr equ '<' ; " lcchr equ '>' ; " crchr equ '|' ; For CRLF ; 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 BASE: jp START db 'Z3ENV' db ENVTYP ; Allow high ORG under Z33/BGii Z3EADR: dw 00000h ; Initialize to nonzero for debug ONLY dw BASE ; Installable options db '[QUIET>' qflag: db 0 ; 0FFh = quiet db '[CASE>' lcflgd: db 0 ; 020h = lower case default START: ld (STACK),sp ld sp,STACK ld hl,(z3eadr) call z3init ld a,(lcflgd) ; Initialize for ZCPR GO ld (lcflg),a ld a,80h ld (BUFPTR),a ; Initialize buffer pointer ld (CRLFLG),a ; Initialize CRLF flag to true ld e,0FFh ; Get current user area ld c,GSUSER call BDOS ld (OLDUSR),a ; Stash for tidy exit ld a,(FCB2+1) ; Check for ignorant user cp ' ' ; Check for no append string jp nz,NOHELP ; Branch around if not help .comment \ EXTEND, Version 1.2z - Append input line to new or existing text file. Syntax: EXTEND [dir:]ufn string - append "string" plus newline EXTEND [dir:]ufn /string - append "string" only Options: %> - begin lowercase %< - begin uppercase %| - imbed newline %% - imbed "%" \ HELP: call progid call ERRXIT db ', Version ' db (VERSION/10)+'0','.',(VERSION MOD 10)+'0' db 'z - Append input line to new or existing text file.',CR,LF db ' Syntax:',cr,lf db tab,'EXTEND [dir:]ufn string - append "string" plus newline' db CR,LF db tab,'EXTEND [dir:]ufn /string - append "string" only',CR,LF db ' Options:',cr,lf db tab,'%>',tab,'- begin lowercase',cr,lf db tab,'%<',tab,'- begin uppercase',cr,lf db tab,'%|',tab,'- imbed newline',cr,lf db tab,'%%',tab,'- imbed "%"',cr,lf db '$' ProgID: ld de,idstr ld c,printf jp BDOS IDStr: db cr,lf,'EXTEND$' NOHELP: ld a,(Z3EADR+1) ; Get MSB of environment address or a ; A zero means it's not Z3 jr z,NOTZ3 ; So branch past user area stuff ld a,(DFCB+13) ; Get requested user area from Z ld e,a ld c,GSUSER ; Log in call BDOS NOTZ3: ld c,SDMAF ; Set DMA to DBUF ld de,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 ld a,(Z3EADR+1) or a call nz,GetQuiet ; A=0FFh if quiet active and ZCPR ld hl,QFlag xor (hl) ; Flip quiet status if QFlag=0FFh jr nz,qexit ; ..(QFlag controls quiet if not ZCPR) call ProgID call ERRXIT ; Exit with message db ' Done.' db CR,LF,'$' ERRXIT: pop de ld c,PRINTF ; Print message call BDOS qexit: ld a,(OLDUSR) ; Retrieve entry user code ld e,a ld c,GSUSER ; Log in to it call BDOS EXIT: ld sp,(STACK) ; Restore stackpointer ret ; MOVE STRING INTO BUFFER MOVSTR: ld hl,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 ex de,hl ; Cmd line ptr in de ld hl,LINBUF ; Point to buffer ld b,(BUFPTR-LINBUF)-1 ; Ovfl watchdog counter ld a,(CRLFLG) or a jr z,MVLN dec b dec b ; Allow room for last CR/LF MVLN: ld (hl),EOF ; First mark possible end ld a,(de) ; Pick up byte of line inc de or a ; Terminator? ret z ; Done if so PUTBUF: ld (hl),a ; Now move it inc hl dec b jr nz,MVLN BUFERR: call ProgID call ERRXIT ; If here, too many characters db ': Line buffer overflow',bell db CR,LF,'$' ; SKIP BLANKS, CHECK IF FINAL CR/LF WANTED SCANB: ld a,(hl) ; Get char cp '/' jr nz,CKTERM xor a ld (CRLFLG),a inc hl ld a,(hl) CKTERM: or a ; Terminator? ret z cp ' ' ; Blank? ret nz inc hl jp SCANB ; No,scan more ; SKIP TO FIRST BLANK SCANC: ld a,(hl) ; Get char or a ; Terminator? ret z cp ' ' ; Got blank? ret z inc hl jr SCANC ; No, scan more ; OPEN INPUT FILE AND ADVANCE TO END ADVANC: ld de,DFCB ld c,OPENF ; Open it call BDOS inc a ; Check result jr nz,ADV1 ; Exists, go scan it ld c,MAKEF ; Doesn't exist, create it ld de,DFCB call BDOS inc a ; Test result jp nz,MAKEOK call ProgID call ERRXIT ; Bad make db ': Can''t create file.',bell db CR,LF,'$' MAKEOK: ld a,80h ; New file buf pointer ld (BUFPTR),a ret ADV1: ld a,(DFCB+FRC) ; Get record count cp 80h ; Full extent? JP C,WIND ; 128..255 imply 128 for CP/M Plus ;jp nz,WIND ld hl,DFCB+EX ; Yes, try next inc (hl) ld de,DFCB ; Open new extent ld c,OPENF call BDOS inc a ; Good open? jp nz,ADV1 ; Yes, keep scanning BACKEX: ld hl,DFCB+EX ; No, re-open prev dec (hl) jp p,BACKOK ; Test case of empty file inc (hl) ; Get back to extent 0 jp MAKEOK ; And pretend we made it BACKOK: ld c,OPENF ld de,DFCB call BDOS ; Can't get an error here ld a,(DFCB+FRC) WIND: dec a ; Make zero relative jp m,BACKEX ; An extent with 0 recs? ld (DFCB+NR),a ; Set record to read ld c,READF ; Now read it ld de,DFCB call BDOS ; Can't get error here ld a,(DFCB+NR) ; See if new extent or a jp nz,FIXRC ; No, go reset rec cnt ld hl,DFCB+EX ; Sigh...have to reopen prev dec (hl) ld de,DFCB ld c,OPENF call BDOS ; Can't get error here ld a,(DFCB+FRC) FIXRC: dec a ; Reset next rec ld (DFCB+NR),a ; NOW SCAN SECTOR IN DBUF FOR EOF MARK ld hl,DBUF ld b,128 ; Scan limit EOFSC: ld a,(hl) ; Pick up a char cp EOF jp z,GOTEOF inc hl ; Not yet dec b jr nz,EOFSC ; Continue scan call ProgID call ERRXIT ; ??? no eof in last sector db ': No EOF in last sector.',bell db CR,LF,'$' GOTEOF: ld a,l ; Use lo byte for buf ptr ld (BUFPTR),a ret ; WRITE LINE TO OUTPUT FILE WRLINE: ld hl,LINBUF WRLP: ld a,(hl) cp EOF ; Line end? jr z,WREND cp escchr jr nz,wrlp3 inc hl ld a,(hl) cp EOF jr z,WREND cp crchr ; Imbed CRLF? jr nz,wrlp0 ; No, see if L/C ld a,cr call wrbyte ld a,lf call wrbyte jr wrlp4 ; Get next wrlp0: cp lcchr ; Lower case? jr nz,wrlp1 ; No, see if upper ld a,20h jr wrlp2 wrlp1: cp ucchr ; Upper case? jr nz,wrlp3 ; No, pass char. xor a wrlp2: ld (lcflg),a ; Yes jr wrlp4 ; Get next wrlp3: call WRBYTE ; Write it wrlp4: inc hl jr WRLP WREND: ld a,(CRLFLG) or a ret z ; Terminating CR/LF not wanted ld a,CR ; Write terminating CR/LF call WRBYTE ld a,LF ; FALL THROUGH ; WRITE A BYTE TO THE OUTPUT FILE WRBYTE: push hl bit 6,a ; First see if ctl. char. jr z,wrbyt1 ; No change if so ld hl,lcflg or (hl) ; Set UC/LC per flag wrbyt1: push af ; Save output byte ld a,(BUFPTR) ; Get buffer pointer or a ; Check for wrap call z,WRSEC ; Wrapped, write record ld hl,DBUF ; Get address of disk buffer ld l,a ; Offset to pointer inc a ; Bump pointer ld (BUFPTR),a pop af ; Retrieve output byte ld (hl),a ; Store it pop hl ret ; WRITE A DISK SECTOR WRSEC: ld c,WRITEF ; BDOS write sec function ld de,DFCB call BDOS or a ; Test result ld a,80h ; (load new buf ptr) ret z call ProgID call ERRXIT ; Print diagnostic and exit db ': Disk full.',bell db CR,LF,'$' ; PAD OUT THE EXISTING BUFFER WITH EOF'S, THEN CLOSE CLOSE: ld a,EOF ; Write an eof call WRBYTE ld a,(BUFPTR) ; Check for sector pad or a ; Spilled to next sector? jp nz,CLOSE ; Not, then keep padding ld c,WRITEF ; Write the last sector ld de,DFCB call BDOS ld c,CLOSEF ld de,DFCB ; Done, close output file call BDOS inc a ; Check result ret nz call ProgID call ERRXIT ; Print diagnostic db ': Can''t close output file.',bell db CR,LF,'$' ; DATA AREA DSEG lcflg ds 1 LINBUF: DEFS 253 ; Line buffer (big 'un) BUFPTR: DEFS 1 ; Disk buffer pointer OLDUSR: DEFS 1 CRLFLG: DEFS 1 DEFS 128 ; Stack area STACK: DEFS 2 ; Stackpointer save end