home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
ZCPR33
/
A-R
/
CONCAT03.LBR
/
CONCAT03.MZC
/
CONCAT03.MAC
Wrap
Text File
|
2000-06-30
|
15KB
|
662 lines
; CONCAT.MAC -- Version 0.3 -- September 19, 1989
;
; Concatenates two or more source files into a destination file,
; similar to PIP. For ZCPR3 only.
;
; USAGE:
;
; CONCAT {dir:}outfile={dir:}infile{,{dir:}infile{, ...}} {/option}
;
; Any file without a DU or DIR specification is assumed to be on
; the current drive/user. Default is text mode, checking for a
; CP/M end-of-file character (^Z). The only option at present is
; 'O', which puts CONCAT is object mode so ^Z's are ignored.
;
; CONCAT requires an output file and at least one input file. Actually,
; it can be used as a simple file-copy utility, but that's not its
; purpose. The same filename may be given repeatedly in the input file
; list. The equal sign separates the output file from the input
; files. Commas separate multiple input files. While the equal sign
; and commas are required, they may be separated from the filenames
; by one or more spaces. A filename cannot begin with a slash unless
; it is preceded by a DU or DIR specification.
;
; If an error occurs, such as an input file not found, the incomplete
; output file is erased. If another file has the same name as the
; output file, it will be renamed to filetype BAK.
;
; An invalid option will be ignored.
;
; To report bugs or make suggestions:
; Gene Pizzetta
; 481 Revere Street
; Revere, MA 02151
;
; Newton Centre Z-Node: (617) 965-7259
; Lilliput Z-Node: (312) 649-1730
; Voice: (617) 284-0891
;
; Re-assembly requires MAC or SLRMAC and SYSLIB, Version 4. With MAC,
; Z80.LIB will also be needed.
;
Bdos equ 05h
MemTop equ Bdos+1
CpmDma equ 80h
TPA equ 100h
;
; Bdos functions . . .
;
FRead equ 20
FWrite equ 21
CurDsk equ 25
SetDma equ 26
;
LF equ 0Ah
CR equ 0Dh
CpmEOF equ 1Ah
BufSiz equ 1
;
MACLIB Z80
;
; Following routines are from VLIB, Z3LIB, and SYSLIB, Version 4
;
ext f$exist,f$open,f$mopen,f$rename,f$delete,f$close
ext logud,pstr,pfn2,crlf,cout,pafdc,initfcb
ext z3vinit,zfname,stndout,stndend
;
jmp Start
;
db 'Z3ENV'
db 1
Z3EAdr: dw 0FE00h ; address of environment descriptor
;
; Messages . . .
;
MsgUse: db 'CONCAT Version 0.3',CR,LF
db 'Usage:',CR,LF
db ' CONCAT {dir:}outfile={dir:}infile{,{dir:}infile{, ...}}'
db ' {/option}',CR,LF
db 'Concatenates infiles to outfile.',CR,LF
db 'Option:',CR,LF
db ' O object files, ignore ^Z',0
MsgWrt: db 'Writing ',0
MsgRd: db ' Reading ',0
MsgNIG: db 'No input file given.',0
MsgNIF: db 'Input file not found.',0
MsgNOG: db 'No output file given.',0
MsgNOF: db 'Output file not found.',0
MsgNEr: db 'Can''t erase existing file.',0
MsgREr: db 'Input read error.',0
MsgWEr: db 'Output write error.',0
MsgCIF: db 'Error closing input file.',0
MsgCOF: db 'Error closing output file.',0
MsgAmb: db 'Ambiguous filename not allowed.',0
MsgDlm: db 'Illegal command line.',0
MsgDne: db 'Done!',0
;
Start: sspd OldStk ; save old stack pointer
lhld MemTop ; get top of memory
mov a,h ; move high byte to A
sui 16 ; preserve 4K for CCP and safety
mov h,a ; ..and put it back
sphl
lhld Z3EAdr ; set up environment
call z3vinit
;
call GetDfD ; get and store default disk
lda CpmDma ; see if there's a tail
ora a
jrnz GtTail ; (yes)
lxi h,MsgUse ; no, tell them how to do it
call pstr
jmp Exit
;
GtTail: mov c,a ; move command tail to storage
inr c
mvi b,0
lxi h,CpmDma+1
lxi d,CTail
ldir
;
call GetOpt ; get options, if any
lxi h,CTail ; get output filename from tail
call EatSpc ; eat any spaces
cpi 0 ; is it NUL?
jrz NoOFl ; (no)
cpi '/' ; is it a slash?
jrnz GtOFl ; (no)
NoOFl: lxi h,MsgNOG
call pstr
jmp Exit
;
GtOFl: lxi d,OutFcb ; ..and put it in FCB
call zfname
jrz CkDelm ; (okay, so far)
lxi h,MsgAmb ; it's ambiguous
call pstr
jmp Exit
;
CkDelm: call EatSpc
mov a,m
cpi '=' ; equal sign after filename?
jrz GtOUsr ; (yes)
lxi h,MsgDlm
call pstr
jmp Exit
;
GtOUsr: inx h ; get past delimiter
shld TailPt ; save command tail pointer
lxi h,FcbOFn
lxi d,OutFil
lxi b,11
ldir
lxi h,TmpTyp
lxi d,FcbOFt
lxi b,3
ldir
lda OutFcb ; get drive (A=1)
ora a ; is there one?
jrnz GtOUs1 ; (yes)
lda DftDsk ; no, get default
GtOUs1: dcr a ; make A=0
sta OutDrv ; ..and store it
lda OutFcb+13 ; get user
sta OutUsr ; ..and store it
;
lhld TailPt
call EatSpc
cpi 0
jrz NoIFl
cpi '/'
jrnz GtIFl
NoIFl: lxi h,MsgNIG
call pstr
jmp Exit
;
GtIFl: call SetIFl ; set up input FCB
call OpnInp ; open input file
call OpnOut ; open output file
;
lxi h,MsgWrt
call pstr
lda OutDrv
mov b,a
lda OutUsr
mov c,a
lxi d,OutFil
call PrtFn
lxi h,MsgRd
call pstr
lda InDrv
mov b,a
lda InUsr
mov c,a
lxi d,FcbIFn
call PrtFn
;
call RdLoop ; read and write files
;
CkMore: call ClsIFl ; close input file
lhld TailPt ; any more input files?
call EatSpc
cpi ','
jrnz NoMore ; (no)
inx h
call EatSpc
cpi 0
jrz NoMore
cpi '/'
jrz NoMore
call SetIFl ; set up input FCB
call OpnInp ; open input file
lxi h,MsgRd
call pstr
lda InDrv
mov b,a
lda InUsr
mov c,a
lxi d,FcbIFn
call PrtFn
call RdLoop ; read and write files
jr CkMore
;
Abort: call OutDU
lxi d,OutFcb
call f$exist ; does an output file exist?
jrz Abort1 ; (no)
call OutDU
lxi d,OutFcb
call f$delete ; yes, erase it
Abort1: jmp Exit
;
NoMore: call ClsOFl ; close output file
lxi h,OutTyp ; move real filetype into FCB
lxi d,FcbOFt
lxi b,3
ldir
lxi d,OutFcb
call initfcb
call OutDU
lxi d,OutFcb
call f$exist ; see if XXX already exists
jrz SkpBak ; (it doesn't)
lxi h,BakTyp
lxi d,FcbOFt
lxi b,3
ldir
call OutDU
lxi d,OutFcb ; point to existing BAK name
call f$delete ; erase any existing file
jrz DelSuc ; (delete successful)
lxi h,MsgNEr
call pstr
jmp Abort
;
DelSuc: lxi h,FcbOFn
lxi d,FcbOFn+12
lxi b,11
ldir
lxi h,OutFil
lxi d,FcbOFn
lxi b,11
ldir
call OutDU
lxi d,OutFcb ; point to old XXX name
lxi h,OutFcb+12 ; point to new BAK name
call f$rename ; ..and rename file
SkpBak: lxi h,TmpTyp ; rename temporary $$$ file
lxi d,FcbOFt ; ..to output filename
lxi b,3
ldir
lxi h,OutFil
lxi d,FcbOFn+12
lxi b,11
ldir
call OutDU
lxi h,OutFcb+12 ; point to new XXX name
lxi d,OutFcb ; point to old $$$ name
call f$rename
;
Finish: lxi h,MsgDne
call pstr
Exit: lspd OldStk ; restore old stack pointer
ret
;
; Subroutines . . .
;
; SetIFl -- set up input file control block
;
SetIFl: lxi d,InpFcb ; point to input FCB
call zfname ; ..and parse filespec
jrz GtIUsr ; (still okay)
lxi h,MsgAmb ; it's ambiguous
call pstr
jmp Abort
;
GtIUsr: shld TailPt ; save command tail pointer
lda InpFcb ; get driv (A=1)
ora a ; is there one?
jrnz GtIUs1 ; (yes)
lda DftDsk ; no, get default
GtIUs1: dcr a ; make A=0
sta InDrv ; ..and store it
lda InpFcb+13 ; get user
sta InUsr ; ..and store it
ret
;
; OpnInp -- open input file
;
OpnInp: mvi a,128 ; initialize pointer
sta GetPtr
sub a ; initialize end-of-file flag
sta GetFlg
lxi d,InpFcb
call initfcb
call InDU ; set drive/user for input
lxi d,InpFcb ; open input file
call f$open
rz ; (all okay)
lxi h,MsgNIF ; open error
call pstr
jmp Abort
;
; OpnOut -- open output file
;
OpnOut: sub a ; initialize pointer
sta PutPtr
lxi d,OutFcb
call initfcb ; initialize FCB
call OutDU
lxi d,OutFcb ; open output file
call f$exist ; does it already exist?
jrz OpnOu1 ; (no)
lxi d,OutFcb
call f$delete ; yes, delete it
jrz OpnOu1
lxi h,MsgNEr ; can't erase it
call pstr
jmp Exit
OpnOu1: lxi d,OutFcb
call f$mopen
rz ; (okay)
lxi h,MsgNOF ; open error
call pstr
jmp Exit
;
; RdLoop -- reads and writes until end of file
;
RdLoop: call FGetC ; get a character
jc RdErr ; (read error)
rz ; (end of file)
cpi CpmEof ; end of file?
cz ChkEof ; (yes, check mode)
rz ; (yes)
call FPutC ; write character
jc WrtErr ; (write error)
jr RdLoop
;
RdErr: lxi h,MsgREr ; we have an input read error
call pstr
jmp Abort
;
WrtErr: lxi h,MsgWEr ; we have an output write error
call pstr
jmp Abort
;
; ChkEof -- checks for Option O and, if so, ignores end-of-file character
;
ChkEof: mov b,a ; save character in B
lda OpOFlg ; get object flag
ora a
mov a,b ; get character back in A
ret
;
; ClsIFl -- close input file
;
ClsIFl: call InDU ; close input file
lxi d,InpFcb
call f$close
rz ; (okay)
lxi h,MsgCIF
call pstr ; error
jmp Abort
;
; ClsOFl -- closes output file
;
ClsOFl: call OutDU
lda OpOFlg ; check option O flag
ora a
jrnz SkpEof ; (object file transfer, skip EOF)
mvi a,CpmEof ; put end-of-file character
call FPutC
lda PutPtr ; check pointer
ora a
jz ClsOut ; we just wrote, so close
mov b,a
mvi a,128 ; fill rest of record with ^Z
sub b
mov b,a
FillZ: mvi a,CpmEof
push b
call FPutC
pop b
djnz FillZ
jr WrLast
;
SkpEof: lda PutPtr ; check pointer
ora a
jz ClsOut ; just wrote, so close
WrLast: lxi d,OWork ; we've got to write the last record
mvi c,SetDma
call Bdos
lxi d,OutFcb
mvi c,FWrite
call Bdos
ora a ; check for error
jnz WrtErr ; (yes, abort)
;
ClsOut: call OutDU ; close output file
lxi d,OutFcb
call f$close
rz ; (okay)
lxi h,MsgCOF
call pstr ; close error
jmp Abort1
;
; GetDfD -- gets default disk (A=0) and stores it (A=1)
;
GetDfD: mvi c,CurDsk ; get default disk
call Bdos
inr a ; make it fcb compatible
sta DftDsk
ret
;
; PrtFn -- Prints drive/user and filename on console
;
PrtFn: call stndout
mov a,b ; get drive
adi 'A' ; make it printable
call cout ; ..and print it
mov a,c ; get user
call pafdc ; ..and print it
mvi a,':'
call cout
call pfn2 ; print filename
call stndend
call crlf
ret
;
; EatSpc -- gobbles up spaces
;
EatSpc: mov a,m
cpi ' ' ; is it a space?
inx h
jrz EatSpc ; (yes)
dcx h
ret
;
; OutDU -- sets default drive and user for output file
;
OutDU: lda OutUsr
mov c,a
lda OutDrv
mov b,a
call logud
ret
;
; InDU -- sets default drive and user for input file
;
InDU: lda InUsr
mov c,a
lda InDrv
mov b,a
call logud
ret
;
; FGetC -- returns character from file. Assumes file has been
; successfully opened. Returns character or ^Z (end-of-file) in
; A. Zero set (Z) on end of file. Carry set (C) if error.
;
FGetC: lda GetFlg ; check end-of-file flag
ora a
jrnz GetEof ; (yes)
lda GetPtr ; get pointer
cpi 128 ; done with buffer?
jrc GetChr ; (no, get a character)
lxi d,IWork
mvi c,SetDMA ; set DMA address
call Bdos
call InDU ; set DU
lxi d,InpFcb
mvi c,FRead ; read more file
call Bdos
cpi 1 ; return code?
jrz GetEof ; (end of file)
jrnc GetErr ; (a problem)
sta GetPtr ; put 0 in pointer
;
GetChr: lxi h,IWork ; point to DMA buffer
mov e,a ; put pointer in DE
mvi d,0
dad d ; add it to HL
mov a,m ; get next character
lxi h,GetPtr ; increment pointer
inr m
stc
cmc ; clear carry
ret
;
GetEof: mvi a,CpmEof
sta GetFlg ; set end-of-file flag
stc
cmc ; clear carry
ret
;
GetErr: mvi a,CpmEof
sta GetFlg
stc ; set carry
ret
;
; FPutC -- Writes character to file. Assumes file has been successfully
; opened. Expects character in A. Returns carry set (C) on error.
;
FPutC: mov c,a ; save character in C
lda PutPtr ; get pointer
cpi 128 ; buffer full?
jrc PutChr ; (no, so do it)
push b ; the character is threatened from all sides
lxi d,OWork
mvi c,SetDma ; setting output DMA
call Bdos
call OutDU ; set drive and user
lxi d,OutFcb ; ..and write out buffer
mvi c,FWrite
call Bdos
pop b ; get back out character
cpi 0 ; return code?
jrnz PutErr ; (problem)
sta PutPtr ; reset pointer to 0
;
PutChr: lxi h,OWork ; point to DMA buffer
mov e,a ; move pointer to DE
mvi d,0
dad d ; ..and add it to HL
mov m,c ; write character
lxi h,PutPtr
inr m ; increment pointer
sub a ; clear carry
ret
;
PutErr: stc ; set carry
ret
;
; GetOpt -- checks command tail for user supplied options and sets
; appropriate option flags. Invalid options are ignored.
;
GetOpt: lxi h,CTail ; point to command tail
lda CpmDma ; anything there?
ora a
rz ; (no)
mov b,a ; yes, put number of chars in B
ScnDLp: mov a,m ; get character
cpi '/' ; delimiter?
jz ScnOpt ; (yes)
mov d,a ; save character
ScnDL2: inx h ; no, keep looking
djnz ScnDLp
ret ; (none found, return)
;
ScnOpt: push psw ; save current character
mov a,d ; get back previous character
pop d ; put current character in D
cpi ' ' ; was previous char a space?
jnz ScnDL2 ; (no)
jmp ScnOp2
;
ScnOLp: call ScnTbl
xchg ; point back to options
ScnOp2: inx h
djnz ScnOLp ; loop through options
ret
;
ScnTbl: mov c,m ; put option in C
lxi d,OptTbl ; point DE to option table
ScnTLp: ldax d ; get table option
ora a ; end of table?
jz NoMat ; (yes, no match)
inx d ; no, keep looking
cmp c ; match?
jz TMatch ; (yes)
inx d ; move pointer to next entry
inx d
jmp ScnTLp ; ..and keep looking
;
NoMat: xchg
ret
;
TMatch: push h ; save option pointer
ldax d ; put address from table into HL
mov l,a
inx d
ldax d
mov h,a
pop d ; recover option pointer in DE
mvi a,1 ; set option flag by jumping to
pchl ; ..table routine and returning
;
; OptTbl -- Option Jump Table
;
OptTbl: db '/' ; / = usage message
dw OptH
db 'O' ; O = object file transfer
dw OptO
db 0 ; end of option jump table
;
; Option setting routines
;
OptH: lxi h,MsgUse
call pstr
jmp Exit
OptO: sta OpOFlg
ret
;
; Data storage . . .
;
OpOFlg: db 0
;
OutFil: db ' ' ; save original output filename here
OutTyp: db ' '
BakTyp: db 'BAK' ; for BAK file
TmpTyp: db '$$$' ; for temporary filename
;
GetFlg: db 0 ; FGetC end-of-file flag
GetPtr: db 0 ; FGetC pointer
InpFcb: db 0 ; input file fcb
FcbIFn: db ' '
FcbIFt: db ' '
ds 24
;
PutPtr: db 0 ; FPutC pointer
OutFcb: db 0 ; output file fcb
FcbOFn: db ' '
FcbOFt: db ' '
ds 24
;
DSEG
;
; Uninitialized storage . . .
;
DftDsk: ds 1 ; default drive
InDrv: ds 1 ; input file drive
InUsr: ds 1 ; input file user
OutDrv: ds 1 ; output file drive
OutUsr: ds 1 ; output file user
OldStk: ds 2 ; old stack pointer
TailPt: ds 2 ; command tail index pointer
CTail: ds 128 ; command tail storage
;
IWork: ds 128*BufSiz ; input buffer
OWork: ds 128*BufSiz ; output buffer
;
end