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
/
JSAGE
/
ZSUS
/
SUBSCRIP
/
ZSUS3-03.LZH
/
CCOUNT10.LBR
/
CCOUNT10.ZZ0
/
CCOUNT10.Z80
Wrap
Text File
|
1992-03-07
|
21KB
|
751 lines
; CCOUNT.Z80
;
; Counts characters in text files.
;
Vers equ 10
SubVers equ ' '
;
; HISTORY:
;
; Version 1.0 -- March 7, 1992 -- Gene Pizzetta
; Based on a very mimimal test program I have used for a long time
; (It had the search characters hard coded into it). I decided that
; maybe I should release it after sprucing it up and making some
; additions: entering search bytes on the command line, option
; to count all characters, screen paging, 32-bit long-word counters
; and display so more than 65,535 characters can be counted, help
; screen, etc. etc. The result is an all-new program.
;
; System addresses . . .
;
CpmFcb equ 5Ch ; default file control block
CpmDma equ 80h ; default DMA buffer
;
; ASCII . . .
;
CtrlC equ 03h ; ^C
TAB equ 09h ; tab
LF equ 0Ah ; line feed
CR equ 0Dh ; carriage return
CtrlS equ 13h ; ^S
CpmEof equ 1Ah ; ^Z (end-of-file)
;
; Program specifications . . .
;
NumSiz equ 10 ; minimum length of long-word output
BufSiz equ 8 ; buffer size in records (sectors)
;
.request zslib,z3lib,syslib
;
ext eatspc,eatnspc,zcheck,fxropen,fxrclose,fxget ; ZSLIB
ext parcnt,parget,uncaps,hvtinit,hvdinit,hvon,hvoff
ext gcomnam,comnam
ext z3init,envptr,z3log,getcrt,puter2,inverror ; Z3LIB
ext isdigit,retud,initfcb,eval,padc,pa2hc,pafdc ; SYSLIB
ext caps,eprint,epstr,crlf,pfn2,cin,condin,cout
ext codend
ext plwdc ; ZSLIB 3.6
;
jp Start
;
db 'Z3ENV',1
Z3EAdr: dw 0
;
; Configuration . . .
;
dw 0 ; filler
db 'CCOUNT' ; for ZCNFG
db Vers/10+'0',Vers mod 10+'0'
AllFlg: db 0 ; 0=requested matches, FFh=all matches
HiFlg: db 0 ; 0=ignore high bits, FFh=don't
PagFlg: db 0 ; 0=page screen, FFh=don't
AbtFlg: db 0 ; 0=abort no error, FFh=abort is error
CRTLns: db 24 ; number of lines on screen (CP/M only)
;
; Start of Program . . .
;
Start: ld hl,(Z3EAdr)
call zcheck ; check for Z-System
jr z,IsZ3 ; (it is)
ld hl,0 ; it's not, so make ENVPTR zero
IsZ3: call z3init ; initialize ENVPTR
ld (Stack),sp ; save stack pointer
ld sp,Stack ; set up new stack
ld hl,DftNam ; point to default program name
call gcomnam ; get invocation name, if possible
call hvtinit ; initialize terminal
;
call GetOpt ; initialize data area, parse command line
call IniFil ; set up control block, log into directory
call OpnFil ; open file
call RdLoop ; process file byte by byte
call ClsFil ; close file
call Report ; print results
;
Finish: xor a ; no error
ErExit: ld b,a ; put error code in B
call Z3Chk ; Z-System?
jr z,Exit ; (no, so skip error flag)
ld a,b ; recover error code
call puter2 ; load program error flag
or a ; error?
call nz,inverror ; (if so, invoke error handler)
Exit: call hvdinit ; de-initialize terminal
ld sp,(Stack) ; restore stack
ret ; return to system
;
; Subroutines . . .
;
; GetOpt -- Initialize data area and parse command line parameters.
;
GetOpt: xor a ; initialize data segment to nulls
ld (BegIni),a
ld hl,BegIni
ld de,BegIni+1
ld bc,EndIni-BegIni
ldir
ld hl,(HiFlg) ; set default options
ld (OpHFlg),hl
ld a,(AllFlg)
ld (OpAFlg),a
call Z3Chk ; Z-System?
ld a,(CRTLns) ; assume default screen lines
jr z,GetOp1 ; (not Z3, use default)
call getcrt ; get screen lines from environment
inc hl
ld a,(hl)
GetOp1: dec a ; screen lines - 1
ld (ScrLns),a ; ..to permanent storage
dec a ; screen lines - 2 for first screen
ld (LnsCnt),a ; ..to screen lines counter
ld hl,CpmDma+1 ; point to command tail
ld a,1 ; check first token
push hl ; save CL pointer
call parget ; make sure we have a file spec
pop hl ; restore pointer
jp z,Usage ; (none, show how to use it)
cp '/' ; usage request?
jp z,Usage ; (yes)
ld a,2 ; check second token
call parget ; do we have any search characters?
ret z ; (none)
cp '/' ; is the first character a slash?
jr z,GotOp1 ; (yes, must be options)
ld hl,CpmDma+1 ; point back to start of command line
ld de,SchStr ; point to search byte storage
ld c,1 ; start with second token
OpLoop: inc c ; point to next token
ld a,c ; move token counter to A for PARGET
push hl ; save CL pointer
call parget ; get token
jr z,PrsEnd ; (end of command line)
cp '"' ; upper-case letter?
jr z,AscChr ; (yes)
cp '''' ; lower-case letter?
jr z,AscLow ; (yes)
cp '^' ; control character?
jr z,AscCtl ; (yes)
cp '/' ; option string?
jr z,GotOpt ; (must be)
call isdigit ; decimal or hex?
jr nz,PrsErr ; (nope, invalid parameter)
push de ; save storage pointer
call eval ; evaluate number
jr c,PrsErr ; (EVAL error)
ld a,d ; high byte zero?
or a
jr nz,PrsErr ; (no, number too large)
dec hl ; prepare for ChkPrs
ld a,e ; get low byte from EVAL
pop de ; recover storage pointer
OpLp1: ld (de),a ; store converted parameter
inc de ; point to next open slot
call ChkPrs ; check for illegal trailing character
ld hl,SchCnt ; point to search byte counter
inc (hl) ; ..and increment it
pop hl ; recover CL pointer
jr OpLoop ; ..and continue with next token
;
; End of command line parse
PrsEnd: pop hl ; adjust stack and return
ret
;
AscLow: inc hl ; point to character after quote
ld a,(hl) ; get it
call uncaps ; make it lower-case
jr OpLp1 ; store it and continue
;
AscChr: inc hl ; point to character after quote
ld a,(hl) ; get it
call caps ; make it upper-case
jr OpLp1 ; store it and continue
;
AscCtl: inc hl ; point to character after caret
ld a,(hl) ; get it
sub '@' ; make it a control character
jr nc,OpLp1 ; (if okay, store it and continue)
;
; Invalid command line parameter
PrsErr: ld a,19 ; error code
call eprint
db 'Invalid parameter.',0
jp ErExit
;
; Parse command line options after slash
GotOpt: pop de ; adjust stack
GotOp1: inc hl ; point to next character
ld a,(hl) ; get it
or a
ret z ; (zero is end of command line)
cp 'A' ; count all characters?
jr z,OptA
cp 'H' ; make high bits significant?
jr z,OptH
cp 'P' ; page screen?
jr z,OptP
cp ' ' ; space?
jr z,GotOp1 ; (skip intervening and trailing spaces)
ld a,19 ; error code
call eprint
db 'Invalid option.',0
jp ErExit
;
OptA: ld de,OpAFlg ; point to option A flag
jr DoOpt ; ..flip it and continue
;
OptH: ld de,OpHFlg ; point to option H flag
;
DoOpt: ld a,(de) ; get flag
cpl ; flip it
ld (de),a ; put it back
jr GotOp1 ; get next option
;
OptP: ld de,OpPFlg ; point to option P flag
jr DoOpt ; ..flip it and continue
;
; ChkPrs -- Checks for illegal trailing character after parameter token
; is evaluated.
;
ChkPrs: inc hl ; get next character from command line
ld a,(hl)
cp ' '
ret z ; (okay)
cp TAB
ret z ; (okay)
or a
ret z ; (okay)
jr PrsErr ; otherwise, it's wrong
;
; IniFil -- Set up input control block, file control block, and buffers.
; Log into target directory.
;
IniFil: ld hl,InFcb ; HL -> control block FCB
ld de,CpmFcb ; DE -> default FCB
call Z3Chk ; Z-System?
jr nz,IniFl1 ; (yep)
call retud ; for CP/M, get current user
ld a,c
ld (CpmFcb+13),a ; ..and stuff it into FCB
IniFl1: call z3log ; log into directory
ex de,hl
inc hl ; HL -> filename
ld a,(hl) ; anything there?
cp ' '
jr z,MissFn ; (missing filename)
push hl ; save filename pointer
ld bc,11
push bc ; save length count
ld a,'?' ; ambiguous?
cpir
jr z,AmbgFn ; (yep, can't do it)
inc de ; DE -> InFcb filename space
pop bc ; recover length count
pop hl ; recover filename pointer
ldir ; move it
ld a,BufSiz ; put buffer size in input control block
ld (InCtrl),a
call codend
ld (BufAdr),hl ; put buffer address in input control block
ld a,(CpmFcb+15) ; check for invalid directory spec
inc a
jr nz,DirOK ; (directory spec okay)
ld a,2 ; error code
call eprint
db 'Invalid directory.',0
jp ErExit
;
MissFn: call eprint
db 'Missing',0
jr NamErr
;
AmbgFn: call eprint
db 'Ambiguous',0
NamErr: call eprint
db ' filename.',0
ld a,8 ; error code
jp ErExit
;
DirOK: call eprint
db 'Reading ',0
call hvon ; turn on highlighting
call retud ; get drive and user
ld a,b ; get drive
add a,'A' ; ..make it ASCII
call cout ; ..and print it
ld a,c ; get user
call pafdc ; ..and print it
ld a,':'
call cout ; print colon
ld de,InFcb+1 ; point to filename
call pfn2 ; ..and print it
call hvoff ; turn off highlighting
call eprint
db ' .. ',0
ret
;
; OpnFil -- Open file for reading.
;
OpnFil: ld de,InCtrl ; point to input control block
call fxropen ; open file
ret nz ; (open okay)
ld a,10 ; error code
call eprint
db 'Not found.',0
jp ErExit
;
; RdLoop -- Reads and processes characters from file according to user's
; selections. Separate loops are provided for the four basic options
; in the interest of speed (since space is no problem).
;
RdLoop: ld de,InCtrl ; point to input control block
ld a,(OpHFlg) ; check H flag
or a
jr nz,RLoopH ; (high bits are significant)
ld a,(OpAFlg) ; check A flag
or a
jr nz,ALoop ; (count all characters)
;
; GLoop counts only given characters, ignoring high bits
;
GLoop: call fxget ; get character
ret z ; (end of file)
cp CpmEof
jr z,GotEof ; (end of file)
and 7Fh ; reset high bit
call Match ; check for match
jr GLoop ; get next character
;
; ALoop counts all characters, ignoring high bits
;
ALoop: call fxget ; get character
ret z ; (end of file)
cp CpmEof
jr z,GotEof ; (end of file)
and 7Fh ; reset high bit
call CkCon ; check for user abort
call IncTot ; increment total characters counter
call GotMat ; increment character array
jr ALoop ; get next character
;
; RLoopH -- high bits are significant here
;
RLoopH: ld a,(OpAFlg) ; check A flag
or a
jr nz,ALoopH ; (count all characters)
;
; GLoopH counts only given characters, and high bits are significant
;
GLoopH: call fxget ; get character
ret z ; (end of file)
cp CpmEof
jr z,GotEof ; (end of file)
call Match ; check for match
jr GLoopH ; get next character
;
; ALoopH counts all characters, and high bits are significant
;
ALoopH: call fxget ; get character
ret z ; (end of file)
cp CpmEof
jr z,GotEof ; (end of file)
call CkCon ; check for user abort
call IncTot ; increment total characters counter
call GotMat ; increment character array
jr ALoopH ; get next character
;
; GotEof -- End-of-file character is recorded, but is not included
; in the total number of characters because it is not really part
; of the text. Still, it's sometimes useful to know it's there.
;
GotEof: call GotMat ; record ^Z end-of-file character
ret ; ..and quit
;
; Match -- Matches and counts characters for "given characters" loops
; (GLoop and GLoopH). Must preserve DE.
;
Match: call CkCon ; check for user abort
call IncTot ; increment total characters counter
ld hl,SchCnt ; get number of search characters
ld b,(hl) ; ..into B
inc b ; check for zero
dec b
ret z ; (no search characters)
ld hl,SchStr-1 ; point to search characters string
MLoop: inc hl ; point to next search character
cp (hl) ; does it match our character in A?
jr z,GotMat ; (match found, increment character array)
djnz MLoop ; loop to end of search string
ret
;
; GotMat -- Computes address in 1024-byte array by using the value of
; the character in A. Then the four-byte-long word at the computed
; address is incremented. DE is preserved.
;
GotMat: ld bc,CCount ; get address of array
ld l,a ; compute offset into array
ld h,0 ; ..using ASCII value of character
add hl,hl ; ASCII value times 2
add hl,hl ; ASCII value times 4
add hl,bc ; HL -> low byte of corresponding counter
;
; Incr32 -- increments 32-bit binary number (4 bytes, low-byte first)
; pointed to by HL.
;
Incr32: ld b,4 ; 4 bytes
Loop32: inc (hl) ; increment byte
ret nz ; (quit if it hasn't reach zero)
inc hl ; point to next byte
djnz Loop32 ; ..and do it again
ret
;
IncTot: ld hl,ChrCnt
jr Incr32
;
; ClsFil -- Closes input file.
;
ClsFil: ld de,InCtrl ; point to file control block
call fxrclose ; close file
ret nz ; (close okay)
ld a,4 ; error code
call eprint
db 'File close error.',0
jp ErExit
;
; Report -- Prints summary report on console.
;
Report: call crlf
ld hl,ChrCnt ; point to total of characters found
call Prt32 ; and print it
call eprint
db ' -> Total Characters',0
ld a,(OpAFlg)
or a
jr z,ReptG ; (only given characters)
;
; ReptA -- Report all characters in set.
;
ReptA: xor a ; characters from null to FFh
ld c,a ; duplicate character is kept in C
ld b,a ; 256 of them
RALoop: bit 7,c ; 8-bit character?
jr z,RALp1 ; (no)
ld a,(OpHFlg) ; yes, but are we ignoring them?
or a
ret z ; (yes, so we're through)
ld a,c ; recover character
RALp1: call PrtRep ; report on this character
inc c ; increment both copies of character
inc a
djnz RALoop ; loop through them all
ret
;
; ReptG -- Report counts only for characters given on command line
;
ReptG: ld hl,SchCnt ; point to number of search characters
inc (hl) ; check for zero
dec (hl)
ret z ; (no search characters)
ld b,(hl) ; get number into counter
ld hl,SchStr-1 ; point to search string
;
RGLoop: inc hl ; point to next search character
ld a,(hl) ; get it into A
ld c,a ; ..and a duplicate into C
bit 7,a ; 8-bit character?
jr z,RGLp1 ; (nope)
ld a,(OpHFlg) ; check H flag
or a
ld a,c ; get character back into A
jr z,RGLp2 ; (ignore 8-bit character)
RGLp1: call PrtRep ; report on this character
RGLp2: djnz RGLoop ; ..and continue
ret ; done
;
; PrtRep -- Prints report of number found of character in A (also duplicated
; in C), followed by character value in ASCII, hexadecimal, and decimal.
;
PrtRep: push af ; save character
call CkScrn ; increment screen counter, check for pause
pop af ; restore character
call PrtMat ; print count
call eprint
db ' -> ',0
push hl ; save SchStr pointer
call PrtAsc ; print ASCII value
bit 7,a ; was a "+" printed?
call z,Space1 ; (no, add extra space instead)
call Space3
call PrtHex ; print hexadecimal value
call Space3
call PrtDec ; print decimal value
pop hl ; recover SchStr pointer
ret
;
; PrtAsc -- Prints ASCII representation of value in A (duplicated in C),
; prefixing a "^" if a control character, suffixing a "+" if an 8-bit
; character, printing "SP" for space and "DL" for delete. Preserves
; character in A and C.
;
PrtAsc: ld c,a ; save character in C
call Space1
ld a,(OpHFlg) ; check H flag
or a
ld a,c ; recover character
jr z,PrtAs1 ; (we're ignoring high bits)
bit 7,a ; check high bit
jr nz,PrtHi ; (special handling for 8-bit character)
PrtAs1: cp 7Fh ; DEL?
jr z,PrtDel ; (yes, special handling)
cp ' ' ; space?
jr z,PrtSpc ; (yes, special handling)
jr c,PrtCtl ; (control character, special handling)
call Space1
ld a,c ; get new copy in A
call cout ; ..and print it
ret
;
; PrtCtl -- Special handling for control characters
PrtCtl: ld a,'^'
call cout ; print caret
ld a,c
add a,'@' ; make character printable
call cout ; ..and print it
ld a,c ; restore character
ret
;
; PrtSpc -- Special handling for space character
PrtSpc: call eprint ; print "SP"
db 'SP',0
ld a,c ; restore character
ret
;
; PrtDel -- Special handling for DEL character
PrtDel: call eprint ; print "DL"
db 'DL',0
ld a,c ; restore character
ret
;
; PrtHi -- Special handling for 8-bit characters
PrtHi: push af ; save character
and 7Fh ; reset high bit
ld c,a ; put copy in C
call PrtAs1 ; print it
ld a,'+'
call cout ; suffix it with a "+"
pop af ; recover character
ret
;
; PrtDec -- Prints decimal value of character in A in three-character field.
;
PrtDec: call padc
ret
;
; PrtHex -- Prints two-character hexadecimal value of character in A,
; suffixing an "h".
;
PrtHex: call pa2hc ; print value
push af ; save character
ld a,'h'
call cout ; suffix an "h"
pop af ; recover character
ret
;
; Prt32 -- Prints 32-bit number pointed to by HL. Preserves AF and BC.
;
Prt32: push af ; save character
ld a,NumSiz ; field size
call plwdc ; convert binary to ASCII string
pop af ; restore character
ret
;
; Space1, Space2, Space3 -- Prints corresponding number of spaces on
; the console, preserving all registers.
;
Space3: call Space1
Space2: call Space1
Space1: push af
ld a,' '
call cout
pop af
ret
;
; PrtMat -- retrieves character count from array of 32-bit counters,
; and prints it. Preserves A, BC, and HL.
;
PrtMat: push hl ; save HL
ld de,CCount ; point to array
ld l,a ; compute offset into array
ld h,0 ; ..using ASCII value of character
add hl,hl ; ASCII value times 2
add hl,hl ; ASCII value times 4
add hl,de ; HL -> low byte of corresponding counter
call Prt32 ; print count
pop hl ; recover HL
ret
;
; Z3Chk -- confirms Z-System by checking ENVPTR. Returns Z if not ZCPR3.
;
Z3Chk: ld a,(envptr+1) ; get high byte of pointer
or a ; zero means no Z3
ret
;
; CkScrn -- Increments and checks screen lines counter. Monitors for
; user input via CkAbrt.
;
CkScrn: call CkAbrt ; check for user input
ld a,(OpPFlg) ; check P flag
or a
call nz,crlf ; (new line if no screen paging)
ret nz ; (no paging)
ld a,(LnsCnt) ; get line counter
dec a ; ..decrement it
ld (LnsCnt),a ; ..and put it back
call nz,crlf ; (new line if screen not filled)
ret nz ; (screen not filled)
call eprint ; screen is filled, print "[more]"
db CR,LF
db '[more]',0
call cin ; ..and pause for user to press a key
call eprint ; key pressed, erase "[more]"
db CR,' ',CR,0
cp CtrlC ; was the key ^C?
jr z,Abort ; (yes, abort the program)
cp ' ' ; was the key a space?
ld a,1 ; assume it was
ld (LnsCnt),a
ret z ; (it was, so advance by one line)
ld a,(ScrLns) ; otherwise, fill the screen again
ld (LnsCnt),a
ret
;
; CkCon -- Checks for user input at the end of each line, while file is
; being read. Aborts program if ^C is received.
;
CkCon: cp LF ; line feed?
ret nz ; (not end of line)
push af ; save character
call condin ; check console
cp CtrlC ; ^C?
jr z,CkCon1 ; (yep, abort)
pop af ; recover character
ret
;
CkCon1: pop af ; adjust stack
call ClsFil ; close file
jr Abort ; ..and abort
;
; CkAbrt -- Checks for user input. If ^S, wait for another key before
; continuing. If ^C, abort the program. If anything else, ignore it.
;
CkAbrt: call condin ; check for key press
ret z ; (none, continue)
cp CtrlS ; ^S?
call z,cin ; (yes, wait for another key)
cp CtrlC ; ^C?
ret nz ; (no, continue)
call crlf ; yes, send new line and abort
;
; Abort -- abort to operating system. Set error flag according to
; configuration.
;
Abort: ld a,(AbtFlg) ; error code (if any)
call eprint
db 'Aborted',0
jp ErExit
;
; Usage -- intelligent usage screen
;
Usage: call eprint
DftNam: db 'CCOUNT Version '
db Vers/10+'0','.',Vers mod 10+'0',SubVers,CR,LF
db 'Counts characters in text files.',CR,LF
db 'Usage:',CR,LF,' ',0
ld hl,comnam
call epstr ; print invocation or default name
call eprint
db ' {dir:}infile {byte {...}} {/options}',CR,LF
db 'Options:',CR,LF
db ' A count ',0
ld a,(AllFlg) ; check configuration flag
or a
ld hl,MsgAll
jr z,Usage1
ld hl,MsgGiv
Usage1: call epstr
call eprint
db ' characters',CR,LF
db ' H high bits are ',0
ld a,(HiFlg) ; check configuration flag
or a
call nz,PrtIn
call eprint
db 'significant',CR,LF
db ' P ',0
ld a,(PagFlg) ; check configuration flag
or a
call z,PrDont
call eprint
db 'page screen',CR,LF
db 'Bytes:',CR,LF
db ' ''c lower-case character',CR,LF
db ' "c upper-case character',CR,LF
db ' ^c control character',CR,LF
db ' nn decimal ASCII value',CR,LF
db ' nnH hexadecimal ASCII value',CR,LF
db 'Hexadecimal values must begin with a digit.',0
jp Finish
;
MsgAll: db 'all',0
MsgGiv: db 'given',0
;
PrtIn: call eprint
db 'in',0
ret
;
PrDont: call eprint
db 'don''t ',0
ret
;
DSEG
;
ScrLns: ds 1 ; number of screen lines
LnsCnt: ds 1 ; screen lines counter
OpAFlg: ds 1 ; FFh=display all characters
OpHFlg: ds 1 ; 0=ignore high bits
OpPFlg: ds 1 ; 0=page screen
BegIni equ $ ; beginning of data initialization area
SchCnt: ds 1 ; number of characters we're counting
SchStr: ds 65 ; string of bytes to match and count
ChrCnt: ds 4 ; total characters found
CCount: ds 256*4 ; character count table (256 * 4 bytes)
InCtrl: ds 1 ; control table for input file
ds 7
BufAdr: ds 2
InFcb: ds 36
EndIni equ $ ; end of data initialization area
;
ds 100 ; stack
Stack: ds 2 ; stack pointer storage
;
end