home *** CD-ROM | disk | FTP | other *** search
-
- ; Program: CON.MAC
- ; Author: Joe Wright
- ; Date: 23 June 90
- ;
- vers equ 20 ; Release 23 June 90
- ;
- ; A simple file concatenation program to combine two files to a third.
- ;
- ; Syntax: CON <[DU:]FILE3>=<[DU:]FILE1>,<[DU:]FILE2>[ /]
- ;
- ; There must be three valid filespecs on the command line. The files
- ; are assumed to be ^Z terminated ASCII unless the '/' option is used.
- ; In that case, ^Z is ignored and the files are read until their end.
- ;
- ; Method:
- ; 1. Create FILE3 and copy FILE1 to it.
- ; 2. Append FILE2 to FILE3.
- ; 3. Close FILE3.
- ;
- ; We will read ASCII input files one character at a time and transfer
- ; them to a Write buffer. The Write buffer will be written to FILE3
- ; as it fills and/or when the input file(s) are exhausted. With the
- ; Binary option selected, Input files are read record at a time directly
- ; to the Output buffer.
-
- ; No attempt is made to make this particularly fancy. The interested
- ; hacker may want to make it a Z utility, treat NDIR, etc.
-
- ; Assemble with any of:
- ; M80 =CON
- ; SLRMAC CON
- ; SLRMAC CON/A Direct .COM file
- ; or rename to CON.ASM and:
- ; RMAC CON $-SPZ Suppress .SYM and .PRN
- ;
- ; Link potential (MicroSoft) .REL to .COM with any of:
- ; SLRNK /P:100,CON,CON/N/E
- ; SLRNKP CON/N,/A:100,CON,/E
- ; ZML CON
- ; L80 /P:100,CON,CON/N/E
- ; LINK CON[NR] Suppress .SYM
-
- ; Declare the Z80 macro library. M80 needs the full filespec in
- ; upper case. RMAC expects only MACLIB Z80 without the .LIB extent
- ; and will complain about it but will assemble correctly. SLRMAC
- ; uses MACLIB Z80[.LIB] as a pseudop to select internal Z80 opcodes
- ; and does not actually read Z80.LIB.
-
- maclib Z80.LIB ; Upper case for M80
-
- fcb1 equ 5ch
- tbuf equ 80h
-
- cr equ 13
- lf equ 10
- ctlz equ 'Z'-40h
-
- bufsiz equ 16 ; Output buffer size (1..32k)
- tim equ 8 ; Print 'Propeller' every 8 SETDMA's (1k)
-
- jmp start ; Jump over the patches
-
- bin: db 0 ; Default to ASCII mode (Patchable)
- buf: db bufsiz ; In kilobytes (1024 bytes)
-
- start: sspd stack ; Save CCP stack pointer
- lxi sp,stack ; Set local stack
- ;
- ; Sign us up.
- ;
- call print
- db ' CONcatenate v',vers/10+'0','.',vers mod 10+'0'
- db ' (',0
-
- lda fcb1+1 ; Get first character of command line
- cpi ' ' ; Space?
- jrz help
- cpi '/' ; Explicit Help?
- jnz go ; Get over Help screen
-
- help: call mode ; Print 'Ascii' or 'Binary' modes
- call print
- db ' Syntax: CON '
- db '[du:]outfile=[du:]infil1[,[du:]infil2][ /]',cr,lf
- db ' where [ /] option denotes Binary/Ascii toggle.',0
- jmp exit
- ;
- mode: lda bin ; Get Mode byte
- ora a ; Test it
- jrz defa ; Ascii
-
- call print
- db 'Binary',0
- jr defx ; Close it up
-
- defa: call print
- db 'Ascii',0
-
- defx: call print
- db ')',cr,lf,0
- ret
- ;
- ; Setup our pointers..
- ;
- go: lxi h,codend+255 ; Next page after stack
- mvi l,0 ; Round down to even page
- shld otbuf ; Output buffer start
- shld otptr ; Output pointer
- ;
- ; Check for valid Output buffer size
- ;
- lda buf ; Output buffer size (1..32)
- dcr a ; Rel 0..31
- cpi 32 ; 32k is maximum
- jnc help ; Quit through HELP
- ;
- ; Calculate and set Output buffer end address
- ;
- inr a ; Restore A
- add a ; X2
- add a ; X4
- add h
- mov h,a ; L is already 0 from above
- shld otend ; Output buffer end
- inr h ; Next page
- shld inplst ; Input file list
- shld inpptr ; Initialize pointer
- ;
- ; Get Default Drive and User
- ;
- mvi c,25
- call bdos ; Current Drive
- inr a ; Rel 1
- mov h,a ; To H
- mvi e,255 ; Get User
- call gsusr
- mov l,a ; User to L
- shld defdu ; Save it
- ;
- ; Parse the command line and Open input file(s)
- ;
- call parser ; Parse to OUTFCB and INPLST
- call newin ; First input file
- jz synerr ; Syntax error, no input file specified
- ;
- ; Open all the input files.
- ;
- opnfil: call open ; Open the New file
- jm nofil ; Not found
- call newin ; Next input file
- jrnz opnfil ; Again..
- ;
- call iniptr ; Reset the input file pointer
- ;
- ; Input file(s) open, attempt making output file.
- ;
- lxi d,outfcb ; Output file
- call make ; Delete and Make it
- jm dirful ; Directory full, quit
- ;
- ; Setup is OK, get on with business.
- ;
- lda bin ; Binary option flag
- ora a
- jrz ascii ; Ascii treatment
- jr binary ; Binary treatment
- ;
- ; Binary option selected. Fast record by record action.
- ;
- binar: call getr ; Copy input to output
- binary: call newin ; Set up INPFCB, return Z if no more
- jrnz binar ; Next file..
- jr fin ; No more files
- ;
- ; This is the top of the GETR/PUTR routine. See GETR.
- ;
- putr: lded otend ; End of Output buffer
- dsbc de ; Compare with otptr
- jrnz getr ; Buffer not full
- call wrbuf ; Write the buffer
- shld otptr ; New Output pointer
- ;
- ; This is the entry to GETR/PUTR. We read the input file until its
- ; physical end, looping throuth PUTR to write the buffer as it fills.
- ;
- getr: lhld otptr ; Output pointer is DMA address
- call fread ; Read a record
- rnz ; End of File, Bail out..
- call ad128 ; Bump HL 128 bytes
- shld otptr ; New Output pointer
- jr putr ; Keep it up
- ;
- ; ASCII file(s), terminated with first ^Z.
- ;
- asci: call geta ; Copy input to output
- ascii: call newin ; Next Input file
- jrnz asci ; Do it again..
- ;
- filz: lhld otptr ; Output pointer
- fil: mvi m,ctlz ; Fill remainder of 128 bytes with ^Z
- inx h ; Increment pointer
- mov a,l ; Check Lo order
- ani 127 ; Modulus 128
- jrnz fil ; Again..
- shld otptr ; New output pointer
- ;
- fin: call wrbuf ; Write partial Output buffer
- lxi d,outfcb
- call close ; Fall through to EXIT
- ;
- exit: lspd stack ; Get the CCP stack pointer
- ret ; That's all folks..
- ;
- ; Top of GETA/PUTA routine. See GETA.
- ;
- puta: lded otend ; End of buffer
- dsbc de ; Difference to HL
- ;
- ; The following check ensures that the output buffer overflows
- ; by at least one and up to 128 bytes.
- ;
- jrc geta ; Not full yet
- jrz geta ; Full. Get one more anyway
- ;
- ; Output buffer has overflowed. Take care of it.
- ;
- sded otptr ; otend to otptr for WRBUF routine
- push d ; Save it on the stack
- push h ; Characters beyond otend on the stack
- call wrbuf ; Write the buffer
- ;
- ; As we have forced overflow, copy the excess to OTBUF and
- ; set OTPTR to the next byte.
- ;
- pop b ; Length of overflow to BC
- xchg ; otbuf to DE
- pop h ; otend to HL
- ldir ; Move overflow to otbuf
- sded otptr ; New pointer, fall through to GETA
- ;
- ; This is the entry to the GETA/PUTA routine. Read records from the
- ; input file to the output buffer until End of File. Loop through
- ; PUTA to write the buffer as it fills. Each character is tested
- ; for ^Z to ensure that we catch the 'first' one in a file.
- ;
- geta: lhld otptr ; Out pointer
- call fread ; Read a record
- rnz ; End of File
- ;
- ; A new record was read into the buffer. Check it for ^Z and
- ; advance HL while doing so.
- ;
- lxi b,128 ; Check 128 bytes
- mvi a,ctlz ; For ^Z
- ccir ; Z80 CPIR instruction
- jrnz notz ; Not found
- dcx h ; Back up to ^Z
- notz: shld otptr ; New output pointer (+0..128)
- jrnz puta ; If ^Z not found
- ret
- ;
- ; WRBUF - Called from PUTA and PUTR when buffer fills and from CLOSE
- ;
- wrbuf: lhld otptr ; Current pointer
- lded otbuf ; Output buffer start
- dsbc de ; Bytes in the buffer to HL
- rz ; Buffer empty
- ;
- ; HL is the number of bytes in the buffer. Calculate the number
- ; of records to write.
- ;
- mvi a,127 ; Balance of one record
- call addhl ; Add it to HL
- dad h ; Shift HL left
- mov b,h ; Number of records to write in B
- ;
- ; Setup for WRIT routine.
- ;
- xchg ; Buffer start to HL
- push h ; Save it on the stack
- lxi d,outfcb ; Output file
- call logusr ; Log its user
- ;
- ; Write B records to the output file.
- ;
- writ: call setdma
- call fwrit ; Write one record
- jrnz dskful
- call ad128 ; Add 128 to HL
- djnz writ ; Decrement record count, Again..
- ;
- ; Buffer written. Restore buffer start address and quit.
- ;
- pop h ; Buffer start to HL
- ret
- ;
- dskful: call close ; FCB still set from above
- call print
- db ' Disk Full',0
- jmp exit
- ;
- ; Specified Input file cannot be found.
- ;
- nofil: call print
- db ' Can''t open input file',0
- jmp exit
- ;
- ; No directory space.
- ;
- dirful: call print
- db ' Directory Full',0
- jmp exit
- ;
- ; Parse any number of filespecs from the command tail into their
- ; associated FCB's.
- ;
- parser: lxi h,tbuf+1 ; Command tail
- call check ; Check for ambiguity and Option
- lxi d,outfcb
- call fname ; Parse the Output filespec
- jr parse
- ;
- ; Now the Input filespecs..
- ;
- pars: call same ; Identity check
- parse: call newin ; Point DE to next infcb in the list
- call inifcb ; Initialize the FCB
- call fname ; Parse the filespec to it
- jrnz pars ; Until FNAME returns Zero
- ;
- ; Initialize the Input file list pointer
- ;
- iniptr: lhld inplst ; Beginning of the list
- shld inpptr ; Set the pointer
- ret
- ;
- ; Initialize potential FCB at DE
- ;
- inifcb: push h ; Command tail pointer
- xra a ; Get a null
- stax d ; Clear Drive byte
- lxi h,12 ; Extent
- dad d ; Point HL to it
- mov m,a ; Clear it
- lxi h,32 ; Current record byte
- dad d ; Point HL to it
- mov m,a ; Clear it
- pop h ; Command tail pointer
- ret
- ;
- ; NEWIN - Return DE (and INPFCB) pointing to the next Input FCB.
- ; Return Z flag if new FCB is blank.
- ;
- newin: push h ; Save HL
- lded inpptr ; Point to 34-byte FCB
- lxi h,34 ; Length of it
- dad d ; Point to next one
- shld inpptr ; New pointer to next FCB
- inx d ; Point to Drive byte of this one
- sded inpfcb ; New FCB pointer for FREAD
- ldax d ; Get drive byte
- ora a ; Test for Zero
- pop h ; Restore HL
- ret
-
- ; FNAME - File Spec Parser
- ; Enter with HL pointing to a filespec and DE pointing to an FCB.
- ; Return Z flag if no more files.
-
- fname: call sksp ; Skip spaces and delimiters
- ora a
- rz ; Nothing to do if Null
- cpi '/' ; Option, not a filespec
- rz
- ;
- ; Ok, we're on. Parse it.
- ;
- lbcd defdu ; Default Drive/User to BC
- push h ; Command tail pointer
- call skdel ; Find first delimiter (in A)
- pop h ; Restore pointer
- cpi ':' ; Check for DU spec
- jrnz fnam ; None
- ;
- ; Resolve D:, U:, or DU:
- ;
- mov a,m ; Drive letter?
- sui 'A' ; Remove ascii bias
- cpi 16 ; Cy if A..P
- jrnc usr ; No Drive spec
-
- inr a ; Rel 1 Drive
- mov b,a ; To B
- inx h ; Next character in tail
- ;
- ; Evaluate potential user number (0..31)
- ;
- usr: push d ; Free up the E register
- mvi e,0 ; Clear it
-
- ulp: mov a,m ; Get the 'next' character
- cpi ':' ; End of User spec?
- jrz uex ; Yes.
- ;
- mov a,e ; Previous digit
- add a ; X2
- add a ; X4
- add e ; X5
- add a ; X10
- mov e,a ; Save it
- mov a,m ; Current digit
- ani 0fh ; Strip ASCII
- add e ; Add it to the Last one
- mov e,a ; Save it again
- mov c,a ; Maybe this time..
- inx h ; Point to next character
- jr ulp ; Try again
-
- uex: mov a,c ; User number
- cpi 32
- jrnc synerr ; Too Big
-
- inx h ; Skip past the ':'
- pop d ; Restore DE
- ;
- fnam: push d ; Save FCB pointer on stack
- xchg ; FCB pointer to HL
- dcx h ; User
- mov m,c ; Put User
- inx h
- mov m,b ; Put Drive
- xchg ; BC is now 'scratch'
- inx d ; Point to FCB Name
- ;
- mov a,m ; First character of filename
- call isdel ; Maybe not specified
- jrz outin ; If not specified
- ;
- mvi b,8 ; Eight chars in name
- nam: mov a,m
- call isdel ; Check for delimiter
- jrz snam ; Space fill if so
- inx h ; Bump the pointer
- cpi '.' ; Check for '.'
- jrz snam ; Fill name with Spaces if so
-
- stax d ; Put character in FCB
- inx d ; Bump pointer
- djnz nam ; Next..
-
- mov a,m ; Ninth character
- call isdel ; Check for delimiter
- jrz ftyp ; Do type if so
-
- inx h ; Past potential '.'
- cpi '.'
- jrz ftyp ; Was '.', do type
-
- synerr: call print
- db ' Syntax Error',0
- jmp exit
-
- snam: call spaco ; Space fill balance of filename
-
- ftyp: mvi b,3 ; Three characters in 'type'
- typ: mov a,m
- call isdel ; Zero if delimiter
-
- cz spaco ; Space out the filetype..
- jrz fnamx ; ..and Quit
-
- inx h ; Bump pointer
- stax d ; Put char in FCB
- inx d ; Bump pointer
- djnz typ ; Next..
-
- mov a,m ; Fourth character of type
- call isdel ; Check for delimiter
- jrnz synerr ; If not
-
- fnamx: pop d ; Restore FCB pointer
- ori 255 ; NZ
- ret
-
- ; OUTIN - Input filespec is blank except perhaps for DU:
- ; Copy Output filename and type to Input FCB.
-
- outin: push h ; Command tail pointer
- lxi h,outfcb+1 ; Output FILENAME.TYP
- lxi b,11 ; Eleven bytes
- ldir ; Move them to Input FCB
- pop h ; Command tail pointer
- jr fnamx ; Restore FCB pointer and quit
-
- ; SPACO - Fill the remainder (B) of Name or Typ with Spaces.
- ; Note that DJNZ does not change any flags.
-
- spaco: mvi a,' ' ; Get a Space
- filla: stax d ; Put it in the FCB
- inx d ; Bump pointer
- djnz filla ; B times..
- ret
- ;
- ; Add 128 to HL.
- ;
- ad128: mvi a,128 ; Fall through to ADDHL
- ;
- ; Add A to HL. Uses only AF. Flags have no real meaning.
- ;
- addhl: add l ; Add low order
- mov l,a ; Back to L
- rnc ; If LT 256
- inr h ; Overflow to H
- ora a ; Clear Cy
- ret
- ;
- ; PRINT - Ye Olde Inline console output routine
- ;
- print: xthl ; Return address to HL
- call pstr ; Print string at return address
- xthl ; New return address on the stack
- ret
- ;
- pstr: mov a,m ; Get a character
- inx h ; Bump pointer
- ora a ; Test for Null
- rz ; Quit if so
- call cout ; Ship the character to the console
- jr pstr ; Next..
-
- cout: mov e,a
- mvi c,2 ; Conout
- jr bdos
-
- setdma: xchg
- mvi c,26 ; Set DMA address
- call bdos
- xchg
- jmp spin ; Spin the Propeller
-
- delete: call logusr
- mvi c,19 ; Delete
- jr bdos
-
- make: call delete
- mvi c,22 ; Make
- jr bdos
-
- open: call logusr
- mvi c,15 ; Open
- jr bdos
-
- close: call logusr
- mvi c,16 ; Close
- jr bdos
-
- fwrit: mvi c,21 ; Write
- jr bdos
-
- fread: call setdma
- lded inpfcb
- call logusr
- mvi c,20 ; Read sequential
- ;
- bdos: push h
- push d
- push b ; Save the registers
- call 5 ; Call the operating system
- ora a ; Note Minus flag set if A = 0FFh
- pop b
- pop d
- pop h ; Restore the registers
- ret
- ;
- logusr: push d ; Save FCB pointer
- dcx d ; Point to user
- ldax d ; Get user number
- mov e,a ; To E
- call gsusr ; Log the User
- pop d ; Restore FCB pointer
- ret
- ;
- gsusr: mvi c,32 ; Get or Set User
- jr bdos
-
- ; Skip spaces and delimiters
-
- sksp: mov a,m
- ora a
- rz ; End of string
- call isdel
- rnz ; Non-delimiter in A
- inx h
- jr sksp
- ;
- ckerr: call mode
- jmp synerr
-
- ; Check for any of '?' or '*'. None allowed.
-
- check: push h ; Save Command tail pointer
- ;
- chek: mov a,m
- inx h
- ora a
- jrz chekx ; End of command tail
-
- cpi '?'
- jrz ckerr
- cpi '*'
- jrz ckerr
- cpi '/'
- jrnz chek
- ;
- lxi h,bin ; Point to Option byte
- mov a,m ; Get it
- cma ; Complement it
- mov m,a ; Put it back
-
- chekx: pop h ; Command tail pointer
- jmp mode ; Return through MODE routine
- ;
- ; SAME - Called by PARSER to ensure unique Output file.
- ;
- same: push h
- push d ; Save FCB pointer
-
- dcx d ; Point to User
- lxi h,outusr
- mvi b,13 ; Check 13 bytes
-
- sam: ldax d
- cmp m
- jrnz samx ; Not the same
- inx d
- inx h
- djnz sam
-
- call print
- db ' Output=Input error',0
- jmp exit
-
- samx: pop d ; Restore FCB pointer
- pop h
- ret
-
- ; Skip past any of Space, Equal, Comma, Colon or Null. Return in A.
-
- skdel: mov a,m
- inx h
- call isdel
- jrnz skdel
- ret
-
- isdel: ora a
- rz ; Null
- cpi ' '
- rz ; Space
- cpi ','
- rz ; Comma
- cpi ':'
- rz ; Colon
- cpi '='
- ret ; Equal?
- ;
- ; This is my implementation of Al Hawley's Propeller to show
- ; activity on the screen while CON is otherwise occupied.
- ; It is called for each SETDMA and will print a 'blade' on
- ; the screen each time TIME goes to zero.
- ;
- spin: push h ; Save HL
- lxi h,time ; Time constant
- dcr m ; Count it down
- jrnz timx ; Quit if not zero
-
- mvi m,tim ; Preset the timer
- lxi h,cntr ; Blade counter
- mov a,m
- ani 3 ; Modulus 4
- inr m ; Advance the counter
- lxi h,blad ; Blade list
- call addhl ; Point to next blade (+0..3)
- mov a,m ; Get it
- sta char ; Put it in the prop string
- lxi h,prop ; Point to the string
- push d ; Save DE
- call pstr ; Print the string
- pop d ; Restore DE
-
- timx: pop h ; Restore HL
- ret
-
- prop: db cr,' '
- char: db ' ',0
- cntr: db 0
- blad: db '|/-\'
- time: db tim
- ;
- ; The File Control Blocks are preceded by one byte which
- ; holds the User Number associated with the file. 34 bytes.
- ;
- outusr: db 0
- outfcb: db 0,' ',0,0,0,0
- ds 16
- db 0
- ;
- ; Uninitialized data starts here.
- ;
- otbuf: ds 2 ; Output buffer start
- otptr: ds 2 ; Current pointer into OTBUF
- otend: ds 2 ; End of output buffer
- defdu: ds 2 ; Default Drive/User
- inplst: ds 2 ; Input file list
- inpptr: ds 2 ; Pointer to next input FCB user
- inpfcb: ds 2 ; Pointer to current input FCB
- ;
- ds 48 ; Local stack space
- stack: ds 2 ; CCP stack pointer
-
- codend:
- end
- ;
- ; End of CON.MAC