; CRCZ.MAC -- CRC checksum utility for ZCPR3 ; Vers equ 11 SubVers equ ' ' ; modification level ; ; USAGE: ; ; CRCZ {dir:}{afn.aft} {{/}options} ; ; All operations target the given named directory or drive/user. If ; no DIR or DU specification is given, the current (default) drive and ; user is assumed. If no filename is given, CRCZ switches to validation ; mode. If neither directory nor filename is given, the slash is ; required before options. ; ; OPTIONS: ; ; F Send output to the file CRCKFILE.CRC on the target ; disk. This option is ignored in validation mode. ; ; L Echo output to the printer (CP/M LST device). ; ; P Toggle screen paging. The number of lines on the ; CRT screen is obtained from the environment descriptor. ; Whether CRCZ defaults to paging or not is determined ; by a configuration byte at the label "PAGING>" in the ; first sector of the COM file. Paging is always turned ; off during printer or disk file output, and when quiet ; mode is in effect. ; ; Q Toggle quiet mode. The default quiet mode state is ; determined by the configuration byte at the label ; "QUIET>" in the first sector of the COM file. The ; configuration byte is ignored if the ZCPR3 Quiet flag ; is set. This option toggles either condition. ; ; S Toggle inclusion of system files. Whether system files ; are ignored by default is determined by a configuration ; byte at the label "SYSFILES>" in the first sector of the ; COM file. This options toggles to the non-default ; state. ; ; CRCZ calculates the CRC checksums for files matching the given ambiguous ; filename, displaying them on the screen, typing them to the printer, ; and/or writing them to a file. ; ; In validation mode, CRCZ searches for the files CRCKFILE.???, CRCKLIST.???, ; CRC.???, and -CATALOG.???, reading the first such file it finds and ; comparing the listed CRC values to the CRC's of listed files on the same ; disk. ; ; The cyclic-redundancy checksum used by CRCZ is based on the CCITT ; standard polynomial: ; x^16 + x^15 + x^13 + x^7 + x^4 + x^2 + x + 1 ; It is compatible with most such CP/M programs. ; ; Options are disabled if the wheel byte is off. ; ; See the accompanying documentation file for more complete information. ; ; HISTORY: ; ; CRCZ is based on CRC, CP/M-80 Version 6.4, October 30, 1984. ; ; Version 1.0 -- May 19, 1990 -- Gene Pizzetta ; Recognizes DU and DIR specifications. Displays usage message ; with "//" option. Uses correct name from external FCB in usage ; message. Simplified output file format for CRCKFILE.CRC. When ; looking for a CRC file, finds system files, but ignores files ; with filetype COM. Looks for CRCKFILE.CRC (created by this ; program), CRCKLIST.CRC (CRC 6.4), CRC.CRC (ZCRCK), and -CATALOG ; files output by SIG/M. When calculating CRC's, always ignores ; DateStamper !!!TIME&.DAT files and temporary ($$$) files, and ; ignores system files by default. Use 'S' option to include ; system files. Use 'L' option for printer output. Use 'P' option ; to toggle paged display. Use 'Q' option to toggle suppression ; of all screen output except error messages. Uses number of ; screen lines from Environment Descriptor. In CRC checking ; (validation) mode, sets program error flag to 10 if a CRC file ; or a file listed in a CRC file is not found. A CRC mismatch sets ; high bit (adds 128) of the error flag. ; ; Version 1.1 -- June 17, 1990 -- Gene Pizzetta ; Fixed bug that caused system crash if program was aborted in ; validation mode and F option had been given (erroneously) on ; the command line. Now also sets program error flag to 10 if ; no files are found matching given file spec, to 4 for an ; invalid option, and to 2 for an invalid directory. Added security ; for remote systems at the request of Chris McEwen: all but ; paging option are disabled (and paging option isn't displayed ; in usage message), always runs in verbose mode and excludes ; system files, uses ZCPR33 parser to exclude illegal directories. ; ; System addresses ; Bdos equ 05h ; BDOS entry CpmFcb equ 5Ch ; default CP/M file control block FcbTyp equ CpmFcb+9 ; FCB filetype FcbUsr equ CpmFcb+13 ; FCB user CpmDma equ 80h ; default CP/M DMA address TPA equ 100h ; transient program area ; ; BDOS Functions ; FSrchF equ 17 ; find first match FSrchN equ 18 ; find next match CurDsk equ 25 ; get current drive ; ; Program values ; CtrlC equ 03h TAB equ 09h LF equ 0Ah CR equ 0Dh CtrlZ equ 1Ah CpmEof equ CtrlZ BufSiz equ 4000h ; size of file input and output buffers ; MACLIB Z80 ; ; external routines from Z3LIB and SYSLIB . . . ; ext logud,initfcb,codend,@fncmp,f$delete,f$rename ext f$make,f$open,f$read,f$write,f$close,setdma ext epstr,bout,condin,lout,crlf,lcrlf,pafdc ext z3init,puter2,getefcb,getquiet,getcrt,getwhl ; jmp Start ; db 'Z3ENV' db 1 Z3EAdr: dw 0FE00h ; address of environment descriptor ; ; Configuration bytes . . . ; db 'PAGING>' ; screen paging default PgFlag: db 0FFh ; 0=no paging ; db 'QUIET>' ; quiet mode default QtFlag: db 0 ; 0=verbose mode ; db 'SYSFILES>' ; system file handling default SyFlag: db 0 ; 0=exclude system files ; ; Messages . . . ; MsgSO1: db ' Target directory = ',0 MsgUse: db 'CRC-Z Version ' db Vers/10+'0','.',Vers mod 10+'0',SubVers,CR,LF db 'Usage:',CR,LF,' ',0 MsgNam: db 'CRCZ',0 MsgUs1: db ' {dir:}{afn.aft}',0 MsgUW1: db ' {{/}options}',0 MsgUs2: db CR,LF db 'All operations target the specified or default directory.',CR,LF db 'If no filename is given, searches for CRCKFILE, CRCKLIST,',CR,LF db 'CRC, and -CATALOG and switches to validation mode.',0 MsgUW2: db CR,LF db 'Options:',CR,LF db ' F Output to file CRCKFILE.CRC',CR,LF db ' L Echo to printer (LST:)',CR,LF db ' P Toggle screen paging ',0 MsgUs3: db ' Q Toggle quiet mode ',0 MsgUs4: db ' S ',0 MsgUs5: db ' system files',0 MsgUof: db 'off',CR,LF,0 MsgUon: db 'on',CR,LF,0 MsgUex: db 'Exclude',0 MsgUin: db 'Include',0 MsgSCF: db ' Searching for CRCKFILE, ',0 MsgSCL: db ' Searching for CRCKLIST, ',0 MsgZRC: db ' Searching for CRC , ',0 MsgSCC: db ' Searching for -CATALOG, ',0 MsgUFl: db 'Using file: ',0 MsgNFF: db 'No files found!',0 MsgNCF: db 'No checksum files found!',0 MsgNoF: db ' No files in checksum file found!',0 MsgFNF: db 'File not found.',0 MsgEOp: db 'File open error.',0 MsgDFu: db 'Disk full: CRCKFILE.CRC.',0 MsgNDr: db 'No directory space: CRCKFILE.CRC.',0 MsgERd: db 'Read error.',0 MsgInv: db 'Invalid option.',0 MsgIDU: db 'Illegal directory.',0 MsgECl: db 'Close error: CRCKFILE.CRC.',0 MsgMat: db ' * Match *',CR,LF,0 MsgWas: db ' <- is, was -> ',0 MsgEPr: db '[*]',0 MsgHyp: db ' - ',0 MsgMtY: db ' Matching CRC''s : ',0 MsgMtN: db ' Non-matching CRC''s : ',0 MsgPrs: db ' Lines failing parse [*] : ',0 MsgNFl: db ' Files not found : ',0 MsgAbt: db 'Aborted!',0 MsgDne: db 'Done!',0 MsgMor: db ' [more] ',0 MsgClr: db CR,' ',CR,0 OldStr: db CR,LF,'--> FILE: ' ; old filename prefix OldStC: db TAB,TAB,'CRC = ',0 ; old CRC prefix NewStr: db CR,LF ; new filename prefix NewStF: db 'XXXXXXXX.' NewStT: db 'XXX' NewStC: db TAB,'CRC = ',0 ; new CRC prefix MsgCR: db CR,LF,0 ; ; Start of program . . . ; Start: lhld Z3EAdr ; set up environment call z3init sspd OldStk ; save stack pointer lxi sp,OldStk call Init ; initialize data area lda FilFlg ; check file spec flag ora a jrz Start1 ; (none) lda CpmFcb+1 ; filename given? cpi ' ' jnz DoCRC ; (yes, calculate CRC's) Start1: xra a ; no, so we're just checking sta OpFFlg call Pcrlf call Plcrlf ; ; Search for newer CRCKFILE file ; lxi h,MsgSCF ; say we're looking for CRCKFILE call PStr lxi d,InFcb+1 lxi h,FnmCfl lxi b,11 ldir call OpenC jrz DoCfil ; (found) ; ; Search for older CRCKLIST file ; mvi a,1 ; set search flag sta SchFlg lxi h,MsgSCL ; say we're looking for CRCKLIST call PStr lxi d,InFcb+1 lxi h,FnmCls lxi b,11 ldir call OpenC jrz DoClst ; (found) ; ; Search for ZCRCK-type CRC file ; FndZrc: mvi a,1 ; set search flag sta SchFlg lxi h,MsgZrc ; say we're looking for CRC call PStr lxi d,InFcb+1 lxi h,FnmZrc lxi b,11 ldir call OpenC jz DoZrc ; (not found) ; ; Search for SIG/M-type -CATALOG file ; FndCat: mvi a,1 ; set search flag sta SchFlg lxi h,MsgSCC ; say we're looking for -CATALOG call PStr lxi d,InFcb+1 lxi h,FnmCat lxi b,11 ldir call OpenC jz DoCat ; ; No CRC file was found ; mvi a,10 ; set error code sta ErCode lxi h,MsgNCF call epstr call Acrlf jmp Abort ; ; check CRC's using CRCZ's CRCKFILE file ; DoCfil: mvi a,1 ; set CRC search flag sta CFlFlg lxi sp,OldStk ; reset stack pointer call GetStr ; load string buffer jnz ErExit ; (we have a problem) lxi h,StrBuf lxi b,0 call PrsFn ; parse filename jrnz DoCfl2 ; (parsing problem) lxi d,NewStC mvi c,19 call ParsLn ; parse CRC lead-in jrnz DoCfl2 ; (parsing problem) call GetCrc jrnz DoCfl2 ; (parsing problem) lxi h,StrBuf call ChkMat jr DoCfil DoCfl2: call PrsErr ; say we have a line parsing problem jr DoCfil ; ; check CRC's using CRC 6.4's CRCKLIST file ; DoClst: mvi a,1 ; set CRC search flag sta CFlFlg lxi sp,OldStk ; reset stack pointer call GetStr ; load string buffer jnz ErExit ; (we have a problem) lxi d,OldStr+2 lxi h,StrBuf lxi b,11 call ParsLn ; parse filename lead-in jrnz DoCls2 ; (parsing problem) call PrsFn ; parse filename jrnz DoCls2 ; (parsing problem) lxi d,OldStC mvi c,31 call ParsLn ; parse CRC lead-in jrnz DoCls2 ; (parsing problem) call GetCrc jrnz DoCls2 ; (parsing problem) lxi h,StrBuf+11 call ChkMat jr DoClst DoCls2: call PrsErr ; say we have a line parsing problem jr DoClst ; ; Check CRC's using CRC file ; DoZrc: mvi a,1 ; set CRC search flag sta CFlFlg lxi sp,OldStk ; reset stack pointer call GetStr ; get line from file jnz ErExit mvi b,0 lxi h,StrBuf call SkpDU ; skip drive/user number jrnz DoZrc1 ; (parsing problem) mvi a,':' ; a period? cmp m jrnz DoZrc1 ; (no, parsing problem) inx h inr b call PrsFn ; parse filename jrnz DoZrc1 ; (parsing problem) call SkpTSp ; skip white space jrnz DoZrc2 ; (parsing problem) call GetCrc ; get CRC jrnz DoZrc2 ; (parsing problem) lhld LinPtr ; get line buffer pointer call ChkMat ; ..and check CRC jr DoZrc ; DoZrc1: lda NoFFlg ora a jrnz DoZrc DoZrc2: call PrsErr jr DoZrc ; ; Check CRC's using -CATALOG file ; DoCat: mvi a,1 ; set CRC search flag sta CFlFlg lxi sp,OldStk ; reset stack pointer call GetStr ; get line from file jnz ErExit mvi b,0 lxi h,StrBuf call SkpNbr ; skip disk number jrnz DoCat2 ; (parsing problem) mvi a,'.' ; a period? cmp m jrnz DoCat2 ; (no, parsing problem) inx h inr b call SkpNb1 ; skip file number jrnz DoCat2 ; (parsing problem) call SkpTSp ; skip white space jrnz DoCat2 ; (parsing problem) call PrsFn ; parse filename jrnz DoCat2 ; (parsing problem) call SkpTSp ; skip white space jrnz DoCat3 ; (parsing problem) call SkpNb2 ; skip file size jrnz DoCat3 ; (parsing problem) DoCat1: call SkpNb2 ; skip file size jrz DoCat1 cpi 'K' ; do have a K jrnz DoCat3 ; (no, parsing problem) call SkpTSp ; skip white space jrnz DoCat3 ; (parsing problem) call GetCrc ; get CRC jrnz DoCat3 ; (parsing problem) lhld LinPtr ; get line buffer pointer call ChkMat ; ..and check CRC jr DoCat ; DoCat2: lda NoFFlg ora a jrnz DoCat DoCat3: call PrsErr jr DoCat ; ; Filename was given, so get CRC's of matching files ; DoCRC: mvi a,1 ; set CRC search flag sta CFlFlg lda OpFFlg ; check for F option ora a jz DisCRC ; (no, display only) jmp FilCRC ; yes, output to file ; ; Do file output to CRCKFILE.CRC ; FilCRC: lxi h,BufSiz shld OutSiz lxi h,00h shld OutPtr lxi d,OutFcb call initfcb inx d lxi h,FnmTmp lxi b,11 ldir lxi d,OutFcb call f$delete ; erase old CRCKFILE.$$$ file call f$make ; ..and create a new one inr a jnz DisCRC call Pcrlf call Plcrlf lxi h,MsgNDr ; say directory is full jmp RdEr1 ; ; Display CRC's at console ; DisCRC: lxi sp,OldStk xra a sta ChkFlg call GetFil jnc PutFn lda SchFlg ora a jz DisCR1 mvi a,10 sta ErCode call Pcrlf lxi h,MsgNFF call epstr jmp ErExit DisCR1: lda OpFFlg ora a jz Done DisCR2: lhld OutPtr mov a,l ani 7Fh jnz DisCR3 shld OutSiz DisCR3: mvi a,CpmEof push psw call WrFile pop psw jnz DisCR2 lxi d,OutFcb call f$close ; close CRCKFILE.$$$ file inr a jnz DisCR4 call Pcrlf lxi h,MsgECl ; say we have a close error call epstr DisCR4: lxi d,FnmCrc call f$delete ; erase old CRCKFILE.CRC file lxi h,OutFcb ; point to CRCKFILE.$$$ xchg ; exchange pointers call f$rename ; rename CRCKFILE.$$$ to CRCKFILE.CRC ; Done: call Pcrlf Done1: lxi h,MsgDne call PStr jmp Finish ; ; Put filename into string, print string, and call open ; PutFn: lxi h,FcbTyp call ChkTmp jz DisCRC lxi h,CpmFcb+1 lxi d,NewStF lxi b,8 ldir lxi h,FcbTyp lxi d,NewStT lxi b,3 ldir lxi d,NewStr call PrtNSt call OpenFl jz DisCRC jmp ErExit ; ; Display usage message ; Usage: xra a ; reset L option sta OpLFlg lxi h,MsgUse ; print usage message call epstr call ComNam ; print our name lxi h,MsgUs1 call epstr call getwhl ; check wheel byte jrz Usage2 ; (not wheel) lxi h,MsgUW1 ; we have options call epstr Usage2: lxi h,MsgUs2 call epstr call getwhl ; check wheel again jz Exit ; (not wheel, so end here) lxi h,MsgUW2 call epstr lda OpPFlg ; check paging mode ora a jrz Usage3 ; (it's off) lxi h,MsgUof jr Usage4 Usage3: lxi h,MsgUon Usage4: call epstr lxi h,MsgUs3 call epstr lda OpQFlg ; check quiet mode ora a jrz Usage5 ; (it's off) lxi h,MsgUof jmp Usage6 Usage5: lxi h,MsgUon Usage6: call epstr lxi h,MsgUs4 call epstr lda OpSFlg ; check S flag ora a jrz Usage7 ; (it's off) lxi h,MsgUex jr Usage8 Usage7: lxi h,MsgUin Usage8: call epstr lxi h,MsgUs5 call epstr jmp Exit ; ; ComNam -- print actual name under which this program was called if ; available from external file control block. Otherwise, use "CRCZ". ; ComNam: call getefcb jrz ComNm2 ; (no external fcb) mvi b,8 ComNm1: inx h mov a,m ani 7Fh cpi ' ' cnz bout djnz ComNm1 ret ; ComNm2: lxi h,MsgNam ; print default name call epstr ret ; ; Exit routines ; ErExit: call Acrlf lda OpFFlg ora a jrz Abort ErEx1: lhld OutPtr mov a,l ani 7Fh jnz ErEx2 shld OutSiz ErEx2: mvi a,CpmEof push psw call WrFile pop psw jnz ErEx1 lxi d,OutFcb call f$close ; close the output file inr a jnz ErEx3 call Pcrlf lxi h,MsgECl ; say we have a file close error call epstr ErEx3: lxi d,OutFcb call f$delete ; erase the output file Abort: lxi h,MsgAbt ; say we're aborting call epstr Finish: lda ChkFlg ora a jz Exit xra a sta OpFFlg call Pcrlf call Plcrlf lxi d,MsgMtY ; say how many CRC's match call PrtNSt lda MtYCnt call PrtInt lda MtNCnt ora a jrz Finis1 call Pcrlf call Plcrlf lxi d,MsgMtN ; say how many CRC's didn't match call PrtNSt lda MtNCnt call PrtInt lda ErCode ori 80h sta ErCode Finis1: lda PrsCnt ora a jrz Finis2 call Pcrlf call Plcrlf lxi d,MsgPrs ; say how many lines didn't parse call PrtNSt lda PrsCnt call PrtInt Finis2: lda NFlCnt ora a jrz Exit call Pcrlf call Plcrlf lxi d,MsgNFl ; say how many files not found call PrtNSt lda NFlCnt call PrtInt ; Exit: call Plcrlf ; send final CRLF to printer? lda ErCode ; set error code call puter2 lhld OldStk ; reset stack sphl ret ; ; Subroutines . . . ; ; RdFile -- read from file into InBuf and reset pointers. ; RdFile: lhld InSiz ; get input buffer size xchg ; put in DE lhld InPtr ; get input buffer pointer mov a,l ; is it full? sub e mov a,h sbb d jrc RdFil4 ; (no) lxi h,00h ; yes, reset pointer shld InPtr RdFil1: xchg ; put pointer in DE lhld InSiz ; get size mov a,e ; get number of bytes in buffer sub l mov a,d sbb h jrnc RdFil3 ; (it's full) lhld InBuf ; add bytes to buffer address dad d call setdma ; set DMA address lxi d,InFcb ; read file call f$read ora a jrnz RdFil2 lxi d,CpmDma lhld InPtr dad d shld InPtr jr RdFil1 RdFil2: lhld InPtr shld InSiz RdFil3: lxi h,CpmDma ; set DMA address call setdma lxi h,00h ; reset pointer shld InPtr RdFil4: xchg lhld InBuf dad d xchg lhld InSiz mov a,l ora h mvi a,CpmEof rz ldax d lhld InPtr inx h shld InPtr ret ; ; OpenC -- open CRC file, if it exists. ; OpenC: lxi h,BufSiz shld InSiz shld InPtr lxi h,InFcb+1 lxi d,CpmFcb+1 lxi b,11 ldir call GetFil jrc NoCrcF lxi h,CpmFcb+1 lxi d,InFcb+1 lxi b,11 ldir lxi d,InFcb call initfcb call f$open inr a jrnz GotFil NoCrcF: lxi h,MsgFNF ; say we can't find CRCK file call PStr call Pcrlf xra a inr a ret ; GotFil: lxi h,InFcb+1 lxi d,StrBuf lxi b,8 ldir mvi a,'.' stax d inx d lxi b,3 ldir xra a stax d lxi d,MsgUFl call PrtNSt lxi h,StrBuf GotFl1: mov a,m ora a jrz GotFl2 call PrtChr inx h jr GotFl1 GotFl2: push psw call Pcrlf call Plcrlf pop psw ret ; ; GetStr -- get string from CRC file into string buffer. ; GetStr: lxi h,StrBuf ; point to string buffer mvi a,0 sta StrPtr GetSt1: push h call RdFile pop h cpi CpmEof ; end of file? jrnz GetSt2 ; (no) lda StrPtr ora a jrnz GetSt4 lda NoFFlg ora a jz Done1 lxi h,MsgNoF call epstr mvi a,CpmEof ora a ret ; GetSt2: ani 7Fh cpi CR ; carriage return? jrz GetSt3 ; (yes) cpi LF ; linefeed? jrz GetSt3 ; (yes) mov m,a inx h lda StrPtr inr a sta StrPtr cpi 80 ; buffer filled? jrc GetSt1 ; (no) GetSt3: lda StrPtr ora a jrz GetStr GetSt4: mvi m,0 xra a ret ; ; PrsErr -- error parsing line in CRC file. ; PrsErr: lxi d,MsgEPr ; say we can't parse line call PrtNSt lxi h,StrBuf ; print line PrsEr1: mov a,m ora a jrz PrsEr2 call PrtChr inx h jr PrsEr1 PrsEr2: call Pcrlf call Plcrlf lxi h,PrsCnt ; increment parse error count inr m ret ; ; ParsLn -- find filename and CRC in line from CRC file ; ParsLn: ldax d ; match lead-in cmp m rnz ; (no match) inx h inx d inr b mov a,b cmp c rz ; (match) jr ParsLn ; loop ; ; ChkMat -- check file for CRC match to CRC file. ; ChkMat: push h lxi d,CpmFcb+1 lxi b,8 ldir inx h lxi b,3 lxi d,FcbTyp ldir xra a mov m,a pop h ChkMa1: mov a,m ora a jz ChkMa2 call PrtChr inx h jmp ChkMa1 ChkMa2: mvi a,1 sta SchFlg lxi d,MsgHyp call PrtNSt call GetFil jnc ChkMa3 mvi a,10 sta ErCode lxi d,MsgFNF call PrtNSt lxi d,MsgCR call PrtNSt lxi h,NFlCnt inr m xra a inr a ret ; ChkMa3: mvi a,1 sta ChkFlg call OpenFl rnz lda OldCrc+1 mov b,a lda NewCrc cmp b jnz ChkMa4 lda OldCrc mov b,a lda NewCrc+1 cmp b jnz ChkMa4 lxi d,MsgMat ; say we have a match call PrtNSt xra a sta NoFFlg lxi h,MtYCnt inr m xra a ret ; ChkMa4: lxi d,MsgWas ; say we don't match call PrtNSt lda OldCrc call PrtHex mvi a,' ' call PrtChr lda OldCrc+1 call PrtHex call Pcrlf call Plcrlf xra a sta NoFFlg lxi h,MtNCnt inr m xra a inr a ret ; ; GetCrc -- get CRC from CRC file. ; GetCrc: call CvtCrc ; get first CRC byte rnz ; (error) sta OldCrc ; store it mov a,m ; do we have a space? inx h cpi ' ' jz GetCr1 ; (yes, continue) dcx h ; no, back up pointer GetCr1: call CvtCrc ; get second CRC byte rnz ; error sta OldCrc+1 ; store it mov b,a xra a ret ; ; PrsFn -- parse filename in CRC file. ; PrsFn: shld LinPtr ; save line buffer pointer call GetCFn rnz mvi c,7 PrsFn1: call GetCFn jz PrsFn2 cpi ' ' rnz PrsFn2: dcr c jnz PrsFn1 mov a,m inx h inr b cpi '.' rnz mvi c,3 PrsFn3: call GetCFn jz PrsFn4 cpi ' ' rnz PrsFn4: dcr c jnz PrsFn3 ret ; ; SkpTSp -- skip tabs and spaces. ; SkpTSp: mov a,m cpi ' ' jz SkpTS1 cpi TAB rnz SkpTS1: inx h inr b mov a,m cpi ' ' jz SkpTS1 cpi TAB jz SkpTS1 cmp a ret ; ; SkpNbr -- skip file numbers in -CATALOG file. ; SkpNbr: call SkpNb1 rnz call SkpNb2 rz cpi '.' rnz dcx h dcr b cmp a ret ; SkpNb1: call SkpNb2 rnz SkpNb2: mov a,m inx h inr b cpi '0' jc SkpNb3 cpi ':' jnc SkpNb3 cmp a ret SkpNb3: cpi '0' ret ; ; SkpDU -- skip drive/user number in CRC.CRC file. ; SkpDU: call SkpCap rnz call SkpNb1 rz cpi ':' rnz dcx h dcr b cmp a ret ; ; SkpCap -- skip upper-case character. ; SkpCap: mov a,m inx h inr b cpi 'A' jc SkpCa1 cpi '[' jnc SkpCa1 cmp a ret ; SkpCa1: cpi 'A' ret ; ; GetCFn -- get CRC file filename. ; GetCFn: mov a,m inx h inr b cpi '!' jrc GetCF1 cpi 7Fh jrnc GetCF1 cmp a ret ; GetCF1: cpi 'A' ret ; ; CvtCrc -- convert CRC number from CRC file. ; CvtCrc: call CvtCr1 ; get first character rnz ; (error) rlc rlc rlc rlc mov c,a ; store it in C call CvtCr1 ; get second character rnz ; (error) mov b,a ; store it in B mov a,c ora b cmp a ret ; CvtCr1: mov a,m inx h inr b cpi '0' jrc CvtCr3 sui '0' cpi 10 jrc CvtCr2 ani 1Fh sui 7 cpi 10 jrc CvtCr3 cpi 16 jrnc CvtCr3 CvtCr2: cmp a ret ; CvtCr3: ora a ret ; ; WrFile -- write to CRC file. ; WrFile: push psw lhld OutSiz xchg lhld OutPtr mov a,l sub e mov a,h sbb d jc WrFil4 lxi h,00h shld OutPtr WrFil1: xchg lhld OutSiz mov a,e sub l mov a,d sbb h jnc WrFil3 lhld OutBuf dad d call setdma ; set DMA address lxi d,OutFcb call f$write ora a jnz WrFil2 lxi d,CpmDma lhld OutPtr dad d shld OutPtr jmp WrFil1 ; WrFil2: call Pcrlf call Plcrlf lxi h,MsgDFu ; say disk is full pop psw jmp RdEr1 ; WrFil3: lxi h,CpmDma ; set DMA address call setdma lxi h,00h shld OutPtr WrFil4: xchg lhld OutBuf dad d xchg pop psw stax d lhld OutPtr inx h shld OutPtr ret ; ; OpenFl -- open file for CRC checking. ; OpenFl: lxi d,CpmFcb ; open file call f$open inr a jnz ChkCrc lxi d,MsgEOp call PrtNSt xra a inr a ret ; ; ChkCrc -- check CRC and check for console abort. ; ChkCrc: lxi h,00h shld NewCrc lxi h,100h shld DmaPtr ChkCr1: lhld DmaPtr mov a,h cpi 0 jrz ChkCr3 call condin ; check for key jrz ChkCr2 ; (none) cpi CtrlC ; ^C? jz ErExit ChkCr2: lxi d,CpmFcb call f$read ora a jrnz ChkCr4 lxi h,CpmDma ChkCr3: mov a,m sta CurChr inx h shld DmaPtr call AddCrc jr ChkCr1 ; ChkCr4: cpi 1 jrz ChkCr5 call ClosFl jmp RdErr ; ChkCr5: lda NewCrc+1 call PrtHex mvi a,' ' call PrtChr lda NewCrc call PrtHex call ClosFl xra a ret ; ; ClosFl -- close file. ; ClosFl: lxi d,CpmFcb ; close file call f$close ret ; ; RdErr -- error reading file. ; RdErr: lxi h,MsgERd ; say we have file read error RdEr1: call epstr call Acrlf xra a inr a ret ; ; AddCrc -- add current character to current CRC calculation. ; AddCrc: lhld NewCrc mov a,h ani 80h push psw dad h lda CurChr add l mov l,a pop psw jrz AddCr1 mov a,h xri 0A0h mov h,a mov a,l xri 97h mov l,a AddCr1: shld NewCrc ret ; ; PrtHex -- print byte in A as two hexadecimal characters. ; PrtHex: push psw rar rar rar rar call PrtHx1 pop psw PrtHx1: ani 0Fh cpi LF jrc PrtHx2 adi 7 PrtHx2: adi '0' jmp PrtChr ; ; PrtNSt -- print text pointed to by DE until a null in found. ; PrtNSt: push h xchg PrtNS1: mov a,m call PrtChr inx h mov a,m ora a jnz PrtNS1 pop h ret ; ; PrtChr -- print character on console unless quiet mode. Also call ; WrtChr for file output and LstChr for printer output. ; PrtChr: push b push d push h ani 7Fh mov e,a push d call WrtChr ; file output? pop d call LstChr ; printer output? lda OpQFlg ; quiet mode? ora a mov a,e cz bout ; (if not, print character) cpi LF ; do we have a linefeed? cz PagChk ; (yes, check line count) pop h pop d pop b ret ; ; WrtChr -- write character to file if F option. ; WrtChr: lda OpFFlg ora a rz mov a,e call WrFile ret ; ; LstChr -- send character to printer if L option. ; LstChr: lda OpLFlg ora a rz mov a,e call lout ret ; ; PStr -- print to console unless quiet mode. ; PStr: lda OpQFlg ; check quiet flag ora a rnz ; (no message) call epstr ret ; ; Pcrlf -- print carriage return and linefeed to console unless quiet mode. ; Pcrlf: lda OpQFlg ; check quiet flag ora a rnz ; (no crlf) Acrlf: call crlf call PagChk ; check line count ret ; ; Plcrlf -- print carriage return and linefeed to printer if L option. ; Plcrlf: lda OpLFlg ; check option L flag ora a rz ; (no crlf) call lcrlf ret ; ; PagChk -- add to line count and pause if appropriate. ; PagChk: mov e,a lda OpPFlg ora a mov a,e rz lda LinCnt inr a ; increment line count sta LinCnt mov d,a ; move line count to D lda CrtLns ; get number of screen lines cmp d rnz lxi h,MsgMor call epstr PagCh1: call condin jrz PagCh1 cpi CtrlC jz ErExit lxi h,MsgClr call epstr mvi a,0 sta LinCnt mov a,e ret ; ; GetFil -- find match for ambiguous filename. ; GetFil: lxi h,CpmDma ; set DMA address to default buffer call setdma lda SchFlg ; is this our first search? ora a jrz GetFl1 ; (no) lxi h,CpmFcb+1 ; yes, move file mask to storage lxi d,FnMask lxi b,11 ldir lxi h,FnMask ; move file mask to FCB lxi d,CpmFcb+1 lxi b,11 ldir lxi d,CpmFcb call initfcb mvi c,FSrchF ; get first matching file call Bdos jr GetFl2 ; GetFl1: lxi h,FilNam ; move previous filename to FCB lxi d,CpmFcb+1 lxi b,11 ldir lxi d,CpmFcb call initfcb mvi c,FSrchF ; get first matching file (ignore it) call Bdos lxi h,FnMask ; move filename mask to FCB lxi d,CpmFcb+1 lxi b,11 ldir lxi d,CpmFcb call initfcb mvi c,FSrchN ; get next matching file call Bdos GetFl2: inr a stc rz dcr a ani 3 add a add a add a add a add a adi 81h mov l,a mvi h,0 push h lxi d,FilNam ; move filename to storage lxi b,11 ldir pop h lxi d,CpmFcb+1 ; move filename to FCB lxi b,11 ldir xra a sta SchFlg ; reset search flag call NamTst ; check for exclusions jrnz GetFl1 ; (yes, excluded) xra a ret ; ; NamTst -- check filename to see if it matches any exclusions. ; Return NZ if file is excluded. ; NamTst: lda CFlFlg ; searching for a CRC file? ora a jrz ComTst ; (yes) lda OpSFlg ; S option? ora a jrnz NamTs1 ; (yes, continue checking) lda CpmFcb+10 ; check system attribute ani 80h jrnz SetNZ ; (it's set, so skip it) ; NamTs1: lxi h,CpmFcb+1 lxi d,FnmTim ; is it !!!TIME&.DAT? mvi b,11 call @fncmp jrz SetNZ ; (yes, skip it) jr SetZ ; ComTst: lxi h,CpmFcb+9 ; check filetype for COM lxi d,FnmCom mvi b,3 call @fncmp jrz SetNZ ; (yes, skip it) ; SetZ: xra a ; returns zero flag set ret ; SetNZ: xra a ; returns zero flag reset inr a ; ..(exclude file) ret ; ; ChkTmp -- check for temporary file (filetype $$$). ; ChkTmp: call ChkTm1 rnz call ChkTm1 rnz ChkTm1: mov a,m ani 7Fh cpi '$' inx h ret ; ; PrtInt -- print byte in A. ; PrtInt: mvi e,0 mvi b,100 call PrtIn1 mvi b,10 call PrtIn1 adi '0' jmp PrtChr ; PrtIn1: mvi c,0FFh PrtIn2: inr c sub b jnc PrtIn2 add b mov d,a mov a,c ora e jrz PrtIn3 ori '0' call PrtChr mvi e,'0' jr PrtIn4 PrtIn3: mvi a,' ' call PrtChr PrtIn4: mov a,d ret ; ; Init -- Initialize data areas, set drive/user area, check for options. ; Init: xra a ; initialize data areas to zero mvi b,InBuf-CurChr ; area to fill lxi h,CurChr Init1: mov m,a inx h djnz Init1 sta StrPtr mvi a,1 sta SchFlg sta NoFFlg call codend shld InBuf ; set input buffer address lxi d,BufSiz dad d shld OutBuf ; ..and output buffer address call getquiet ; is ZCPR quiet flag set? jrnz SetQOp ; (yes) lda QtFlag ; no, get quiet configuration byte SetQOp: sta OpQFlg ; ..and store in Q option lda PgFlag ; get screen paging flag sta OpPFlg ; ..and store it lda SyFlag ; get system files flag sta OpSFlg ; ..and store it call getcrt ; get number of lines on console inx h mov a,m dcr a ; subtract 1 and store it sta CrtLns ; lxi h,CpmDma ; do we have a tail? mov a,m ora a jz Init4 ; (no) Init2a: inx h ; point to beginning of tail call EatSpc ; skip spaces mov b,a ; save character in b cpi '/' ; do we have options? jrnz Init2b ; (no) jr Init2d ; yes, go get them ; Init2b: mvi a,1 ; we have a file spec sta FilFlg ; ..so set flag mov a,b ; get back character Init2c: inx h ; increment pointer cpi 0 ; ..and skip past first token jz Init4 ; (no more tail) cpi ' ' ; space? mov a,m jrnz Init2c ; (no, loop) call EatSpc ; skip spaces cpi 0 jrz Init4 cpi '/' ; a slash? jrnz Init2e ; (no) Init2d: inx h ; yes, skip it Init2e: mov a,m ; get options inx h cpi 0 jrz Init4 ; end of tail cpi '/' ; another slash? jz Usage ; (yes, it's a help request) cpi 'F' ; option F? jrz OptF ; (yes, set flag) cpi 'L' ; option L? jrz OptL ; (yes) cpi 'P' ; option P? jrz OptP ; (yes) cpi 'Q' ; option Q? jrz OptQ ; (yes) cpi 'S' ; option S? jrz OptS ; (yes) cpi ' ' ; skip spaces jrz Init2e call Pcrlf xra a sta OpLFlg lxi h,MsgInv ; say it's invalid call epstr mvi a,4 sta ErCode jmp Exit ; OptF: sta OpFFlg ; set flag jr Init2e ; OptL: sta OpLFlg ; set flag jr Init2e ; OptP: lda OpPFlg ; get P flag ora a jrz OptP2 ; (not set) sub a ; zero it jr OptP3 OptP2: mvi a,'P' ; set flag OptP3: sta OpPFlg ; store it and return jr Init2e ; OptQ: lda OpQFlg ; get Q flag ora a jrz OptQ2 ; (not set) sub a ; zero it jr OptQ3 OptQ2: mvi a,'Q' ; set flag OptQ3: sta OpQFlg ; store it and return jr Init2e ; OptS: lda OpSFlg ; get S flag ora a jrz OptS2 ; (not set) sub a ; zero it jr OptS3 OptS2: mvi a,'S' ; set flag OptS3: sta OpSFlg ; store it and return jr Init2e ; Init4: lda OpQFlg ; check quiet flag ora a cnz PZero ; (yes, cancel P option) lda OpFFlg ; check file output ora a cnz PZero ; (yes, cancel P option) lda OpLFlg ; check printer output ora a cnz PZero ; (yes, cancel L option) lda CpmFcb ; get target drive ora a jrz Init5 ; (no, we don't have one) dcr a jr Init6 Init5: mvi c,CurDsk ; no drive, so get default call Bdos Init6: sta Drive mov b,a lda CpmFcb+13 ; get targer user area sta User ; ..and store it mov c,a lda CpmFcb+15 ; check for directory error ora a jz Init7 ; (we're okay) lxi h,MsgIDU ; we've got an illegal directory call epstr mvi a,2 sta ErCode jmp Exit Init7: call getwhl ; check wheel byte jrnz Init8 ; (it's on, so skip restrictions) ; ; Non-wheels come here so we can impose restrictions ; xra a ; reset options (except paging) sta OpFFlg ; no file output sta OpLFlg ; no printer output sta OpQFlg ; no quiet mode sta OpSFlg ; no system files ; Init8: call logud ; ..and log in lda OpQFlg ; check quiet flag ora a rnz ; (skip sign-on) lxi h,MsgSO1 ; print sign-on call epstr mov a,b ; print drive adi 'A' ; make printable call bout mov a,c ; print user call pafdc mvi a,':' ; print colon call bout ret ; PZero: xra a ; zero P option sta OpPFlg ret ; ; EatSpc -- gobbles up spaces. ; EatSpc: mov a,m cpi ' ' inx h jrz EatSpc dcx h ret ; ; data . . . ; FnmCom: db 'COM' FnmTim: db '!!!TIME&DAT' FnmCfl: db 'CRCKFILE???' FnmCls: db 'CRCKLIST???' FnmZrc: db 'CRC ???' FnmCat: db '-CATALOG???' FnmTmp: db 'CRCKFILE$$$' FnmCrc: db 0,'CRCKFILECRC',0,0,0,0 ; ; Uninitialized data . . . ; DSEG ; CurChr: ds 1 ; current character for CRC calculation FilFlg: ds 1 ; file spec flag (0=no file given) CFlFlg: ds 1 ; CRC search flag (0 = looking for CRC file) SchFlg: ds 1 ; search flag (0=subsequent search) OpFFlg: ds 1 ; option F flag (F=file output) OpLFlg: ds 1 ; option L flag (L=list output) OpPFlg: ds 1 ; option P flag (0=no screen paging) OpQFlg: ds 1 ; option Q flag (0=verbose, non-zero=quiet) OpSFlg: ds 1 ; option S flag (S=include system files) ChkFlg: ds 1 ; CRC validation mode flag NoFFlg: ds 1 ; no files found flag (0=files found) LinCnt: ds 1 ; temporary line count ErCode: ds 1 ; error code passed to ZCPR3 MtYCnt: ds 1 ; count of file CRC matches MtNCnt: ds 1 ; count of file CRC non-matches PrsCnt: ds 1 ; count of line parse failures NFlCnt: ds 1 ; count of files not found Drive: ds 1 ; target drive User: ds 1 ; target user CrtLns: ds 1 ; number of lines on console NewCrc: ds 2 ; New CRC from this run OldCrc: ds 2 ; CRC from CRC file LinPtr: ds 2 ; line buffer pointer DmaPtr: ds 2 ; file DMA pointer InPtr: ds 2 ; input buffer pointer OutPtr: ds 2 ; output buffer pointer InBuf: ds 2 ; input buffer address OutBuf: ds 2 ; output buffer address InSiz: ds 2 ; input buffer size OutSiz: ds 2 ; output buffer size ; InFcb: ds 36 ; input file control block OutFcb: ds 36 ; output file control block (CRCKFILE.CRC) ; FnMask: ds 11 ; filename mask from command line FilNam: ds 11 ; filename from CRC file StrPtr: ds 1 ; string buffer pointer StrBuf: ds 80 ; string buffer ds 96 ; stack OldStk: ds 2 ; storage for old stack pointer ; END