IMD 1.16: 1/06/2007 13:22:08 FOGCPM.192 --FOGCPM192FIRE12 COMFIRE12 DOCB FIRE12 Z80FIRE12 Z80 !"#$%&'()*+,-./FIRE12 Z800123456789:;<=>?FIRE12 Z80O@ABCDEFGHI-06-12 88 -CPM192 DOCFIRE12 NOT JKDMAP COM LMDMAP DOCNRESTORE BUGOPRESTORE DOCQRSTUVWXYZ[\]^_`RESTORE DOC abSAPP20 COMcdSAPP20 DOC7efghijkSAPP20 Z80lmnopqrstuvwxyz{SAPP20 Z80{|}~ARCOPY COMARCOPY DOCARCOPY DOCThis is the disk name. WY[]_a Oogikmoq s@u`wy{} @`  Z80 needed  }2Y "| FiRe v12 08/10/87 GFR:(* Change disk - any key continues u ! w S 8 "ZK"6w 1:2S 8 ! "I !"T :\=2U Drive: A2!_p  O :=*6&o)))[ARE:<G!))[6S 8[*Rl&-)))"-:=( { *A#R"  > Z*A# " [6" :=W:<G!)" )R Insufficient buffers room Unsorted - use SAPP or equivilent first [ * "  Calculating...[6! K ͞  x :@(x22 : į*4"* " *6": (6  Relocating: :!_p *~͎ :p  *" :2* ͸ * #" * K͸ ʵ>2* #" T " |(: ([ * "S* #" * ^:@(#VS: (  * "S* #" (!: ([2 * "S* #" : ([2 * "S* #" ([* s:@(#r[* s:@(#r*#"* #:@(#" :=2:IJ(2* +|(Z" * | Restore aborted, *  entr| }=  y ies left : › Statistics Active dir entries: *  Total groups allocated: *  Group swaps needed: *  Group reads needed: *  Group writes needed: *  Directory writes needed: * :(8 Press 'Y' to continue, else aborts: u :<2  Press ^C to abort... f Done * #" : >22*6"S*G"%!"':2:=2 ɯ*S"*%"*'">22:=<2KSl [%{ ['͟ :  :O- ['*:R0[%*+R0S%S'*S"S:=2 :=2("*"S*"%*"'2*R CRC error abort ' [ *~0 #z V .:C:D}&0,f k k *?R { ͟ bk:<G)& <2 *: 6 : =2 6 [:*G #* #"#*6~(~_#~( :_#͸ (  *#+"#| |:@(+<~O8 #7:@Wy(#x}/o|/g#R8{ 7R8  `iBKx ! T]x( ~#(_p S 8SOCM> > Z |^#V+"8! ~#fo:[? S+*)|2"S 8CS> Z *+R S%BKS 8SX> Z S'[G*%R`i0 :" + [8 S 8c >Z :; g")DMS 8}2V> Z  Track ErrorS 8f > Z  Read ErrorS 8r >Z  Write Error:Y 0*o] 2 _p dͦ  ͦ 0́ 8 _y ({y0́ {'  d  }0́ R8 Zy y0Á  >.́  ́ !"O*yO}o|g0|g}!o "0 0 *i`>2m<2n 2my2n˜ :=<2d:U2e:X2f:V2h:d =2d:U!e :X :V!h 4~!: *f#"f2h2l 2d<2l2k:IG:V*Z  )2a"i!b~69 :U!\2 :X!]2 :a!_V :cđ :U2\*X"]:a2_:lĉ 2c[S*i:mj >2c:n… :k… 2c͑ :k͝ > Ö ͝ > 2k*]"M> :_o&"M*8"O> }2`"M> *Z"M> >2L> 2K2K 'FiRe - File Restoral Utility Program' FIRE v12 by George F Reding 08/10/87 Utility program to restore disk files to like-new condition, where the file allocations for each file are in a sequential order rather than being scattered all over the disk (such as after a file/program has been modified). Usable on any CP/M 2.2 or 3.0 system which has a Z80 CPU. Can be used regardless of TPA memory and/or if the system directory max- imum capacity is too large. Version 10 clobbered the DE registerd in the XBIOS2 routine which would make the sectran for CP/M 2.2 to not work properly. ------------------------------------------------------------------------ Created from: RESTORE v12 by: Steve Dirickson 21145 Raintree Place NW Poulsbo, WA 98370-9726 Other credits: CPM22E by Mike Griswold SYSLIB by Richard Conn ------------------------------------------------------------------------ CONFIGURATION There's very little (if any) configuration required in order to use this program. The following bytes may be changed with DDT, SID, or SZAP: 103 byte Disk change wait. set non-zero for no waiting. 104 byte File relocation linefeed character. set 0Ah for scrolling of file names, 0 for no scrolling (updates 1 line), as each file is worked on. 105 word For CP/M 3.0 only. value of your largest sector size. (Morrow hard-disk systems is 1024) ------------------------------------------------------------------------ NOTES FROM THE SOURCE CODE DPB offset and table lengths for CP/M 2.2 are different from those for CP/M 3.3. They are now automatically set by the program, and the BIOS access is performed differently, eliminating the need to copy BIOS vec- tors into the program. Much 8080 code is changed to Z80 which conserves memory space and increases the speed of the program. You may use the program even if available (TPA) memory falls short of the capability to handle your system maximum directory entry capacity (BSH, BLM, DRM, and AL0 and AL1 values are all factors). Either your TPA may be too low, or your system directory capacity (DRM) is too large (eg., 2048 or more? maximum entries capacity). If the situation does arise, a message is given of the MAXIMUM ACTIVE entries that you may have to be able to con- tinue, and you may abort if you desire not to continue. The user should ensure that there are LESS active directory entries by using DU, after using SAP. >> WARNING << To CONTINUE with MORE entries ACTIVE than the figure shown WILL TRASH both files and directory. Because of the memory limitation and differences among among systems, no warranties are expressed or implied and a user of this program shall assume any and all re- sponsibility for results arising from usage of the program. adjust maximum directory number (DRM) to a smaller/fake value for cases where the directory has a capacity to hold 2048 (or more??) entries, or in cases where the tpa may fall short of of capability to handle all of the directory entries. Adjust the directory group count (AL0 and AL1) which varies with BLM (block size BLS) so that it matches with the smal- ler/fake DRM value. In order to use the program in such cases of too many directory entries and/or of too little tpa space the TOTAL of ACTIVE directory entries will have to be verified by the user with "DU" or some equivilent program (after using "CLEANDIR", "SAP", "SAPP", etc). A warning message and the maximum number of active entries that the pro- gram can handle is given, with opportunity for the user to abort. Faking of the DRM value is similar to that which "SAPP" (for CP/M 3.0) is capa- ble of (an assembly time option). The way I have implemented it here is an improvement though and "SAPP" will subsequently be updated to imple- ment this means of "auto-faking". ; ; CALL DOCOLL ; Get group count and 1st group ; don't save real group count LD A,(NEWGRP) ; Get our fake group count LD (DIRGRP),A ; Save fake number of groups instead JR FAKGRP ; Save real start/search group ; ; If enough memory, set up variables (see above about faking) ; CKDRBF: CALL DOCOLL ; Get group count and 1st group LD (DIRGRP),A ; Save number of groups in directory ; FAKGRP: LD (STRTGP),HL ; Starting group in case faked LD (SRCHGP),HL ; Save 1st group after directory ; ------------------------------------------------------------------------ CP/M 3 TEST CP/M 3.0 tests made on a Morrow MD-11 with two 20 Mb CMI hard disk drives, formatted capacity is 22,004k each, block size of 4k, and a maximum capacity of 2048 directory entries. In my test, 2,488k of files were present on the test hard drive. In performing the test I newly formatted one hard drive then randomly PIP'ed some files to it eg., *.MAC, *.BIN, *.TXT, *.ASM.. etc., which didn't make things totally scrambled, but it was a start. Then, I ran- domly PIP'ed some more files to another user area. But that still was not random enough for my test. Then I edited a few of those files in conjustion, along with erasing, etc. That broke things up just enough for the test to soon begin. I used SAPP (for CP/M 3.0) as the directory MUST BE sorted with any un- used or erased entries set to 0E5H's. Then used DU to list the active directory entries to my printer to see where the allocations for each file were now at, before use of this program. Then came the big test with the program. The Morrow can have up to 2048 maximum directory entries, so I was given the warning message and shown the maximum number of active entries that I could have (1536 for my system). I already knew that I had MUCH less than that amount active, so I con- tinued. In a moment, I was given the statistics of the drive which was displayed as follows: Statistics Active dir entries: 94 Tot grps allocated: 372 Group swaps needed: 156 Group reads needed: 312 Group writes needed: 312 Dir rewrites needed: 61 Press 'Y' to continue, else aborts: And I chose to continue at the prompt, after those stats. The program was roughly halfway through listing the files as it works on each one takes a bit of time when it just zipped through the remaining file names and said completed. I then used DU to inspect and everything is fine. CRC check of the those files that were just relocated compared to the CRC of their originals resulted in an exact match. Final test was made on the other hard disk drive where there were 5,824k of files present. The statistics were: Statistics Active directory entries: 427 Total groups allocated: 1456 Group swaps needed: 1449 Group reads needed: 2889 Group writes needed: 2889 Directory rewrites needed: 424 The final test took approximately 3.5 hours for the program to complete its task. Most definitely the program does take quite a bit of time to do its work, but in comparison to the act of copying files to floppy or other drive, then erasing or reformatting the source drive, and copying the files back to the source drive, this program is a labor saving de- vice. Result: TEST SUCCESSFUL FOR CP/M 3.0. ------------------------------------------------------------------------ FINAL NOTES Definitely the wear and tear that this program may save on a drive will justify its periodic usage. Much of the original source code is still intact, although I did change labels to a limit of 6 characters and removed the 8080 code which was in the program for conditional assembly. Plus some minor changes were made, replacing some 8080 code with Z80 eg., usage of direct load of DE, etc), addition of my own code for compati- bility with CP/M 3.0 utilizing routines from CPM22E, and accomodation for low TPA systems and/or any systems with too large of a directory entry capability. The author of RESTORE from which this program has developed, should have a field day figuring out where all of the mods have been made. Much credit should be given to him, for his work. Thanks much to Steve Dir- ickson. ------------------------------------------------------------------------ - George Reding  TITLE 'FiRe - File Restoral Utility Program' ; ;----------------------------------------------------------------------- ; ; FIRE v12 by George F. Reding 08/10/87 ; ; Utility program to restore disk files to like-new condition, where the ; file allocations for each file are in a sequential order rather than ; being scattered all over the disk (such as after a file/program has ; been modified). ; ; NOTE: This program may possibly be not suitable ; for use on a hard disk drive that has been ; "partitioned" into several pseudo-drives. ; Use at your own risk especially in such case. ; ; DPB offset and table lengths for CP/M 2.2 are different from those for ; CP/M 3.0. They are now automatically set by the program, and the BIOS ; access is performed differently, eliminating the need to copy BIOS ; vectors into the program. Much 8080 code is changed to Z80 which con- ; serves memory space and increases the speed of the program, although ; it does restrict usage to computers with Z80 microprocessors. You may ; use the program even if available (TPA) memory falls short of the cap- ; ability to handle your system maximum directory entry capacity (BSH, ; BLM, DRM, and AL0 & AL1 values are all factors). Either your TPA may ; be too low, or your system directory capacity (DRM) is too large e.g., ; 2048 or more? max entries capacity. If the situation does arise, a ; message is given of the MAXIMUM ACTIVE entries that you may have to ; be able to continue, and you may abort if you desire not to continue. ; The user should ensure that there are LESS active directory entries ; by using DU, after usage of SAP. ; ; >> WARNING << To CONTINUE with MORE entries ACTIVE ; than the figure shown WILL TRASH both files and di- ; rectory. Because of the memory limitation and dif- ; ferences among systems, no warranties are expressed ; or implied and a user of this program shall assume ; any and all responsibility for results arising from ; usage of the program. ; ; CP/M 3 test made on a Morrow MD-11 with two 20M hard drives, 22,004k ; formatted capacity. Morrow directory entry maximum is 2048 entries ; for each hard drive. ; George F Reding ; P.O. Box 86386 ; North Vancouver, BC ; Canada V7L 4K6 ; ; CompuServe: 72436,45 ; ; ; 08/10/87 Modified CONBUF routine so that if return only is entered ; v12 after the summary is given that the program will properly ; abort. - George Reding ; ; 07/13/87 The original clobbered the DE register in the XBIOS2 routine ; v11 which would cause RECTRAN for CP/M 2.2 to nor work properly. ; - George Reding ; ; 06/15/87 Orginal version of FIRE. Created from RESTORE v 1.2. ; v10 Created from RESTORE v 1.2 ; ;----------------------------------------------------------------------- ; ; Created from: RESTORE v 1.2 ; by: Steve Dirickson ; 21145 Raintree Place NW ; Poulsbo, WA 98370-9726 ; ; Other credit: ; CPM22E by Mike Griswold ; SYSLIB by Richard Conn ; ;----------------------------------------------------------------------- ; ; ASEG ; M80 needs this ; NO EQU 0 YES EQU NOT NO ; For conditional assembly ; DEBUG EQU NO ; Yes for debug display PROTCT EQU NO ; Yes to disable disk write ; VER EQU 12 ; Version number VERM EQU 08 ; Version month VERD EQU 10 ; Version day VERY EQU 87 ; Version year ; ; System equates ; BDOS EQU 0005H ; BDOS DPB2OF EQU 10 ; Offset to CP/M 2.2 DPB DPB2LN EQU 15 ; Length of CP/M 2.2 dpb DPB3OF EQU 12 ; Offset to CP/M 3.0 dpb DPB3LN EQU 17 ; Length of CP/M 3.0 dpb FCB EQU 5CH ; RCCPM3 EQU 1024 ; Size of CP/M 3.0 record buffer ; Set to your largest record size ; Bdos function codes ; CONINF EQU 1 ; Console input CONOUF EQU 2 ; " output RDCBUF EQU 10 ; Read console buffer CONSTF EQU 11 ; Console status RETVER EQU 12 ; Return version # RESETF EQU 13 ; Reset disk GETDSK EQU 25 ; Get default disk ; ; Ascii characters ; BEL EQU 07H ; Bell BS EQU 08H ; Backspace CR EQU 0DH ; Carriage return LF EQU 0AH ; Linefeed TAB EQU 09H ; Tab ; ;----------------------------------------------------------------------- ; ; Macros to make things easier ; PUSHRG MACRO PUSH HL PUSH DE PUSH BC ENDM ; POPRG MACRO POP BC POP DE POP HL ENDM ; ;----------------------------------------------------------------------- ; ; ORG 100H ; FIRE: JP CPUTST ; May be modified with SID/DDT DWAIT: DEFB 0FFH ; Non-zero for disk change wait ADDLF: DEFB 0 ; Zero = no LF, else 0Ah = LF RECSIZ: DEFW RCCPM3 ; Size of CP/M 3.0 record buffer ; ; Use Bob Freed's z80 CPU test ; CPUTST: SUB A ; Z80 test JP PO,SIGNON ; If Z80, continue CALL PRINT ; Else give msg DEFB CR,LF,'Z80 needed',CR,LF,0 JP 0000H ; And quit ; ; Get version, see if valid, then give program name/version ; SIGNON: LD C,RETVER ; Get version CALL BDOS LD A,L ; Get version to 'A' LD (VERFLG),A ; Store it CP 22H ; Must be at least 2.2 JP C,0000H ; If less then quit LD A,H OR A ; Test for 0 for CP/M JP NZ,0000H ; If not, quit as MP/M untested ; CALL PRINT DEFB CR,LF,LF,'FiRe v' DEFB VER/10+'0',(VER MOD 10)+'0',' ' DEFB VERM/10+'0',(VERM MOD 10)+'0','/' DEFB VERD/10+'0',(VERD MOD 10)+'0','/' DEFB VERY/10+'0',(VERY MOD 10)+'0',' ' DEFB ' GFR',0 ; LD A,(DWAIT) OR A JR Z,CLRDT ; Skip if no disk change wait CALL PRINT DEFB CR,LF,LF,'Change disk - ' DEFB 'any key continues ',0 CALL CONBUF ; Wait for input ; ; Initialize data and buffer areas to zero ; CLRDT: XOR A ; Clear data/stack space to 0 LD HL,DATA ; Point to 1st spot (source) LD (HL),A ; Fill 1st LD DE,DATA+1 ; Point to 2nd spot (destination) LD BC,DATSIZ ; Length LDIR ; Zero it all ; ; Next is for CP/M plus in debug mode ; IF DEBUG PUSHRG CALL V3CHEK JR C,CLRDT1 ; If not CP/M 3.0 skip this PUSH HL ; Save HL CALL PRINT DEFB CR,LF,'HSTBUF: ',0 POP HL ; Restore for display CALL PHL4HC CLRDT1: POPRG ENDIF ; ; Only CP/M 3.0 needs the next buffer ; CALL V3CHEK ; Cy set if not 3.0 JR C,CLRDT2 ; Skip if 2.2 XOR A ; Zero for fill LD (HSTBUF),HL ; Save address of CP/M 3.0 record buffer LD BC,(RECSIZ) ; Get size of CP/M 3.0 record buffer LDIR ; ; Save directory buffer start address, initialize this buffer ; CLRDT2: LD (DIRECT),HL ; Save address of directory buffer ; IF DEBUG PUSHRG PUSH HL ; Save HL CALL PRINT DEFB CR,LF,'DIRECT: ',0 POP HL ; Restore for display CALL PHL4HC POPRG ENDIF ; XOR A ; Zero LD (HL),A ; The 1st location (source) LD BC,256*32 ; Length to fill LDIR ; ; Buffers initialized, now set up stack and linefeed char ; LD SP,STACK ; Set our own stack LD A,(ADDLF) ; Get LF character or zero LD (FILELF),A ; Store it ; ; Set up dpb length and dpb offset according to CP/M version ; SETDPB: CALL V3CHEK ; Carry set if not 3.0 JR C,STDPB2 ; Skip if 2.2 LD HL,DPB3OF ; Offset XLT address to DPB address LD (DPBOF),HL ; Was defaulted for CP/M 2.2 LD HL,DPB3LN ; Length of CP/M 3.0 DPB table  LD (DPBLN),HL ; Was defaulted for CP/M 2.2 ; ; The next protect conditional changed to be compatible. ; Wasn't sure if the original would work under CP/M 3.0. ; STDPB2: IF PROTCT LD A,0AFH ; Store "XOR A" op code LD (WRITE),A ; At start of write routine LD A,0C9H ; Store "RET" op code LD (WRITE+1),A ; Just after it ENDIF ; ; If no disk specified on command line, get current disk ; SETUP: LD A,(FCB) ; Check for any drive DEC A JP P,SETUP1 ; Skip if disk mentioned LD C,GETDSK ; Get current disk (0=A,1=B,...) CALL BDOS ; ; Save disk, give msg, reset disk (CP/M 3.0), and go select it ; SETUP1: LD (LOGDSK),A ; Save disk for CP/M 3.0 PUSH AF ; Save it CALL PRINT ; Give drive message DEFB CR,LF,LF,'Drive: ',0 POP AF ; Restore disk PUSH AF ; Resave it ADD A,'A' ; Convert to ASCII LD (ASCDSK),A ; Save ASCII disk letter LD E,A CALL CONOUT LD C,RESETF ; Reset disk for CP/M 3.0 CALL BDOS POP AF ; Restore disk LD C,A ; Disk to 'C' CALL SELECT ; Select disk and get parameters ; ; Check that there is enough memory to read in the directory. ; Get the max # of dir entries that will fit in available memory. ; GTDRMX: LD A,(BDOS+2) ; Fetch BDOS page DEC A ; Less one LD HL,(DIRECT) ; Subtract start of directory buffer SUB H ; To get space available for directory LD H,0 LD L,A ; # of 256-byte pages available ADD HL,HL ; *2 multiply by 8 to get number of ADD HL,HL ; *2 32-byte directory entries ADD HL,HL ; *2 that will fit ; IF DEBUG PUSHRG PUSH HL ; Save HL CALL PRINT DEFB CR,LF,'MAXDIR: ',0 POP HL ; Restore for display CALL PHLFDC POPRG ENDIF ; LD DE,(DRM) ; Get # of directory entries on disk INC DE ; Convert from 0-relative to 1-relative OR A SBC HL,DE ; Subtr from max that will fit JP NC,CKDRBF ; Ok if enough room ; ;----------------------------------------------------------------------- ; ; Adjust maximum directory number (DRM) to a smaller/fake value for cases ; where the directory has a capacity to hold 2048 (or more??) entries, or ; in cases where the TPA may fall short of of capability to handle all of ; the directory entries. Adjust the directory group count (AL0 and AL1) ; which varies with BLM (block size BLS) so that it matches with the ; smaller/fake DRM value. In order to use the program in such cases of ; too many directory entries and/or of too little TPA space the TOTAL of ; ACTIVE directory entries will have to be verified by the user with ; "DU" or some equivilent program (after using "CLEANDIR", "SAP", "SAPP", ; etc). A warning message and the maximum number of active entries that ; the program can handle is given, with opportunity for the user to ; abort. Faking of the DRM value is similar to that which "SAPP" (for ; CP/M 3.0) is capable of (an assembly time option). The way I have im- ; plemented it here is an improvement though and "SAPP" will subsequently ; be updated to implement this means of "auto-faking". - George Reding ; LD A,(BSH) LD B,A LD HL,128 ; Calculate BLS ; LESSBK: ADD HL,HL ; Shift HL left BSH times DJNZ LESSBK ADD HL,HL ; *2 for size of 2 buffers LD DE,(DIRECT) ; Start of directory buffer ADD HL,DE ; Add to get space used up ; CALL V3CHEK ; Carry set if not 3.0 JR C,NO3BUF ; Skip if 2.2 LD DE,(RECSIZ) ; Get size of CP/M 3.0 record buffer ADD HL,DE ; Add it to the space used up ; NO3BUF: EX DE,HL ; Space used up to DE LD HL,(BDOS+1) ; BDOS page (memory top) to HL OR A ; Clear for subtract SBC HL,DE ; Subtract space used from available LD L,H ; # of 256 byte pages available to L LD H,0 DEC L ; Less one page ADD HL,HL ; *2 multiply by 8 to get number of ADD HL,HL ; *2 32-byte directory entries ADD HL,HL ; *2 that will fit LD (NEWMAX),HL ; Save memory maximum directory entries ; ; Now have faked maximum directory entries. Now calculate new directory ; allocation groups ; LD A,(BLM) ; Get block size INC A ; Make 1 relative LD L,A ; Put into HL LD H,0 LD DE,8 ; Divisor 8 - 128 blocks make 1k CALL UDIVID ; Result HL = block size k's LD (BLMSIZ),HL ; Save block k size ; EX DE,HL ; Move k size to DE (divisor) LD HL,(NEWMAX) ; Get memory maximum (dividend) CALL UDIVID LD DE,32 ; Divisor 32 entries per k CALL UDIVID LD H,0 ; Zero high LD A,L ; Result (new groups) into 'A' LD (NEWGRP),A ; Save new/fake group count ; ; Now have new directory groups. Recalculate matching new fake DRM. ; LD DE,32 ; Mult by 32 entries CALL MULT LD DE,(BLMSIZ) ; Mult by k size CALL MULT DEC HL ; Make 0-relative LD (FAKDRM),HL ; Save new/fake "DRM" ; CALL PRINT DEFB CR,LF,LF,BEL DEFB 'Insufficient directory room',CR,LF,LF DEFB 'Continue ONLY IF LESS than ',0 LD HL,(FAKDRM) ; Get fake maximum entries INC HL CALL PHLFDC CALL PRINT DEFB ' entries are ACTIVE!',CR,LF DEFB 'To continue IF MORE WILL DESTROY' DEFB ' directory & files!',CR,LF,LF DEFB 'Press ''Y'' to continue,' DEFB ' else aborts: ',0 ; CALL CONBUF ; Get response LD A,(INBUF+2) AND 31 ; "Y", "y", and "^Y" are equiv CP 'Y'-40H JP NZ,0000H ; If not Y, then abort ; LD HL,(FAKDRM) ; Get our fake value LD (DRM),HL ; Store it CALL DOCOLL ; Get group count and 1st group ; don't save real group count LD A,(NEWGRP) ; Get our fake group count LD (DIRGRP),A ; Save fake # of group instead JR FAKGRP ; Save real start/search group ; ; If enough memory, set up variables (see above about faking) ; CKDRBF: CALL DOCOLL ; Get group count and 1st group LD (DIRGRP),A ; Save # of groups in directory FAKGRP: LD (STRTGP),HL ; Starting group in case faked LD (SRCHGP),HL ; Save 1st group after directory ; ;----------------------------------------------------------------------- ; ; Then read in the directory ; CALL READDR ; Read in the directory ; LD DE,(DRM) ; Get maximum # of directory entries in DE INC DE ; Make 1-relative LD (DIRACT),DE ; Default to all entries active ; ; Count active directory entries by finding 1st erased entry if any ; LD BC,32 ; Size of each entry LD HL,(DIRECT) FINDER: LD A,0E5H ; Look for first 'E5' in user # CP (HL) JR Z,FOUNDR ADD HL,BC ; Bump pointer to next entry DEC DE LD A,E OR D ; See if all entries checked JR NZ,FINDER ; Loop if some left JR DRNMST ; If no 'E5' found, already set up ; ; Now calculate the number of active entries ; FOUNDR: PUSH HL ; Save pointer to 1st erased entry LD HL,(DRM) ; Calculate # of entries found INC HL OR A SBC HL,DE ; Subtr # of entries left LD (DIRACT),HL ; Save # of active dir entries ; IF DEBUG PUSHRG PUSH HL ; Save HL CALL PRINT DEFB CR,LF,'DIRACT: ',0 POP HL ; Restore for display CALL PHLFDC POPRG ENDIF ; ; Because Morrow had an oddity (possibly others) of setting the RC field ; to 00H on newly formatted HARD drives, the set of DE (should have been ; BC) register to the bytes-left value is now remarked out (below) and a ; value of 13 is used for "remaining bytes to check". Besides, if 13 ; "sequ ential bytes" are 0E5h you can probably bet your booties any r- ; emaining bytes are the same, ensuring the directory was sorted and ; cleaned with SAP. ; ;;; LD HL,32 ; Mult entries left by 32 ;;; CALL MULT ; To get # of bytes ;;; EX DE,HL ; Move count of bytes left to DE LD BC,13 ; Check 13 bytes, see note above POP HL ; Restore pointer to 1st erased entry ; ; Ensure remaining free directory space is erased ; CKERLP: LD A,0E5H ; Check that the rest of directory CPI ; Is erased ; IF DEBUG JP NZ,DRNSRT ELSE JR NZ,DRNSRT ; Error if anything but 'E5' found ENDIF JP PE,CKERLP ; Loop until rest of directory checked ; ; Calculate total dir bytes, save 1st buffer address ; DRNMST: LD HL,(DRM) ; Max # of directory entries INC HL ; Make 1-relative LD DE,32 ; Size of each directory entry CALL MULT ; Hl has # of bytes in directory buffer LD (DIRBYT),HL ; Save it ; IF DEBUG PUSHRG PUSH HL ; Save HL CALL PRINT DEFB CR,LF,'DIRBYT: ',0 POP HL ; Restore for display CALL PHL4HC POPRG ENDIF ; LD DE,(DIRECT) ; Size in HL, get directory buffer start ADD HL,DE ; Add size and directory buffer start LD (GRBUF1),HL ; Addr of 1st group buffer ; IF DEBUG PUSHRG PUSH HL ; Save HL CALL PRINT DEFB CR,LF,'GRBUF1: ',0 POP HL ; Restore for display CALL PHL4HC POPRG ENDIF ; LD A,(BDOS+2) ; Fetch BDOS page DEC A ; Less one SUB H ; Subtr buffer address LD D,A ; Move to high, to multiply by 256 LD E,0 ; DE = bytes available for buffers ; ; Calculate the size of each block on the disk - 1k, 2k, 4k, etc. ; See if room for two buffers. ; CALBLS: LD A,(BSH) LD B,A LD HL,128 ; Calculate BSL ; CBLSLP: ADD HL,HL ; Shift HL left bsh times DJNZ CBLSLP LD (BLS),HL ; HL = block size (BLS) bytes, 1024, 2048,.. ADD HL,HL ; Double to get size of 2 buffers EX DE,HL ; Put available space back in HL OR A SBC HL,DE ; Subtr buffer size from available space ; JP NC,CKSORT ; Ok if no carry CALL PRINT DEFB CR,LF,LF,'Insufficient ' DEFB ' buffers room',BEL,0 JP 0000H ; DRNSRT: CALL PRINT ; Directory unsorted error message DEFB CR,LF,LF,'Unsorted - use SAPP or' DEFB ' equivilent first',CR,LF,0 JP 0000H ; ; Calculate and save addr of 2nd buffer ; CKSORT: LD DE,(BLS) LD HL,(GRBUF1) ; Calculate address of 2nd group buffer ADD HL,DE LD (GRBUF2),HL ; IF DEBUG PUSHRG PUSH HL ; Save HL CALL PRINT DEFB CR,LF,'GRBUF2: ',0 POP HL ; Restore for display CALL PHL4HC POPRG ENDIF ; CALL PRINT ; Hard drives may take time DEFB CR,LF,LF,'Calculating...',0 ; LD DE,(DIRECT) ; Put start of directory in DE LD HL,32 ADD HL,DE ; Point to 2nd directory entry EX DE,HL ; Now HL = 1st entry, DE = 2nd LD BC,(DIRACT) ; # of entries to check DEC BC ; Minus 1 ; ; Ensure the directory is sorted ; CKSTLP: CALL CMPDIR ; Compare the two directory entries JP C,DRNSRT ; Error if unsorted PUSH BC LD BC,32 ; Size of directory entries EX DE,HL ADD HL,BC ; Bump pointers EX DE,HL ADD HL,BC POP BC DEC BC LD A,B OR C JR NZ,CKSTLP ; Loop until done ; ; Directory is verified to be sorted ; LD B,16 ; Assume 8 bit groups LD A,(DSM+1) OR A JR Z,SGRPAL LD B,8 ; SGRPAL: LD A,B LD (GRPALL),A ; Save # of allocation groups per entry XOR A ; Clear stats flag LD (STATS),A ; Zero means stats only ; REDO: LD A,(STATS) ; Get stats flag OR A CALL NZ,READDR ; If after stat pass, reread directory ; ; Set initial starting group to be the 1st group after directory ; LD HL,(STRTGP) ; Starting group in case faked LD (SRCHGP),HL ; LD HL,(DIRACT) LD (DIRCTR),HL ; Initialize directory entry counter LD HL,(DIRECT) ; Point to 1st entry in directory ; ; Main program loop (finally) ; ; On entry, HL points to next directory entry to be checked. (SRCHGP) ; holds the 1st alloc group we want this file to have. The allocation ; group pointer and counter are set up, then the inner loop is entered ; to process each allocation group assigned to this file.  ; RECOVR: LD (FILPTR),HL ; Save pointer to next file LD A,(STATS) OR A JR Z,RECOV1 ; SHOFIL: CALL PRINT ; Show user what we're doing DEFB CR FILELF: DEFB 0,0 ; Program sets to 0 or linefeed ; CALL PRINT DEFB 'Relocating: ',0 LD A,(ASCDSK) LD E,A CALL CONOUT LD HL,(FILPTR) ; Get file pointer PUSH HL LD A,(HL) ; Get user # CALL PAFDC LD E,':' CALL CONOUT POP DE ; Get file pointer back to 'DE' INC DE ; Bump past user # CALL PFN1 CALL CTLCS ; See if abort requested JP Z,ABORT ; RECOV1: LD HL,(FILPTR) ; Restore file pointer LD DE,16 ; Offset to allocation map ADD HL,DE LD (ALLPTR),HL ; Pointer to working allocation group ; IF DEBUG PUSHRG PUSH HL ; Save HL CALL PRINT DEFB CR,LF,'ALLPTR: ',0 POP HL ; Restore for display CALL PHL4HC POPRG ENDIF ; LD A,(GRPALL) ; Get max # of alloc groups per entry LD (GRPNUM),A ; Save it as the group counter ; ; Inner loop to process each directory entry. This loop steps through ; the alloc ation map for the current directory entry, checking that the ; next allocation group is the next sequential group. If it isn't, the ; group allocated to this file is swapped with the next sequential group ; if it is also allocated, or simply copied to the next group if unallo- ; cated. The directory entries are updated to reflect the group swap. ; The loop also terminates when a zero allocation group is found, indi- ; cating that no more groups are allocated to this directory entry. ; ; On entry, HL points to the next allocation group in the disk map of ; the current directory entry. This should be the next consecutive al- ; location group. If not, the group pointed at is swapped with the next ; sequential group. ; ; (SRCHGP) holds the number of the next sequential group ; RECLP: LD HL,(ALLPTR) ; Pointer to next map entry to check LD BC,0 ; Check for no more groups allocated CALL GRPCMP ; For this directory entry JP Z,DONXEN ; If so, skip to next entry ; IF DEBUG PUSHRG CALL PRINT DEFB CR,LF,'At RECLP, SRCHGP: ',0 LD HL,(SRCHGP) CALL PHL4HC POPRG ENDIF ; LD HL,(SGPCTR) ; Increment allocation group counter INC HL LD (SGPCTR),HL ; LD HL,(ALLPTR) ; Get back pointer LD BC,(SRCHGP) ; Next sequential group that should be there ; CALL GRPCMP ; See if the next group is already in place JP Z,DONEXT ; Skip if no swap of group needed ; SWAP: LD A,0FFH ; Set flag that directory has changed LD (DIRCHG),A LD HL,(SWPCTR) ; Increment swap counter INC HL LD (SWPCTR),HL CALL GETGRP ; If swap needed, see if it's allocated LD (GRPTR),HL ; Save possible allocation pointer ; IF DEBUG PUSHRG PUSH HL ; Save HL CALL PRINT DEFB CR,LF,'GRPTR: ',0 POP HL ; Restore for display CALL PHL4HC POPRG ENDIF ; LD A,H OR L PUSH AF ; Save flags JR Z,COPY2 ; If not allocated, don't worry about copy ; IF DEBUG PUSHRG CALL PRINT DEFB CR,LF,'Copying group ',0 LD HL,(SRCHGP) CALL PHL4HC CALL PRINT DEFB ' to GRBUF1',0 POPRG  ENDIF ; COPY1: LD A,(STATS) OR A JR Z,COPY1X ; Skip if stats only LD DE,(SRCHGP) CALL DEGRUP ; Select track and sector for desired group LD HL,(GRBUF1) ; Select DMA address LD (DMAADR),HL CALL READGP ; Read the group into group buffer 1 ; COPY1X: LD HL,(GRDCTR) ; Increment group read counter INC HL LD (GRDCTR),HL COPY2: LD HL,(ALLPTR) ; Get bad group in current entry LD E,(HL) LD A,(DSM+1) ; See if 8 or 16-bit groups OR A JR Z,COPY2S ; Skip if small groups INC HL LD D,(HL) JR COPY2M ; COPY2S: LD D,0 ; Zero high byte for small groups ; COPY2M: LD (BADGRP),DE ; Save # of group being swapped ; LD A,(STATS) OR A JR Z,COPY2X ; Skip if stats only ; IF DEBUG PUSHRG CALL PRINT DEFB CR,LF,'Copying group ',0 LD HL,(BADGRP) CALL PHL4HC CALL PRINT DEFB ' to GRBUF2',0 POPRG ENDIF ; COPY2A: CALL DEGRUP ; Select it LD HL,(GRBUF2) ; Select DMA addr LD (DMAADR),HL CALL READGP ; Read it into buffer 2 ; COPY2X: LD HL,(GRDCTR) ; Increment group read counter INC HL LD (GRDCTR),HL POP AF ; Get back flags PUSH AF ; Save again JR Z,COPY4 ; Skip if desired group is not allocated ; COPY3: LD A,(STATS) OR A JR Z,COPY3X ; Skip if stats only LD DE,(BADGRP) ; Get group pointer to swap ; IF DEBUG PUSHRG PUSH DE ; Save DE CALL PRINT DEFB CR,LF,'Copying from GRBUF1 to grp ',0 POP HL ; Restore for display CALL PHL4HC POPRG ENDIF ; COPY3A: XOR A ; Flag allocated write LD (WRTYP),A CALL DEGRUP LD HL,(GRBUF1) ; Select DMA address LD (DMAADR),HL CALL WRITGP ; Write it out ; COPY3X: LD HL,(GWRCTR) ; Increment group write counter INC HL LD (GWRCTR),HL ; COPY4: LD A,(STATS) OR A JR Z,COPY4X ; Skip if stats only LD DE,(SRCHGP) ; Write the copy of the desired group ; IF DEBUG PUSHRG PUSH DE ; Save DE CALL PRINT DEFB CR,LF,'Copying from GRBUF2 to grp ',0 POP HL ; Restore for display CALL PHL4HC POPRG ENDIF ; COPY4A: XOR A ; Flag allocated write LD (WRTYP),A CALL DEGRUP ; Select the proper group LD HL,(GRBUF2) ; Select DMA address LD (DMAADR),HL CALL WRITGP ; COPY4X: LD HL,(GWRCTR) ; Increment group write counter INC HL LD (GWRCTR),HL POP AF ; Get back flags JR Z,COPY6 ; COPY5: LD DE,(BADGRP) ; Get the bad grp number to DE LD HL,(GRPTR) ; Change the allocation of the group LD (HL),E ; Save lower byte of group # LD A,(DSM+1) ; See if 8 or 16-bit groups OR A JR Z,COPY6 ; Skip if small groups INC HL ; Else save upper byte also LD (HL),D ; COPY6: LD DE,(SRCHGP) ; Change the current alloc group LD HL,(ALLPTR) ; To the updated value LD (HL),E LD A,(DSM+1) ; See if 8 or 16-bit groups OR A JR Z,DONEXT ; Skip if small groups INC HL LD (HL),D ; DONEXT: LD HL,(SRCHGP) ; Do next alloc group for this file INC HL ; Incr desired group LD (SRCHGP),HL LD HL,(ALLPTR) ; Get allocation grp pointer INC HL LD A,(DSM+1) ; See if 16-bit groups OR A JR Z,DONEX2 ; Don't increment again for small groups INC HL ; For 16-bit group s, bump pointr again ; DONEX2: LD (ALLPTR),HL ; Save new pointer LD A,(GRPNUM) ; Get group counter DEC A LD (GRPNUM),A JP NZ,RECLP ; Continue if still in current entry ; DONXEN: LD A,(DIRCHG) ; See if directory changed for this entry OR A CALL NZ,WRITDR ; Rewrite directory if so CALL CTLCS ; Check for abort request JR Z,ABORT XOR A LD (DIRCHG),A ; Clear changed flag LD HL,(DIRCTR) ; See if at end of directory DEC HL LD A,H OR L JR Z,EXIT ; Exit when done ; LD (DIRCTR),HL ; Update counter LD HL,(FILPTR) ; Get current file pointer LD DE,32 ; Bump to next file ADD HL,DE JP RECOVR ; Loop back ; ABORT: CALL PRINT DEFB CR,LF,LF,'Restore aborted, ',0 LD HL,(DIRCTR) PUSH HL ; Save HL CALL PHLFDC CALL PRINT DEFB ' entr',0 POP HL ; Restore it LD A,H ; Test if 0 OR A JR NZ,ABORT1 ; LD A,L DEC A JR NZ,ABORT1 CALL PRINT DEFB 'y',0 JR ABORT2 ; ABORT1: CALL PRINT DEFB 'ies',0 ; ABORT2: CALL PRINT DEFB ' left',CR,LF,0 JP 0000H ; EXIT: LD A,(STATS) ; See if doing stats or disk OR A JP NZ,EXIT2 ; Exit if 2nd time through ; CALL PRINT DEFB CR,LF,LF,'Statistics',LF DEFB CR,LF,TAB,'Active dir entries: ',0 LD HL,(DIRACT) CALL PHLFDC CALL PRINT DEFB CR,LF,TAB,'Total groups allocated: ',0 LD HL,(SGPCTR) CALL PHLFDC CALL PRINT DEFB CR,LF,TAB,'Group swaps needed: ',0 LD HL,(SWPCTR) CALL PHLFDC CALL PRINT DEFB CR,LF,TAB,'Group reads needed: ',0 LD HL,(GRDCTR) CALL PHLFDC CALL PRINT DEFB CR,LF,TAB,'Group writes needed: ',0 LD HL,(GWRCTR) CALL PHLFDC CALL PRINT DEFB CR,LF,TAB,'Directory writes needed: ',0 LD HL,(DIRWCT) CALL PHLFDC ; EXITX: LD A,(DWAIT) OR A JR Z,LOOPBK ; Skip if no waiting CALL PRINT DEFB CR,LF,LF,'Press ''Y'' to continue,' DEFB ' else aborts: ',0 CALL CONBUF ; Get response LD A,(INBUF+2) AND 1FH ; 'y', 'y', and 'ctrl-y' are equivalent CP 'Y'-40H JP NZ,0000H ; LOOPBK: INC A ; Set non-stats flag LD (STATS),A CALL PRINT DEFB CR,LF,'Press ^C to abort...',CR,LF,0 JP REDO ; EXIT2: CALL PRINT DEFB CR,LF,LF,BEL DEFB 'Done',CR,LF,0 JP 0000H ; INBUF: DEFB 1,0,0 ; Buffer for console buffer read ; ; ; Read or write the directory of the current disk ; READDR: IF DEBUG PUSHRG CALL PRINT DEFB CR,LF,'Entering READDR',0 POPRG ENDIF ; XOR A ; Clear write flag JR RWDIR ; WRITDR: IF DEBUG PUSHRG CALL PRINT DEFB CR,LF,'Entering WRITDR',0 POPRG ENDIF ; LD HL,(DIRWCT) ; Increment director write counter INC HL LD (DIRWCT),HL LD A,(STATS) OR A RET Z ; Skip if only doing stats ; LD A,1 ; Set write flag LD (WRTYP),A ; Flag as directory write also ; RWDIR: LD (WRFLG),A LD HL,(DIRECT) ; Select directory buffer for DMA address LD (DMAADR),HL LD HL,(SYSTRK) ; Select 1st directory track LD (CURTRK),HL LD HL,1 ; Set record 1 LD (CURREC),HL LD A,(DIRGRP) ; # of groups in directory LD (GRPCTR),A ; To loop counter ; RWDRLP: CALL RWGRP1 ; Read or write group LD A,(GRPCTR) DEC A ; Decrement counter LD (GRPCTR),A JR NZ,RWDRLP ; Loop until all groups done RET ; ; ; Read or write selected group into or from current dma buffer. ; ; On entry, the desired group has been selected by a call to DEGROUP ; which calculates the proper track and record. The starting DMA address ; for the group is in dMADDR. ; ; On entry to writgp only, wrtyp must have been set as follows: ; 0 = write to allocated block ; 1 = write to directory ; READGP: XOR A ; Clear write flag JR RWGRP ; WRITGP: LD HL,(DMAADR) ; Save start of group buffer LD (CRCBEG),HL ; For CRC calculation LD HL,(CURTRK) ; Likewise the current track LD (CRCTRK),HL LD HL,(CURREC) ; And record values LD (CRCREC),HL LD A,2 ; Set write flag ; RWGRP: LD (WRFLG),A ; Set write flag LD (CRCFLG),A ; And CRC-needed flag ; RWGRP1: LD A,(BLM) ; Get block mask INC A ; Increment to get # of 128-byte records LD (RECCTR),A ; Per allocation group - set up counter ; IF DEBUG PUSHRG CALL PRINT DEFB CR,LF,'Entering RWGRP',0 POPRG ENDIF ; ; Loop to read or write a group ; RWGPLP: IF DEBUG PUSHRG CALL PRINT DEFB CR,LF,'In RWGPLP, DMA: ',0 LD HL,(DMAADR) CALL PHL4HC CALL PRINT DEFB ' T: ',0 LD HL,(CURTRK) CALL PHLFDC CALL PRINT DEFB ' S: ',0 LD HL,(CURREC) CALL PHLFDC POPRG ENDIF ; LD BC,(DMAADR) ; Set DMA addr CALL SETDMA LD DE,(CURTRK) ; Get track CALL SETTRK LD DE,(CURREC) ; Get record CALL SETREC ; LD A,(WRFLG) ; See if read or write OR A JR NZ,WRGRP CALL READ ; Read the record JR RWNXSC ; WRGRP: LD A,(WRTYP) ; Get type of write LD C,A CALL WRITE ; Write the record ; RWNXSC: LD DE,(CURREC) ; Incr current record INC DE LD HL,(SPT) ; See if beyond end of track OR A SBC HL,DE ; Subtract current from SPT JR NC,NEXTOK ; If not > SPT then continue ; LD DE,(CURTRK) ; Is > SPT, bump track counter INC DE LD HL,(MAXTRK) ; See if at end of disk OR A SBC HL,DE ; Subtract new track from maximum JR NC,NX TTRK ; If not > maximum track then jump over ; LD DE,0 ; Is > maximum track, wrap to start ; NXTTRK: LD (CURTRK),DE ; Store new current track LD DE,1 ; And set record to 1 ; NEXTOK: LD (CURREC),DE ; Store new current record ; LD HL,(DMAADR) LD DE,80H ; Advance to next DMA address ADD HL,DE LD (DMAADR),HL ; Store next DMA address LD A,(RECCTR) ; Get # of 128 byte records DEC A ; Decrement it LD (RECCTR),A ; Save new ; IF DEBUG JP NZ,RWGPLP ; Normal jump for debug mode ELSE JR NZ,RWGPLP ; Loop until all read ENDIF ; ; CRC verfification ; CKCRC: LD A,(CRCFLG) ; See if CRC needed OR A RET Z ; Return if not ; DEC A ; See if 1st or 2nd time through LD (CRCFLG),A ; Save updated flag JR Z,CRCVER ; If 2nd loop, go verify CRC ; CALL CRCGEN ; Get the CRC for the grp in the buffer LD (CRCVAL),HL ; Save CRC value LD HL,(CRCBEG) ; Restore the grp buffer addr LD (DMAADR),HL LD HL,(CRCTRK) ; Likewise the track LD (CURTRK),HL LD HL,(CRCREC) ; And record values LD (CURREC),HL XOR A LD (WRFLG),A ; Clear write flag JP RWGRP1 ; Go read the group back in ; CRCVER: CALL CRCGEN ; Generate the new CRC EX DE,HL ; Save 2nd CRC in de LD HL,(CRCVAL) ; Get back the original CRC OR A SBC HL,DE ; Compare the two CRCs RET Z ; Return if ok ; CRCERR: CALL PRINT DEFB CR,LF,'CRC error abort',CR,LF,0 JP 0000H ; ; Generate a CRC for the group in the buffer starting at (CRCbeg) with ; length (BLS). CRC value returned in HL. ; CRCGEN: CALL CRCCLR ; Initialize CRC ; LD DE,(BLS) ; Length of group in bytes LD HL,(CRCBEG) ; Point to start of buffer CRCLP: LD A,(HL) ; Get next byte in buffer CALL CRCUPD ; Update the CRC INC HL DEC DE LD A,D OR E JR NZ,CRCLP ; Loop until all bytes done JP CRCDON ; Finish CRC ; ; Get directory group count into a and 1st group number after directory ; into HL. ; DOCOLL: LD L,0 LD A,(AL0) ; Read directory group allocation bits CALL COLECT ; Collect count of directory groups LD A,(AL1) CALL COLECT LD A,L ; Group count in register 'A' and LD H,0 ; HL = 1st group after directory RET ; ; Collect the number of '1' bits in a as a count in L ; COLECT: LD B,8 ; Number of bits ; COLOP: RLA JR NC,COSKIP INC L ; COSKIP: DJNZ COLOP RET ; ; ; Check for CTL-C or CTL-S, returns Z-flag set if CTL-C typed, else NZ ; CTLCS: CALL CONST ; See if character available OR A JR NZ,GETC OR 1 ; Return NZ if not RET ; GETC: CALL CONIN ; Get the character AND 31 CP 'S'-40H ; Wait for next character if CTL-S or S or s CALL Z,CONIN CP 'C'-40H ; Check for CTL-C or C or c RET ; Z-flag set if CTL-C ; ; Select the track/record corresponding to the group in DE ; DEGRUP: LD HL,(DSM) ; Compare group # with maximum OR A SBC HL,DE ; Subtract group number from maximum RET C ; Return bad if too large ; CALL GRP2SC ; Convert group number to track and record CALL SETTRK ; Set track EX DE,HL CALL SETREC ; And record RET ; ; ; Convert group in DE to corresponding record (HL) and track (DE) ; GRP2SC: LD H,D LD L,E LD A,(BSH) ; Get block shift (count) LD B,A ; To 'B' XOR A ; Zero 'A' ; SHLGRP: ADD HL,HL ; Shift block left 1 place JP NC,SHLSKI INC A ; SHLSKI: DJNZ SHLGRP LD (TEMPC),A EX DE,HL LD HL,(SPT) ; Get SPT CALL NEG ; Negate it for division EX DE,HL ; Put into DE LD BC,0 ; Initial quotient ; GRPDLP: INC BC ; Incr quotient ADD HL,DE ; Divide by adding negative SPT JP C,GRPDLP LD A,(TEMPC) DEC A LD (TEMPC),A JP P,GRPDLP ; DEC BC ; Adjust division result LD DE,(SPT) ; Position SPT to DE, negative value = HL ADD HL,DE ; Add to get remainder PUSH HL ; Save records remaining LD HL,(SYSTRK) ; Get number of system tracks ADD HL,BC ; Add to quotient (tracks) EX DE,HL ; Put absolute track into DE POP HL ; Get back records remaining INC HL ; Make 1-relative RET ; ; Find which file the group in (BC) belongs to. On return, HL has the ; address of the allocation map entry which matches BC, or 0 if no ; matching entry found. ; GETGRP: LD HL,(DIRACT) ; Max directory entry number INC HL ; Make 1-relative LD (FILECT),HL LD HL,(DIRECT) ; Point to directory PUSH HL ; Save pointer to name ; GETGLP: POP HL ; Restore file pointer PUSH HL LD A,(HL) ; Look at user number of next directory entry CP 0E5H ; Erased? (shouldn't be, but just in case) JR Z,GETGNF ; Ignore if so LD DE,14 ; Now get record count ADD HL,DE ; S2 portion .. LD A,(HL) ; Is 0 in CP/M 1.4 AND 0FH LD E,A INC HL LD A,(HL) OR E JR Z,GETGNF ; Skip if no records allocated (empty file) LD A,(GRPALL) ; Initialize loop counter to number of LD E,A ; Allocation groups per file ; GETGL2: INC HL ; Point at next allocation map entry CALL GRPCMP ; See if it matches the one in 'BD' JR Z,GETGEX ; Exit if match found DEC E ; Count down JR NZ,GETGL2 ; Go test some more ; GETGNF: POP HL ; Get back entry pointer LD DE,32 ADD HL,DE ; Bump to next entry PUSH HL ; Save i t LD HL,(FILECT) ; See if all directory entries checked DEC HL LD (FILECT),HL LD A,H OR L JR NZ,GETGLP ; Continue if more entries left ; GETGEX: POP DE ; Pop extra directory pointer LD A,H OR L RET Z ; Exit if HL = 0, indicating no match found LD A,(DSM+1) ; Get disk size indicator OR A JR Z,GETGX1 ; Skip if 8-bit groups DEC HL ; Since GRPCMP increments pointer for big ones GETGX1: INC A ; Reset zero flag to show match found RET ; Return with address of match in HL ; ; Compare the directory entry pointed to by HL to the one pointed to by ; DE. Set carry flag if the one pointed to by HL (lower in the direc- ; tory) is larger, indicating that the directory is not sorted. ; CMPDIR: PUSH HL ; Save registers PUSH DE PUSH BC ; IF DEBUG PUSHRG PUSH DE ; Save 2nd file PUSH HL ; Save 1st file CALL PRINT DEFB CR,LF,'Comparing ',0 POP DE ; Restore 1st file INC DE ; Skip user number CALL PFN1 CALL PRINT DEFB ' and ',0 POP DE ; Restore 2nd file INC DE ; Skip user # CALL PFN1 POPRG ENDIF ; ; LD B,15 ; Check user, name, type, extent, s2, & rc ; ; Loop through the user number, file name, type, and extent. Any time ; the character in the lower entry is larger than the corresponding ele- ; ment in the upper entry, meaning the directory is not in order, the ; routine exits with the carry flag set. When the first non-match is ; found with the upper element larger, indicating that the two entries ; are in order, the routine exits with the carry flag cleared. ; CMPDLP: LD A,(HL) ; Compare corresponding elements AND 7FH ; Mask off attribute bit LD C,A ; Save it LD A,(DE) AND 7FH CP C JR C,CMPDEX ; Error if lower one is larger JR NZ,CMPDEX ; If not the same and no carry, ; Higher must be larger - exit ok INC DE ; If the same continue the check INC HL ; Bump pointers DJNZ CMPDLP ; Loop until all characters checked ; ; This point should never be reached, since, if we drop out of the loop, ; it indicates that the two entries have the same user number, file name, ; type, and extent which is not really legal. "SAPP", "CLEANDIR", or ; whatever was used to sort the directory should have caught this, but ; didn't. Treat this the same as an improperly sorted directory. ; CMPDER: SCF ; Set carry ; CMPDEX: POP BC POP DE POP HL RET ; ; ; See if the group # in bc and the one pointed to by hl are the ; same. Set z flag if so, nz if not. Hl is incremented if the ; allocation groups are 16 bits. Flags are set. ; GRPCMP: LD A,(DSM+1) LD D,A ; Set size indicator LD A,C ; Get (lower byte of) group number INC D DEC D ; See if one or two bytes in number JR Z,CMP8 ; Skip if only 1 byte to be checked CP (HL) ; Compare lower byte of number INC HL ; Bump pointer RET NZ ; Return if not equal LD A,B ; Otherwise get upper byte ; CMP8: CP (HL) ; Set flag for compare RET ; ; 2's complement HL ==> HL ; NEG: LD A,L CPL LD L,A LD A,H CPL LD H,A INC HL RET ; ; Unsigned division. Divide HL by DE. Destroys register 'A'. Return ; HL = quotient, DE = remainder, carry clear if ok. Carry set if DE > HL ; or if DE = 0 else carry is clear. UDIVID: PUSH BC ; Save registers LD BC,0 ; Initialize quotient PUSH HL ; Save HL OR A ; Clear carry SBC HL,DE ; Test if divisor > dividend POP HL ; Restore HL JR C,DIVBAD ; If so, it is bad LD A,E ; Else test divisor OR D ; For 0 JR NZ,DIVLOP ; If <> 0 continue DIVBAD: POP BC ; Restore registers SCF ; Set carry to show bad RET ; DIVLOP: INC BC ; Increment quotient OR A ; Clear carry for subtract SBC HL,DE ; Division by subtract JR C,DIVOFL ; If borrow, done dividing JR NZ,DIVLOP ; If <> 0 more to do JR DIVREM ; Was 0, skip calc of remainder ; DIVOFL: ADD HL,DE ; HL=negative remainder, DE=divisor DEC BC ; The even division was 1 less ; DIVREM: EX DE,HL ; Positive remainder to DE LD H,B ; Put quotient LD L,C ; Into HL POP BC ; Restore register OR A ; Clear carry (valid result) RET ; ; Multiply HL by DE, return result in HL ; MULT: PUSH BC PUSH DE EX DE,HL LD B,D LD C,E LD A,B OR C JR NZ,MULCON LD HL,0 ; Filter special case JR MLDONE ; Of multiply by 0 ; MULCON: DEC BC LD D,H LD E,L ; MULTLP: LD A,B OR C JR Z,MLDONE ADD HL,DE DEC BC JR MULTLP ; MLDONE: POP DE POP BC RET ; ; Print string following the call. Return to spot after string. ; PRINT: EX (SP),HL ; Get string pointer ; PRINLP: LD A,(HL) INC HL OR A JR Z,PREX LD E,A PUSH HL CALL CONOUT POP HL JR PRINLP ; PREX: EX (SP),HL RET ; ;----------------------------------------------------------------------- ; ; Select disk whose number is in C (A=0, B=1, etc) ; SELECT: CALL V3CHEK ; Carry set if not 3.0 JR C,SELEC2 ; Skip if 2.2 LD DE,1 ; Set 1st time flag for CP/M 3.0 LD (DEREG),DE ; Into e of DPB LD (BCREG),BC ; Into c of DPB LD A,9 ; BIOS SELDSK function CALL XBIOS3 ; Do it JR SELEC3 ; Skip over SELEC2: LD A,9 ; BIOS SELDSK function CALL XBIOS2 ; Do it ; SELEC3: LD A,H ; Get result OR L JP Z,0000H ; LD E,(HL) ; Get the record table pointer INC HL LD D,(HL) DEC HL EX DE,HL LD (RECTBL),HL ; Save the record table pointer LD HL,DPB2OF ; Default, CP/M 2.2 offset to DPB ; DPBOF EQU $-2 ; ADD HL,DE LD A,(HL) ; Get DPB pointer INC HL LD H,(HL) LD L,A ; Into HL LD DE,DPB ; Copy dpb to local area LD BC,DPB2LN ; Default, DPB size for CP/M 2.2 ; DPBLN EQU $-2 ; LDIR LD DE,(DSM) ; Get number of last group CALL GRP2SC ; Find out what track and record it is in LD (MAXTRK),DE ; Save the maximum track number LD HL,(PHYREC) ; This logic will tell LD A,H ; If first record OR L ; Is physical 0 or 1 LD (FIRST0),A RET ; ; Set DMA address in BC ; SETDMA: CALL V3CHEK ; Carry set if not 3.0 JR C,STDMA2 ; Skip if 2.2 LD (DMAADR),BC ; Do for CP/M 3 RET ; And return STDMA2: LD A,12 ; BIOS SETDMA function JP XBIOS2 ; Do it ; ; Set track # in DE ; SETTRK: PUSH HL ; Save HL LD HL,(MAXTRK) ; Verify within limits OR A SBC HL,DE ; Error if > maximum track # POP HL ; Restore HL in case error JP C,OUTLIM ; If too big, abort ; LD (CURTRK),DE ; Save desired track LD B,D ; BC=track number LD C,E PUSH HL ; Resave HL ; ; Is ok, so set track ; CALL V3CHEK ; Carry set if not 3.0 JR C,STTRK2 ; Do it 2.2 way LD (LOGTRK),DE ; Setup track for CP/M 3 JR STTRK3 ; Skip 2.2 stuff ; STTRK2: LD A,10 ; BIOS SETTRK function CALL XBIOS2 ; Do it ; STTRK3: POP HL ; Restore HL RET ; ; ; Set record number in DE ; SETREC: PUSH HL ; Save HL PUSH DE ; Save record # LD (CURREC),DE ; Store current record LD DE,(SYSTRK) ; Get number of sys trks LD HL,(CURTRK) ; Get current track OR A ; Clear carry for subtract SBC HL,DE ; See if in system tracks POP BC ; Restore record number to BD LD H,B ; And LD L,C ; To HL JR NC,NOTSYS ; If no carryy we're not in system tracks ; LD A,(FIRST0) ; See if first record is 0 OR A JP NZ,STREC1 ; No, jump away DEC HL ; Yes, so decrement requested JP STREC1 ; Then go ; NOTSYS: LD DE,(RECTBL) ; Point to XLT table DEC BC ; Decr record CALL V3CHEK ; Carry set if not 3.0 JR C,NOTSY2 ; Do it 2.2 way CALL RECTRN ; Else do CP/M 3 way (see note) JR NOTSY3 ; Skip 2.2 stuff ; NOTSY2: LD A,16 ; Bios RECTRAN function # CALL XBIOS2 ; Do it ; NOTSY3: LD A,(SPT+1) ; If SPT<256 (HI-ord = 0) OR A ; Then force 8-bit translation JP NZ,STREC1 ; Else keep all 16 bits LD H,A ; STREC1: LD (PHYREC),HL LD B,H LD C,L ; CALL V3CHEK ; Cy set if not 3.0 JR C,STREC2 ; Do it 2.2 way LD A,L LD (LOGREC),A ; Setup track for CP/M 3 JR STREC3 ; Skip 2.2 stuff ; STREC2: LD A,11 ; BIOS SETREC function # CALL XBIOS2 ; Do it ; STREC3: POP HL ; Restore HL RET ; ; Out of disk track limit ; OUTLIM: CALL PRINT DEFB CR,LF,BEL,'Track Error',0 JP 0000H ; ; Read next block into dma address ; READ: CALL V3CHEK ; Carry set if not 3.0 JR C,READ2 ; Skip if 2.2 CALL READ3 ; Do CP/M 3.0 way JR READ4 ; Jump over ; READ2: LD A,13 ; BIOS READ function CALL XBIOS2 ; Do it ; READ4: OR A ; Check status RET Z ; Return if ok ; CALL PRINT DEFB CR,LF,BEL,'Read Error',0 JP 0000H RET ; ; Write block in DMA address to disk ; WRITE: CALL V3CHEK ; Carry set if not 3.0 JR C,WRITE2 ; Skip if 2.2 CALL WRITE3 ; Do CP/M 3.0 way JR WRITE4 ; Jump over ; WRITE2: LD A,14 ; BIOS WRITE function CALL XBIOS2 ; Do it ; WRITE4: OR A ; Check for write error RET Z ; CALL PRINT DEFB CR,LF,BEL,'Write Error',0 JP 0000H ; ;----------------------------------------------------------------------- ; ; Test for CP/M 3.0, used by above routines ; V3CHEK: LD A,(VERFLG) ; Check for version 3.0 CP 30H ; Carry set if not 3.0 RET ; VERFLG: DEFB 0 ; Version number of CP/M ; ;----------------------------------------------------------------------- ; ; For CP/M 2.2, enter with BIOS function in A, eg., LD A,9 (DELDSK) ; XBIOS2: PUSH DE ; Save registers LD HL,(0001H) ; Warmboot address LD L,A ; BIOS function number to L LD E,L ; And to E LD D,0 ; Zero Dd ADD HL,DE ; Add so that HL ADD HL,DE ; Points to BIOS function address POP DE ; Restore DE JP (HL) ; Do it and return to call address ; ;----------------------------------------------------------------------- ; ; BDOS subroutines ; CONST: LD C,CONSTF JP BDOS ; CONIN: LD C,CONINF JP BDOS ; CONOUT: LD C,CONOUF JP BDOS ; CONBUF: XOR A ; Zero the character buffer LD (INBUF+2),A LD C,RDCBUF LD DE,INBUF JP BDOS ; ;----------------------------------------------------------------------- ; ; Used by some SYSLIB routines. (Modified - SYSLIB used BIOS.) ; COUT: PUSHRG ; Save registers PUSH AF LD E,A CALL CONOUT POP AF POPRG ; Restore registers RET ; ;----------------------------------------------------------------------- ; ; Start of SYSLIB routines (by Richard Conn). ; NOTE - some of these have been modified to Z80 code. ; ; Print register 'A' as decimal characters in N-character field floating ; print, where 1-3 chars are used ; PAFDC: PUSH BC ; Save registers PUSH DE PUSH AF ; Save 'A' LD D,1 ; Turn on leading sapce flag ; ; Print routine ; LD B,100 ; Print 100's CALL PAC ; Print a character LD B,10 ; Print 10's CALL PAC ADD A,'0' ; Convert to ASCII CALL COUT ; Print POP AF ; Restore 'A' POP DE ; Restore registers POP BC RET ; ; Print result of division of 'A' by 'B' with leading space (integer ; division) ; PAC: LD C,0 ; Set count ; PACL: SUB B ; Compute count JR C,PACD INC C ; Increment count JR PACL ; PACD: ADD A,B ; Add 'B' back in LD E,A ; Save 'A' LD A,C ; Get count OR A ; Zero? JR NZ,PACD1 OR D ; 0 means no leading space JR Z,PACD1 ; (A=0, A or D = 0 means D=0) LD A,E ; Restore 'A' RET ; PACD1: LD D,0 ; D=0 for no leading space LD A,C ; Get count ADD A,'0' ; Convert to decimal CALL COUT ; Print it LD A,E ; Restore 'A' RET ; ; Changed following to utilize some Z80 code ; ; Print HL as decimal characters in N-character field floating print, ; where field size is from 1 to 5 characters ; PHLFDC: PUSH AF ; Save all registers PUSH BC PUSH DE PUSH HL LD B,1 ; B=1 for leading ; ; Print HL using leading space flag in 'B' ; LD DE,10000 ; Print 10000's CALL PHDC1 LD DE,1000 ; Print 1000's CALL PHDC1 LD DE,100 ; Print 100's CALL PHDC1 LD DE,10 ; Print 10's CALL PHDC1 LD A,L ; Print 1's ADD A,'0' ; Convert to ASCII CALL COUT POP HL ; Restore all registers POP DE POP BC POP AF RET ; ; Divide HL by DE and print quotient with leading spaces ; PHDC1: LD C,0 ; Set count ; PHDC2: OR A ; Clear carry for Z80 way SBC HL,DE ; Subtract DE from HL with borrow JR C,PHDC3 ; Done if carry set (further borrow) INC C ; Increment count JR PHDC2 ; PHDC3: OR A ; Clear carry ADC HL,DE ; Add DE to HL with carry Z80 way LD A,C ; Get result OR A ; Check for zero JR NZ,PHDC4 OR B ; 0=no leading space RET NZ ; (A=0, A or B = 0 means B = 0) ; PHDC4: LD B,0 ; Turn off leading space LD A,C ; Get value ADD A,'0' ; Convert to ascii JP COUT ; ; ; Print FCB file name and type pointed to by DE on console: Format of ; output: xxxxxxxx.yyy Modified. (8 chars and/or spaces, period, 3 ; characters and/or spaces) ; PFN1: PUSH DE ; Save registers PUSH BC PUSH AF LD B,8 ; 8 characters first CALL PRFNX LD A,'.' ; Dot CALL COUT LD B,3 ; 3 more characters CALL PRFNX POP AF ; Restore registers POP BC POP DE RET ; PRFNX: LD A,(DE) ; Get character AND 7FH ; Mask out MSB CALL COUT ; Print it INC DE ; Point to next DJNZ PRFNX ; Loop until done RET ; ; Clear the CRC accumulator ; CRCCLR: PUSH HL LD HL,0 ; Set CRC to zero LD (CRCACC),HL POP HL RET ; ; Update the CRC accumulator. This routine must be called once for each ; byte in the byte stream for which the CRC is being calculated. ; ; Input parameters: A = byte to be included in CRC ; CRCUPD: PUSH AF ; Save all registers PUSH BC PUSH HL LD B,8 ; Rotate 8 bits LD C,A ; Byte in C LD HL,(CRCACC) ; HL=old CRC value ; UPDLOP: LD A,C ; Rotate HLC as a 24-bit ACC left 1 bit RLCA LD C,A LD A,L RLA LD L,A LD A,H RLA LD H,A JR NC,SKIPIT LD A,H ; The generator is x^16 + x^12 + x^5 + 1 XOR 10H ; As recommended by CCITT. LD H,A ; An alternate generator which is often LD A,L ; Used in synchronous protocols XOR 21H ; Is x^16 + x^15 + x^2 + 1. this may be LD L,A ; Used by substituting xor 80h for xor 10h ; And XOR 05h for XOR 21h in the adjacent code. SKIPIT: DEC B ; Count down 8 bits JR NZ,UPDLOP LD (CRCACC),HL ; Save new CRC value POP HL ; Restore all POP BC POP AF RET ; ; Complete the CRC calculation. Called after the last byte of the byte ; stream has passed through CRCUPD. It returns the calculated CRC bytes ; in HL. ; ; Output parameters: HL = calculated CRC bytes ; CRCDON: PUSH AF ; Save 'A' XOR A ; Send out 2 zeroes CALL CRCUPD CALL CRCUPD LD HL,(CRCACC) ; Return CRC value in HL POP AF RET ; ; Print HL as 4 hex characters, no registers are to be affected ; IF DEBUG ; For dumping hex values PHL4HC: PUSH AF ; Save 'A' LD A,H ; Print H CALL PA2HC LD A,L ; Print L CALL PA2HC POP AF ; Restore 'A' RET ; ; Print regiaster A as 2 hex characters on console ; PA2HC: PUSH AF ; Save 'A' PUSH AF RRCA ; Exchange nybbles RRCA RRCA RRCA CALL PAHC ; Print low-order nybble as hex POP AF ; Get a CALL PAHC ; Print low-order nybble as hex POP AF ; Restore a RET ; PAHC: AND 0FH ; Mask for low nybble CP 10 ; Letter or digit? JR C,PADIG ; Digit if carry ADD A,'A'-10 ; Convert to 'a'-'f' JP COUT ; Print ; PADIG: ADD A,'0' ; Convert to '0'-'9' JP COUT ; Print ENDIF ; ;----------------------------------------------------------------------- ; ; Start of the CP/M 3.0 specific subroutines for disk I/O. Extracted ; from CPM22E BIOS program, and slightly modified. This portion most ; probably could be utilized in almost any CP/M 2.2 program which uses ; the BIOS for disk I/O. ; ; Translate records. Records are not translated yet. Wait until we ; know the physical record number. This works fine as long as a program ; trusts the BIOS to do translation. For programs that directly access ; the XLAT table to do their own translation, this may give wrong idea ; about disk skew but it shouldn't cause harm. ; RECTRN: LD L,C ; Return record in HL LD H,B RET ; ; Read the selected CP/M record ; READ3: LD A,1 LD (READOP),A ; Read operation INC A ; A=2 (WRUAL) LD (WRTYP3),A ; Treat as unallocated JP ALLOC ; Perform read ; ; Write the selected CP/M record ; WRITE3: XOR A LD (READOP),A ; Not a read operation LD A,C LD (WRTYP3),A ; Save write type CP 2 ; Unallocated block? JP NZ,CHKUNA ; ; Write to first record of unallocated block ; LD A,(BLM) ; Get block shift mask INC A ; Adjust value LD (UNACNT),A ; Unalloc record count LD A,(LOGDSK) ; Set up values for LD (UNADSK),A ; Writing to an unallocated LD A,(LOGTRK) ; Block LD (UNATRK),A LD A,(LOGREC) LD (UNAREC),A ; CHKUNA: LD A,(UNACNT) ; Any unallocated records OR A ; In this block JP Z,ALLOC ; Skip if not DEC A ; --UNACNT LD (UNACNT),A LD A,(LOGDSK) LD HL,UNADSK CP (HL) ; LOGDSK = UNADSK ? JP NZ,ALLOC ; Skip if not LD A,(LOGTRK) CP (HL) ; LOGTRK = UNATRK ? JP NZ,ALLOC ; Skip if not LD A,(LOGREC) LD HL,UNAREC CP (HL) ; LOGTRK = UNASEC ? JP NZ,ALLOC ; Skip if not INC (HL) ; Move to next record LD A,(HL) LD HL,SPT ; Address of SPT CP (HL) ; Record > SPT ? JP C,NOOVF ; Skip if no overflow LD HL,(UNATRK) INC HL LD (UNATRK),HL ; Bump track XOR A LD (UNAREC),A ; Reset record count ; NOOVF: XOR A LD (RSFLAG),A ; Do not pre-read JP RWOPER ; Perform write ; ALLOC: XOR A ; Requires pre-read LD (UNACNT),A INC A LD (RSFLAG),A ; Force pre-read ; RWOPER: XOR A LD (ERFLAG),A ; No errors yet LD A,(PSH) ; Get physical shift factor OR A ; Set flags LD B,A LD A,(LOGREC) ; Logical record LD HL,(HSTBUF) ; Addr of CP/M 3 record buffer LD DE,128 JP Z,NOBLK ; No blocking EX DE,HL ; Shuffle registers ; SHIFT: EX DE,HL RRCA JP NC,SH1 ADD HL,DE ; Bump buffer address ; SH1: EX DE,HL ADD HL,HL AND 07FH ; Zero high bit DJNZ SHIFT EX DE,HL ; HL=buffer address ; NOBLK: LD (RECHST),A LD (RECBUF),HL LD HL,HSTACT ; Buffer active flag LD A,(HL) LD (HL),1 ; Set buffer active OR A ; Was it already? JP Z,FILHST ; Fill buffer if not LD A,(LOGDSK) LD HL,HSTDSK ; Same disk ? CP (HL) JP NZ,NMATCH LD A,(LOGTRK) LD HL,HSTTRK ; Same track ? CP (HL) JP NZ,NMATCH LD A,(RECHST) ; Same buffer ? LD HL,HSTREC CP (HL) JP Z,MATCH ; NMATCH: LD A,(HSTWRT) ; Buffer changed? OR A CALL NZ,WRTHST ; Clear buffer ; FILHST: LD A,(LOGDSK) LD (HSTDSK),A LD HL,(LOGTRK) LD (HSTTRK),HL LD A,(RECHST) LD (HSTREC),A LD A,(RSFLAG) ; Need to read ? OR A CALL NZ,REDHST ; Yes XOR A LD (HSTWRT),A ; No pending write ; MATCH: LD DE,(DMAADR) LD HL,(RECBUF) LD A,(READOP) ; Which way to move ? OR A JP NZ,RWMOVE ; Skip if read LD A,1 LD (HSTWRT),A ; Mark buffer changed EX DE,HL ; Hl=dma de=buffer ; RWMOVE: LD BC,128 ; Byte count LDIR ; Block move LD A,(WRTYP3) ; Write type CP 1 ; To directory ? JP NZ,RWEXIT ; Done LD A,(ERFLAG) ; Check for errors OR A JP NZ,RWEXIT ; Do not write dir if so XOR A LD (HSTWRT),A ; Show buffer written CALL WRTHST ; Write buffer ; RWEXIT: LD A,(ERFLAG) RET ; ; Disk read, call bios to fill the buffer with one physical record ; REDHST: CALL RWINIT ; Initialize CP/M 3.0 BIOS LD A,13 ; BIOS READ function JP DORW ; Go do it ; ; Disk write, call BIOS to write one physical record from buffer ; WRTHST: CALL RWINIT ; Initialize CP/M 3.0 BIOS LD A,14 ; BIOS WRITEW function ; ; Call BIOS to read (or write) 1 physical record to (or from) buffer. ; DORW: CALL XBIOS3 ; Go do it to the record LD (ERFLAG),A RET ; ; Read/write initialization routine does the following: Translate re- ; cord, set track, record, DMA buffer and DMA bank. ; RWINIT: LD HL,(HSTTRK) ; Get physical track number LD (BCREG),HL ; Put track number in BC LD A,10 ; BIOS SETTRK function CALL XBIOS3 ; LD A,(HSTREC) ; Get physical record number LD L,A LD H,0 LD (BCREG),HL ; Put record number in BC LD HL,(RECTBL) ; Address of XLAT table LD (DEREG),HL ; XLAT address in DE LD A,16 ; BIOS RECTRN function CALL XBIOS3 ; Get skewed record # ; LD A,L LD (ACTREC),A ; Actual record LD (BCREG),HL ; Record number in BC LD A,11 ; BIOS SETREC function CALL XBIOS3 ; Set CP/M 3.0 record ; LD HL,(HSTBUF) ; Address of CP/M 3.0 record buffer LD (BCREG),HL ; Buffer address in BC LD A,12 ; SETDMA function CALL XBIOS3 ; LD A,1 ; DMA bank number LD (AREG),A ; Put bank number in 'A' LD A,28 ; BIOS SETBNK function CALL XBIOS3 ; Set DMA bank RET ; ; ; Under CP/M 3.0, direct bios calls via the bios jump vector are only ; supported by the bios console i/o and list functions. You must use ; bdos function 50 to call any other bios function. Store appropriate ; registers in the bios parameter block [eg: ld (bcreg),bc] and enter ; this routine with the desired bios function number in register a. ; ; All CP/M 3.0 disk I/O calls are made through here ; XBIOS3: LD (BIOSPB),A ; Set BIOS function LD C,50 ; Direct BIOS call function LD DE,BIOSPB ; BIOS parameter block JP BDOS ; Jump to BDOS ; ;----------------------------------------------------------------------- ; ; Temporary storage area for the program ; DATA EQU $ ; STATS: DEFS 1 SGPCTR: DEFS 2 SWPCTR: DEFS 2 GRDCTR: DEFS 2 GWRCTR: DEFS 2 DIRWCT: DEFS 2 ; GRBUF1: DEFS 2 GRBUF2: DEFS 2 BLS: DEFS 2 DIRBYT: DEFS 2 DIRCTR: DEFS 2 DIRACT: DEFS 2 DIRGRP: DEFS 2 SRCHGP: DEFS 2 BADGRP: DEFS 2 GRPALL: DEFS 1 GRPNUM: DEFS 1 FILPTR: DEFS 2 ALLPTR: DEFS 2 GRPTR: DEFS 2 WRTYP: DEFS 2 WRFLG: DEFS 2 GRPCTR: DEFS 1 RECCTR: DEFS 1 DIRCHG: DEFS 1 ; CRCACC: DEFS 2 ; Accumulator for CRC value (SYSLIB) CRCFLG: DEFS 1 CRCVAL: DEFS 2 CRCBEG: DEFS 2 CRCTRK: DEFS 2 CRCREC: DEFS 2 ; TEMPC: DEFS 1 ; Used in "GRP2SC" ; ASCDSK: DEFS 1 ; ASCII drive number FIRST0: DEFS 1 ; Sets 0 if first record number is 0 FILECT: DEFS 2 ; File count CURTRK: DEFS 2 ; Current track number CURREC: DEFS 2 ; Current record number PHYREC: DEFS 2 ; Current physical record number MAXTRK: DEFS 2 ; Maximum track number ; ; Next 5 for low memory or high DRM ; NEWMAX: DEFS 2 ; New faked memory directory capacity FAKDRM: DEFS 2 ; New faked DRM value BLMSIZ: DEFS 2 ; K size of blocks NEWGRP: DEFS 1 ; New faked number of directory groups STRTGP: DEFS 2 ; Starting group in case faked ; DIRECT: DEFS 2 ; Pointer to directory buffer ; ;----------------------------------------------------------------------- ; ; Data area common to program and CP/M 3.0 subroutines ; RECTBL: DEFS 2 ; Pointer to record XLT table ; DPB: ; DPB for CP/M 2.2 & 3.0 SPT: DEFS 2 ; Copy of disk parameter block BSH: DEFS 1 BLM: DEFS 1 EXM: DEFS 1 DSM: DEFS 2 DRM: DEFS 2 AL0: DEFS 1 AL1: DEFS 1 CKS: DEFS 2 SYSTRK: DEFS 2 PSH: DEFS 1 ; Physical shift count (CP/M 3.0) PHM: DEFS 1 ; Physical record mask (CP/M 3.) ; BIOSPB: DEFS 1 ; BIOS function AREG: DEFS 1 ; A register BCREG: DEFS 2 ; BC registers DEREG: DEFS 2 ; DE registers HLREG: DEFS 2 ; HL registers ; DMAADR: DEFS 2 ; Last DMA address LOGDSK: DEFS 1 ; Logical disk number LOGREC: DEFS 2 ; Logical recort number LOGTRK: DEFS 2 ; Logical track number HSTBUF: DEFS 2 ; Address of CP/M 3.0 record buffer ; ;----------------------------------------------------------------------- ; ; Data/variable storage area for CP/M 3.0 subroutines ; HSTDSK: DEFS 1 ; Physical disk number HSTTRK: DEFS 2 ; Physical track number HSTREC: DEFS 1 ; Physical record number ; ACTREC: DEFS 1 ; Skewed physical record RECHST: DEFS 1 ; Temp phyical racord HSTACT: DEFB 0 ; Buffer active flag, initial 0 HSTWRT: DEFB 0 ; Buffer changed flag, initial 0 ; UNACNT: DEFS 1 ; Unallocated record count UNADSK: DEFS 1 ; Unallocated disk number UNATRK: DEFS 2 ; Unallocated track number UNAREC: DEFS 1 ; Unallocated record number RECBUF: DEFS 2 ; Logical racord address in buffer ; ERFLAG: DEFS 1 ; Error reporting RSFLAG: DEFS 1 ; Force record read READOP: DEFS 1 ; 1 if read operation WRTYP3: DEFS 1 ; Write operation type ; ;----------------------------------------------------------------------- ; ; Start of stack and buffers ; DEFS 40 ; Min stack depth (20 levels) EVEN EQU ($+255)/256*256 ; Start buffer on even page ORG EVEN ; Also increases stack greatly STACK EQU $-2 DATSIZ EQU $-DATA ; Size of data area ; Buffers start here ; END  -READ.ME Files included in this LBR file are a combination of the original plus new files. You may want to read the original files for RESTORE from which this program, FiRe, was created. It may give you a bit of back- ground information. DMAP should be usable under CP/M 2.x but it is untested under CP/M 3.0. I doubt if it would cause any harm under CP/M 3.0 but I do not think that it would work properly, especially if a system has a DRM (max num- ber of directory entries) of 2048, as is the case with my system, a Morrow Hard Disk system. Originally SAP (Sort And Pack directory), was included in the .LBR file but it was a version for CP/M 1.4 or 2.2. I have replaced it with the source and documentation for SAPP v20 which is good for CP/M 2.2 or CP/M 3.0 (if using a Z80 CPU). SAPP will not work under CP/M 1.4 or for any non-Z80 CPU's. Source, documentation, and a preconfigured COM file (not that there is much to be configured), for FiRe are contained in this LBR. Be sure to read the documentation, and, as with any "new" program that "works" on the disk, be sure to use on a backed-up copy if its a floppy, or backup the hard disk first if doing it there. Reminder: the program can take TIME if using on a HARD DISK. On my Morrow CP/M 3.0 hard disk system, it took well over five hours. George F. Reding !9" <":2*"""""""4"8"C Disk Mapping Utility, Version 1.0 !2aa:U:U~>-w> 2}*#}¹|}Ez{ʬ ~Ü ~|6~> >(w~͊#a>.wa:w~͊~> 0>)wNC ++ Free ++ :2h>:w~# o~unxwa*#"!"*~~_#~:ʭW#9*|>*2"® *+"|*!""#""Oy2|^#V#"!~#fo**|2lC++ READ Failed, Sector may be Invalid ++ >>w:ʉx͊y͓ ڜ0w2'  d   }0w}o|g:2z0w> w>|wC+++ Out of memory +++ *#*͵=*#*͵6"!"~UXwXX#~D#*"!"*#MMDM*BK:=3**6! x~3}o|gBKx! T]x ~# x*""*͵6bk:)=*E% ** #yC#x}/o|/g#ɷ|g}o\ W:2:\=2O > w:€ü ҟ ʟ °y °>2ü ڼ:<2*͵"BK*"*͵`i:+* :g"DMC++ Internal Error - Track Out Of Bounds ++ ͭOͷ̷C Type Any Character to Continue or ^C to Abort - ͷʥaè*> > O*. *}$.DMAP.DOC - Documentation for DMAP.COM Description: This is an "un-ZCPR3'd" version of DU3 with only the 'M' command left in. The problem with DU3 is that it is so large that it runs out of memory trying to produce an allocation map of my 20 Meg ST225. DMAP will print the disk's map on your terminal. Use: Simply type 'DMAP'. The disk's allocation map will be printed on the terminal one page at a time. You may type Control-C at the end of any page to abort the program. Installation: The program is set up for 24-line terminals. If your terminal has a different number of lines, change the byte at address 104h (just after the initial jump) to the (hex) number of lines for your terminal. ld bc,0SECDIV: inc bc ;divide sector # by SPT to get t A bug was discovered in the original release of RESTORE.LBR. This library now contains corrected versions of RESTOREI and RESTOREZ. The problem was that they did not correctly handle small drives, specifically those with DSM < 256. This value is typically found on SSSD floppies (and some 40 track DS 5-1/4" floppies) and on small to medium RAM disks. The symptoms of the problem are that, after running RESTORE, the files are all still on the disk, but they are only ONE allocation group long, no matter what the original length was. However, the size reported by DIR, XDIR, or CRC will match the length of the original file! In addition, if you dump out an allocation map of the disk after RESTOREing it, you will find the files listed, in the proper order, at the beginning of the map, then (usually, but not always), some ++Free++ allocation groups, then more pieces of the files that are listed at the start of the map (if they are more than one group long), in the same locations as before you RESTOREd the disk. All that is fixed now, at least on the disk sizes and formats I have available to test. Thanks to John Lorimer of Denver for reporting the problem, and especially for providing mapping info that let me track down the problem quickly. If anyone else has any other problems, particularly if your disk format is unusual, send bug reports to the BBS's listed below, or call direct. Happy RESTOREing! Steve Dirickson 4 February 1987 21145 Raintree Place NW Poulsbo WA 98370-9726 Voice: 206-697-1270/9311 BBS: Seattle's 'downspout': 206-325-1325 ZNode Central: 415-489-9005  RESTORE.DOC - Documentation for RESTORE.COM 31 January 19871 Steve Dirickson Description: ----------- RESTORE is a utility program that improves disk performance by regrouping the allocation groups assigned to each file on the disk so that each file is allocated to sequential sectors on the disk i.e., 'restoring' the disk to the condition it was in when it was new. Discussion: ---------- CP/M-compatible operating systems store files on disk in what are called "allocation groups." The size of an allocation group is determined by the system's BIOS, and is typically 1k or 2k bytes for floppy disks, and 2k, 4k, or 8k for hard disks. The allocation group is the smallest unit of storage on the disk; if you write a one-byte file to the disk, it will take up just as much space (in the sense of making that space unavailable for storing other files) as would a file the size of one entire alloca- tion group. When a disk is formatted, all allocation groups are free, and the first file written to the disk occupies the allocation groups immediately after the directory, in sequence. The next file written to the disk occupies the groups after the first and so on. When a file is deleted, its allo- cation groups are freed up, and may be reused by the system. As files are written and erased, later files start to become 'fragmented' i.e., they are written with some allocation groups in one location and other groups in other locations. Thus, when these 'fragmented' files are read or written, the system must position the read/write head over one track for part of the file, then move the head to another track for more of the file. This starts to degrade disk performance, because more and more time is spent moving the head around to find the various parts of the file, causing access times for the files to get longer and longer. This problem affects both floppy and hard disks. The effect this has on the system response is dependent on how the system buffers the disk data in memory. A system that uses a 'track buffer' system, where an entire track is saved in memory each time the disk is read, will be VERY much slower when accessing severely fragmented files than when the files are stored in sequential allocation groups and thus several related groups are kept in memory together. Similarly, a system that uses multiple- record disk I/O will be seriously degraded by disk fragmentation. Con- versely, an unbuffered BIOS, and especially an unblocked BIOS, will be less affected, because latency (the time the controller has to wait for the desired record to rotate under the head) is more significant in com- parison to track stepping times. The only way to recover from this situation has been to copy all files on the disk to backup disks, reformat the disk (or simply erase all the files), and then recopy the files from the backup disk(s) to the working disk. This takes a significant amount of time to do, and it is even more inconvenient if files are assigned to several user areas on the disk, since each user area on the backup disk must be individually entered and the files in that area copied to the corresponding area on the work disk. RESTORE is designed to make this process easier by eliminating the copy to and from the backup disks. RESTORE works only on the disk being re- stored, and thus may be used even in a single-disk system. ***WARNING*** Since RESTORE does in-place restoration of the disk, it has INCREDIBLE potential for wrecking havoc with your disk if something goes wrong while it is working, like a loss of power or a controller fault. Therefore, you should ALWAYS back up the disk to be RESTOREd shortly before running the program. This shouldn't be much of a problem, since you do a full back up of your hard disk at least every weekend, right? RIGHT? NO?!! Well, if you don't, you should. It only takes one occurrence of having to rebuild a disk's directory one group at a time to make you a believer. Just after a full backup is the optimum time to run RESTORE. Use: --- NOTE: You MUST sort the directory of the disk to be RESTOREd before running RESTORE, by running SAP, SAPP, CLEANDIR or some similar program. If RESTORE finds that the directory is not sorted, it will tell you so and quit. (SAP.COM, version 5.0 is included in this .LBR). Also note that some older versions of CLEANDIR, SAP, SAPP, etc., did not properly sort the directory because they did not include the last directory entry on the disk in the sort. RESTORE WILL find this entry, and will tell you that your directory is not sorted. You can use DUU, DU3, PATCH or your own favorite disk utility to examine the directory of the disk to see what is wrong. Once you have the disk's directory sorted, simply start the program by typing 'RESTORE'. The program will sign on, and ask you to change disks if you desire. This allows single-disk users to place the disk contain- ing the program in the drive, start the program, and then swap in the disk to be restored. This means you don't have to copy the program to each disk you want to restore. Note that RESTORE --ALWAYS-- works on the user's default disk i.e., the one you were on when you invoked the program. So, if you want to restore the disk in drive B but RESTORE is on the disk in drive A, you MUST log onto disk B and then call the program from drive A: A>B: B>A:RESTORE RESTORE reads in the directory of the default disk, analyzes the direc- tory, and prints some information; how many directory entries are on the disk, how many groups are allocated, how many groups have to be swapped, and how many times the directory will be rewritten. This last is sig- nificant since 1) directory writes usually take much longer than other writes, because most BIOSs immediately write their buffer to the disk after a change in the directory, rather than waiting until the user wants to write somewhere else, and 2) the directory takes up several allocation groups, and the entire directory is rewritten after each directory entry has been fixed. Note that the number of directory entries reported by RESTORE will probably not match the number of files on the disk, since each extent of a file takes up its own directory entry. After RESTORE tells you what it is going to have to do to the disk, it asks you to type 'Y' to restore the disk, or anything else to abort. You may enter an upper or lower case Y, or even type CTL-Y. Anything else will cause RESTORE to abort without doing anything to the disk. After you type 'y', RESTORE will start to fix your disk. It prints the name of the directory entry is is working on, so you can keep track of how it is doing. You may type a CTL-C at any time, and RESTORE will a- bort after it finishes the current directory entry, and tell you how many directory entries are left over. Left to itself, RESTORE will finish the disk, then tell you it is finished and terminate. If you are using a system that does not reload the directories from disk on each warm boot, like ZRDOS version 1.6 and later, you will need to do whatever your system needs (run DISKRST in the case of ZRDOS) to restore the system's directory to match the one on disk. Remember, RESTORE works thorough the BIOS, so the DOS has no idea what is going on. System Requirements: ------------------- Operating System - A CP/M-compatible operating system that supports con- sole input and output and the following direct BIOS function calls: Select Disk Set Track Set Sector Set DMA Address Disk Read Disk Write Sector Translation These are the only direct BIOS calls used by RESTORE. Console I/O is done via the BDOS, so you may use the CTL-P to echo the output to your printer. If you do so, you may need to install the patch discussed be- low. Processor - Versions are provided for both 8080/8085-compatible proces- sors (RESTOREI.COM) and for Z80-compatible processors (RESTOREZ.COM). The Z80 version is about 5% smaller and runs about 1% faster than the 8080 version. Memory - Depends on your disk system. The largest disk directory RESTORE can handle is 1024 entries (DRM = 1023 for you operating system hacker types). This size disk typically uses 4k byte allocation groups. With this type of disk, RESTORE requires a 44k TPA. This is the largest TPA requirement. Other disk sizes can be restored in less TPA. A SSSD floppy limited to 64 directory entries using 1k allocation groups can be RESTOREd in 8k of TPA. Note that RESTORE is NOT a ZCPR3 utility. I have tried to make it as nearly universal as possible. It does not use cursor-positioning se- quences for the terminal, or require any installation. The only thing your terminal has to be able to do is receive and display standard ASCII characters, including being able to do a carriage return without a line- feed. This is used in the display of the files being processed. The same line is rewritten as each new file is started. If your terminal can't do a carriage return without a line feed (or makes a mess doing so, like a TTY), or if you want to echo the output of the program to your printer without wearing a hole in the paper, install the following patch: Patches: ------- To make the file display do a line feed as well as a carriage return, use your debugger or disk utility to look at the word at address 103h (just after the jump at the start of the program). This word is the address of the location in the file display string where you can insert a line feed character (0ah). Put a LF there and write the modified file back to disk. The other patch option is the flag that controls the disk-change wait. As released, the program will print a message telling you to change disks if necessary, and press any key when ready. Then it waits for you to press any key. Later, after the statistics for the disk are printed, it asks you to press 'Y' to continue, or any other key to abort. If you don't want these waits, or you want to do unattended 'batch processing' of mul- tiple drives, you will need to patch this byte to zero. It is located just after the word described above, at address 105h. It is non-zero to have the program wait for input in these two situations, or zero to start processing immediately. The input for these two user waits is done using the BDOS 'Read Console Buffer' function, so ZEX or XSUB may be used to provide the input; note, however, that doing so will cost you about 3k of TPA, and may cause a memory shortage problem. Try it on your largest disk. If it works, you can have it both ways. Limitations: ----------- As discussed above, the maximum size disk RESTORE can handle is one with a limit of 1024 directory entries using 4K byte allocation groups (ac- tually, it will also handle this size disk with 8K allocation groups but only if you have a 51.5k TPA i.e., your BDOS starts at some address after CE00h. Since I am a believer in the 48k TPA convention, this is not the advertised maximum disk size). This is the typical configuration for most popular CP/M hard disks (10-30 Mb). If you have a drive with a DRM of 2047, RESTORE will not run. I'm not really sure why people make disks like this, since, with CP/M's limit of 8 Mb per logical disk, this means that there is room for one directory entry for each allocation group on the disk!. That's a lot of lost directory space. Since it would take 64k simply to read in the directory of such a disk, RESTORE will tell you that there is not enough memory and quit. I have chosen not to use the incremental-directory-read technique that was added to CLEANDIR and SAP for such disks, since, to remain within the 48k TPA boundary, this would only increase the maximum number of directory entries from 1024 to 1152. One of the problems associated with a program of this type is the hand- ling of bad sectors on the disk. Since there is no standard way of marking bad sectors, I have completely ignored the issue in writing the program. I recommend the following system for those with bad sectors on their hard disk: 1) You have probably used BDxx, FBAD, or whatever you use to mark the bad sectors on the disk. Use DUU, DU3, PATCH, or your own disk utility to examine the disk's directory. Note the allocation groups that are assigned to the '.BAD' (or however your utility marks them) files. Make a list of the allocation groups containing the bad sectors. 2) After you finish backing up your disk (that IS when you use RESTORE, isn't it? See the WARNING above if not), run RE- STORE, then use your disk utility to look at the disk's di- rectory and see what files are now assigned to the allocation groups on your list of bad groups. Make a list of these files. 3) Erase the files on the list. 4) Rerun FBAD, etc., to re-mark the bad wgroups. 5) Copy over the files on the list from your backup disk. I realize that this is somewhat cumbersome, and I apologize for that. But, since there is no sandardized method for handling bad sectors, this this is the best I have been able to come up with. Note: When the discussion above talks about 'using your disk utility' to examine the disk's directory, you may use the DMAP program included with RESTORE instead. See the DMAP.DOC file for information on this u- tility. Simply type 'DMAP' and look for the filenames or allocation groups you want to scroll by. Operation: --------- RESTORE uses a brute-force method to relocate allocation groups. It reads in the directory (which MUST be sorted), and decides from the di- directory allocation data in the Disk Parameter Block for the disk how many groups are allocated to the directory. The next sequential alloca- tion group should be assigned to the first file in the directory. RE- STORE looks to see if that is the case. If not, it scans the directory to find what file has that group allocated to it. If the desired group is not allocated, RESTORE copies the group referenced in the directory into the desired group, then changes the directory entry to show the new group. If the group is allocated to another file, RESTORE reads both groups into memory, then writes them to the alternate locations, effect- ively swapping the groups in place. Both directory entries are modified, the next sequential allocation group is selected, and the process re- peats. The updated directory is written to the disk after each directory entry is completed, not after each allocation group swap. Thus, the number of directory rewrites will typically be about one-half to one- fourth (on a hard disk; one-fourth to one-eighth on a floppy disk) of the number of group swaps required. When I say that the method is 'brute force', I mean that no intelligence is used in the sort process. Under worst-case conditions, the first al- location group on a disk might be free, with all other groups assigned in the desired sequence to the files in the directory. In this case the disk is really not fragmented and nothing needs to be done. If you run RESTORE on a disk in this situation, it will tell you that EVERY GROUP on the disk must be moved, and will do so if you let it. No analysis of the fragmentation of the disk is done, and no optimization is used to figure out how to restore the disk using the least number of group swaps. While possible, the code to do this would make the program long enough that it might not be able to process a 1024-entry directory. On a more basic level, it would take me longer to write the optimization than I feel is reasonable. RESTORE works, and works well. The machine does not in the least mind moving the same allocation group three or four times. On the subject of speed: RESTORE is, in addition to being more conven- ient than copying all the files back from a backup disk, considerably faster than that method. While times vary depending on the degree of fragmentation and the hardware, I have found RESTORE to be about 20% faster than a NON-VERIFYING copy of files from backup floppies back to my hard disk. RESTORE does read-after-write CRC verification of all disk writes. Plus, you don't have to sit around and feed your machine floppies for hours. RESTORE will process a filled 20 Meg hard disk in about 5 hours on my hardware; doing the same thing, with verification, from floppies takes over 9 hours and about 28 disk swaps! With RESTORE, simply use a multiple-command line or SUBMIT file (DON'T use ZEX or XSUB if you have a big hard disk) and let RESTORE crunch away over night, or while you are out shopping for a computer for your wife (or husband). Note that if you want to use this 'batch processing' of multiple drives, you must patch the disk-change flag to zero as described above to disable the disk-change wait. DISCLAIMER: ---------- This is a very useful utility, and I have tested it extensively. How- ever, as noted above, it is also very capable of lunching your disks completely if the system has a problem. Use it in good health and hap- piness, but MAKE BACKUPS BEFORE YOU USE IT!!. Papa Wallenda ran his system without a backup. He's not around any more. 'Nuff said. Steve Dirickson 21145 Raintree Place NW Poulsbo WA 98370-9726 Voice: 206-697-1270/9311 BBS: Seattle's 'downspout': 206-325-1325 ZNode Central: 415-489-9005 %SAPP v20 08/02/87 GFR/!; }2"|!!!6w7͢8 ">K!"S*%"QRDM*S1͢8 ! "!"ͮi͇ͼ͞r!:\=2q28 ͢8S:> 0:qO> ͩ|(^#V+"^! ~#fo@*Q[SR"U*I#|:#2*GR8 \[UR*U z+"G:$!J*G![Q:[!Ç:]!!>2b*M"r͢ҟKr> ͩ!"`*G#"W*S"͢K> ͩ[`*@R0*r#"r͢Kr> ͩ!Z:b͢8͸ > ͩ B'͢8$>ͩ **"*W+"W|¼!"͢K> ͩ!#"`DM*^ ͢8͵t>ͩD}2t͢:tO> ͩ!"Y~ʙê6TR6[G*Y#Rڊɯ2]!/*Y#"o*S"e! "c*o"o}*Y#R"c!"g"e- x"i"m"k~G]# x![eSeSg*cR*m[i8>2]~w# xi*o|/W}/_*g:#"g[k*i{ozg"k!"Y ~0 2[#~$#~$#:[w+6$+6$+6$*Y#[GRڡ)))))[S~_#'**d* *}0FR8 ,Zy y0F_ YBKxk! T]x( nR8{7RښÜ `i:0*o]i`>2<2+2y2:C<2}:q2~:r2:t2:}+=2}:q!~+:r+:t!+4~!@$*#"2232}<22:OG:t*>VO)2z"!{~6ʋ:q!u„:r!v„:z!xʨ:|:q2u*r"v:z2x:2|[*:¼>2|::2|:> >02*v"8> 0:xo&"8*^":>0}2y"8> 0*>"8> 0>27>02626 Z80 needed If MORE than directory entries exist, please reduce entries and/or use DU first. Continue with MORE will destroy directory! Press 'Y' to continue, else aborts: Done Read, (Previously-sorted) Write,  Error Sort,  SAPP v 2.0 Documentation 08/02/87 Sort & Pack Directory for CP/M 2.2 and 3.0 By George F. Reding Based upon SAP v 44 With subroutines from CPM22E and SYSLIB ------------------------------------------------------------------------ NOTES A bug was found in v 1.9 when used under CP/M 2.2 where the DMA pointer was not being advanced, so the buffer under 2.2 was being overwritten, thus trashing the directory. The bug has been corrected by having moved 1 label and added another label, eg, DIRLOP & DIRLP1. Thanks to David Bowerman, the Sysop of Frog Hollow RCPM System (604-937-0906) for having located the cause of the problem. David then tested it on a disk with 250 of 256 directory entries used, under CP/M 2.2 and all is now fine. SAPP v1.8 clobbered the DE registers in the XBIOS2 routine for 2.2 which caused the SECTRAN function to not work under CP/M 2.2. My previous versions of this program for CP/M 3.0 only are also docu- mented in the source code of the program. This new version has improve- ments which I shall attempt to briefly describe. The program uses Z80 code wherever I could possibly make use of it, therefore, it can only be used on those systems which have a Z80 CPU. A simple test of Bob Freed's is used at the beginning of the program. Then the CP/M version number is obtained from the system and is stored in the program. This program uses BIOS functions for disk input/output and for such operations the version number is checked and appropriate routines are subsequently selected. Data areas are initially zero-filled, with an additional buffer area being set up if CP/M 3.0. If CP/M 3.0, the DPB offset and DPB lengths are set accordingly. The desired disk is selected and the translation table address and DPB is copied to the program. The program then tests the DRM value of the DPH, tests to see if there is enough TPA room to copy all of the direc- tory entries into the TPA. If not enough room then a calculation is made to determine the maximum number of entries that the TPA can hold, which is then stored in the program's copy of the DPB for its usage and a warning is then given to the user. (The warning may optionally be turned-off if the user providing that the user can remember to never ex- ceed the maximum number of entries that the program can handle. If the warning flag is set for no message, then the program will continue on its own.) This auto-faking of the DRM may occur when either the TPA is too low or the DRM is too large or both. It will work for CP/M 2.2 or 3.0 systems. ------------------------------------------------------------------------ CONFIGURATION Very few items are required to be set by the user in either source code or in the COM file in order to use this program. In the .COM file these user configurable bytes "should" be at the following locations (they follow the signon message that ends with my initials GFR and a 01A hex in case the .COM file was typed): 011B Clear screen code (6 bytes maximum) 0121 Null terminator for above 0122 CP/M 3.0 largest sector size (low/high) 0124 Normally E5 hex 0125 Normally 00 hex The byte at 011B is the clear screen code for your console. If you do NOT desire to clear your screen on program start, then set this first byte to 00 hex. Six bytes maximum may be used for your clear screen code. The byte at 0121 is a zero terminator for the clear screen. If using less than 6 bytes to clear your screen, then set any unused bytes to zero. At 0122 hex, is a word value for CP/M 3.0 systems. Set this to your largest sector size used on your system. The first byte of that word value is the low and the second byte is the high of the word, eg, for 1024 byte sectors the byte at 0122 is 00 hex and at 0123 is 04 hex. CP/M 2.2 users should just ignore this. The byte at 0124 hex should be E5 hex. The Morrow hard disk users (with the MD5, MD11, etc) should set it to 00 hex. It is the value for the RC field of the directory entries. On a Morrow hard disk computer, Morrow would set the RC field of each potential directory entry to 00 with the other bytes to the normal E5, when the hard drive is formatted. Whether it is checked by any of the Morrow utilities/programs or not is yet to be determined by me, so in order to prevent possible conflict with any possible Morrow program or utility, I have retained the oddity. Leave the byte at 0124 set to E5 hex if you are NOT using a Morrow hard disk computer. The byte at 0125 hex is the warning message flag. It should be 00 hex to enable the program to give its warning message when the TPA is low and/or the DRM is too large. Keep this byte set to zero unless your human memory is good and/or you are an experienced computer user. If it's set to a NON-zero the program will not give a message, and will continue as if everything was alright. ------------------------------------------------------------------------ USAGE It's not required to have this program on the drive on which its work is to be performed. Some examples are as follows, where this program is on drive A. A>SAPP do drive A A>SAPP B: do drive B A>SAPP C: do drive C B>SAPP do drive B (system uses search-path) B>A:SAPP B: do drive B (no system search-path) B>SAPP C: do drive C (system uses search-path) B>A:SAPP C: do drive C (no system search-path) ------------------------------------------------------------------------ WARNING MESSAGE OPTION If you encounter a warning message it is either because your TPA is too low and/or your system has the capability to have to have too many di- rectory entries (than what the available memory can hold). If you are unsure of the number of entries that are occupied then you should not continue at this time. You then have to eliminate some files from disk with PIP and by erasing them and/or by using "DU" to "manually" perform a bit of what this pro- gram does. Once you are sure that your number of directory entries does not exceed the maximum shown in the program's warning message, you may continue. Be forewarned that to continue with the program when MORE en- tries exist than what the program states it can handle could very well destroy your directory! (If in doubt, don't!) ------------------------------------------------------------------------ RELEASE I have tested the program on a Morrow MD11 (now with two 20 Mb hard disk drives) under CP/M 3.0. It has also been tested on a CP/M 2.2 system. This is a free "public domain" program, and as with most all programs, no warranties or guarantees are expressed or implied, although much ef- fort is made to ensure bug-free operation of the program. Any user of the program uses it at their own risk. - George F. Reding  TITLE 'SAPP Sort & Pack Directory for CP/M 2.2 or 3.0' ; ASEG ; Needed for M80 .Z80 ; Needed for M80 ; ;----------------------------------------------------------------------- ; ; SAPP v2.0 Sort & Pack Directory for CP/M 2.2 & 3.0 ; 08/02/87 By George F. Reding ; Based upon (and many routines from) ; SAP v44 Sort & Pack program for CP/M 2.2 ; A Public Domain utility program. ; Not to be sold (in whole or part). ; ;----------------------------------------------------------------------- ; ; 08/02/87 CP/M 2 bug was found; where the DMA pointer was ; v2.0 not advanced, so the buffer under 2.2 was being ; overwritten. Corrected with the move of 1 label ; and addition of 1 label; e.g., DIRLOP & DIRLP1. ; Thanks to David Bowerman, sysop of Frog Hollow ; RCPM System (604-937-0906), for having located ; the cause of the problem. David then tested it ; on disk with 250 of 256 directory entries used, ; under CP/M 2.2 and all is now fine. ; ; 07/13/87 Found that vers 1.8 clobbered the DE reg in the ; v1.9 XBIOS2 thus causing SECTRAN function for CP/M 2 ; to not work properly. ; ; 06/21/87 Added Z80 & version tests and restored CP/M 2.2 ; v1.8 code so the program is usable under CP/M 2.2 or ; 3.0. Added adjustment for TPA size diffences so ; this may be used, at user discretion when TPA's ; are too low and/or maximum number of entries is ; too large. User options may now be set in the ; COM file using DDT, SID, or SZAP if desired. ; ; ; 06/15/87 Reinstated disk reset in new location after the ; v1.7 current or specified drive is obtained. ; ; 04/16/87 Removed reset disk function which forced a drive ; v1.6 specification to be required for other drives. ; Added check for second hard drive. ; ; 01/11/87 Modified use of relative jumps to ensure they're ; v1.5 only used to optimize speed (faster if condition ; is NOT met). Now use alt reg in SWITCH routine. ; Some more comments added. ; ; 12/10/86 Uses alt regs in COMPR1 and PRNTL routines. ; v1.4 ; ; 11/15/86 Modified the program by removing some unused code ; v1.3 and by utilizing z80 code anywhere possible. Use ; ZASM or other equivilent z80 assembler. ; ; ; SAPP (for CP/M Plus) was created from SAP (for CP/M 1.4 or 2.2) ; ;----------------------------------------------------------------------- ; NO EQU 0 YES EQU NOT NO ; For conditional assembly ; ; Version number and date ; VER EQU 20 ; Version number VERM EQU 08 ; Version month VERD EQU 02 ; Version day VERY EQU 87 ; Version year ; ; System equates ; BASE EQU 0 ; Base of system (warm boot) BDOS EQU BASE+5 ; BDOS FCB EQU 5CH ; File control block ; DPB2OF EQU 10 ; Offset to CP/M 2.2 DPB DPB2LN EQU 15 ; Length of CP/M 2.2 DPB DPB3OF EQU 12 ; Offset to CP/M 3.0 DPB3LN EQU 17 ; Length of CP/M 3.0 ; ; BDOS functions ; CONOUF EQU 2  ; Console output RDCBUF EQU 10 ; Read console buffer RETVER EQU 12 ; Return version number RESET EQU 13 ; Reset disk GETDSK EQU 25 ; Get current disk ; ; ASCII equates ; BEL EQU 07H ; Bell BS EQU 08H ; Backspace CR EQU 0DH ; Carriage return LF EQU 0AH ; Line feed ; ;----------------------------------------------------------------------- ; ; User configuration equates ; DELZRO EQU NO ; Yes deletes all zero-length ; files not starting with "-" ; (as in SAP44). ; No to delete solely retains all zero- ; length files ; CLRS1 EQU 1AH ; To clear screen on start up CLRS2 EQU 00H ; Room for maximum 6 bytes CLRS3 EQU 00H ; Set terminating byte to 0 CLRS4 EQU 00H ; If clear screen undesired then CLRS5 EQU 00H ; Set 1st (or all) bytes to 0 CLRS6 EQU 00H ; Nul terminator is in program ; ; Following is set to 0E5H, except Morrow MD5 -> MD34 set to 0 ; RCVALU EQU 0E5H ; Set 0 if Morrow, else 0e5h ; ; Following for CP/M 3.0 users only ; SSCPM3 EQU 1024 ; Your largest record size ; ;----------------------------------------------------------------------- ; ORG 100H ; JP START ; Jump over stuff ; TITLM: DB 'SAPP v' DB VER/10+'0',VER MOD 10+'0',' ' DB VERM/10+'0',VERM MOD 10+'0','/' DB VERD/10+'0',VERD MOD 10+'0','/' DB VERY/10+'0',VERY MOD 10+'0' DB ' GFR',0 DB 1AH ; EOF in case .COM file typed ; ;----------------------------------------------------------------------- ; ; Following may be patched with DDT, SID, or SZAP ; CLRCOD: DB CLRS1,CLRS2 ; Clear screen code DB CLRS3,CLRS4 DB CLRS5,CLRS6 DB 0 ; RECSIZ: DW SSCPM3 ; Size of CP/M 3.0 rec buffer RFIELD: DB RCVALU ; Normally 0E5h, Morrow is 0 ; Set 0E5h if not MD5, MD11, etc SKPWRN: DB 0 ; Zero = warning. Nonzero = none. ; Normally should be 0 (false) ; ;----------------------------------------------------------------------- ; START: SUB A ; Z80 test JP PO,START1 ; Continue if z80 LD HL,NDZ80 ; Need z80 msg JP PRNTL ; Print it & ret to system  ; ; Is Z80 CPU, so get CP/M version ; START1: LD C,RETVER ; Get version CALL BDOS LD A,L ; Version to 'A' LD (VERFLG),A ; Store it CP 22H ; Must be at least 2.2 JP C,BASE ; If less, then quit LD A,H OR A ; Test for 0 for CP/M JP NZ,BASE ; If not, quit as MP/M untested ; ; Clear screen and/or give signon message ; START2: LD HL,CLRCOD ; Clear screen at start CALL PRNTL ; Of program LD HL,TITLM ; Program signon msg CALL PRNTL ; ; Now initialize the data and buffers ; START3: XOR A ; Zero for fill LD HL,DATA ; Point to data start (source) LD (HL),A ; Fill it LD DE,DATA+1 ; Point to next (destination) LD BC,DATSIZ ; Length LDIR ; Zero it all ; ; This buffer is only used by CP/M 3.0 ; START4: CALL V3CHEK ; Check version JR C,START5 ; Skip if 2.2 LD (HSTBUF),HL ; Save CP/M 3.0 sec buf addr LD BC,(RECSIZ) ; Get size of rec buffer LDIR ; Zero it too ; ; Do program buffer for directory ; START5: LD (PRGBUF),HL ; Save program buffer addrress EX DE,HL ; Move buffer addrress to DE LD HL,(BDOS+1) ; Get BDOS address DEC H ; Make 1 lower LD (MEMTOP),HL ; Store memory top address OR A ; Clear carry for subtract SBC HL,DE ; Subtract to be byte count LD B,H ; Move to BC LD C,L ; For length of our fill INC DE ; Increment buffer address (destination) LD HL,(PRGBUF) ; Get back buffer start (source) LDIR ; Zero it too ; ; Data & buffers done so set up stack and offset/length (3.0) ; LD SP,STACK ; Use our own stack CALL V3CHEK ; Check version JR C,START6 ; Skip if 2.2 LD HL,DPB3OF ; Offset xlt address to address LD (DPBOF),HL ; Was default for CP/M 2.2 LD HL,DPB3LN ; Length of CP/M 3.0 table LD (DPBLN),HL ; Was default for CP/M 2.2 ; ;----------------------------------------------------------------------- ; ; Now select drive, copy DBP (make fake if needed), run program ; START6: CALL SETUP ; Select disk, do , etc CALL RDDIR ; Read the directory CALL CLEAN ; Set erased files to all E5 CALL SORT ; Sort the entries CALL PACK CALL WRDIR ; Write new directory back LD HL,DONEM ; 'Done' message CALL PRNTL ; EXIT: JP BASE ; Go warm boot ; ;----------------------------------------------------------------------- ; ; Select disk, get translation table address and copy ; SETUP: LD A,(FCB) ; Get any option DEC A JP P,SELDSK ; Skip if disk mentioned LD C,GETDSK ; Else get current disk CALL BDOS ; Returns 0=A,1=B,2=C,... ; SELDSK: LD (LOGDSK),A ; Store selected disk LD (BCREG),A ; Set C reg in BIOSPB LD C,RESET ; Reset disk system CALL BDOS ; In case new disk CALL V3CHEK ; Check version JR C,SELEC2 ; Skip if 2.2 LD DE,1 ; First call flag LD (DEREG),DE ; Set E register in BIOSPB LD A,9 ; BIOS select function CALL XBIOS3 ; CP/M 3.0 select JP SELEC3 ; Skip over ; SELEC2: LD A,(LOGDSK) ; Get disk back LD C,A ; To C LD A,9 ; BIOS select function CALL XBIOS2 ; Do it ; SELEC3: LD A,H OR L ; Check for HL=0 JR Z,EXIT ; Quit if select error ; LD E,(HL) ; Get address of xlat table INC HL LD D,(HL) DEC HL EX DE,HL LD (RECTBL),HL ; Save xlat address LD HL,DPB2OF ; Default, CP/M 2.2 offset to ; DPBOF EQU $-2 ADD HL,DE LD A,(HL) ; Get address of INC HL LD H,(HL) LD L,A ; Into HL LD DE,DPB ; Destination - our own LD BC,DPB2LN ; Default, size for CP/M 2.2 ; DPBLN EQU $-2 ; LDIR ; ; Calculate memory bytes free for directory ; LD HL,(MEMTOP) ; Get memory top LD DE,(PRGBUF) ; Get buffer start OR A ; Clear cy for subtr SBC HL,DE ; Subtr to get room (bytes) free LD (PRGMAX),HL ; Save free room available ; ; This portion is for Morrow computors. Should not matter to others. ; LD HL,(AL0) ; Get dir alloc INC HL ; If hard disk, was 0FFFFh LD A,H ; Incr would make it 0000h OR L ; Test if zero now JP NZ,SELEC4 ; Skip if not Morrow hard disk LD A,(RFIELD) ; Value for Morrow's rc field LD (FILLR2+1),A ; Modify value in program ; ; Do initial test of number of directory entries ; SELEC4: LD HL,(DRM) ; Get real maximum number of entries LD DE,2047 ; Cannot be > 2047 for 64k TPA EX DE,HL ; Limit to HL, real maximum to DE OR A ; Clear carryy for subtract SBC HL,DE ; Subtract to see if it is greater JR C,SELEC5 ; Skip next if greater ; ; See if enough room for real number of directory entries ; EX DE,HL ; Real maximum to HL LD DE,32 ; Multiply by directory entry length CALL MULT ; To get bytes normally needed LD DE,(PRGMAX) ; Get back free room available EX DE,HL ; Swap so HL=free room DE=needed OR A ; Clear carry for subtract SBC HL,DE ; Subtract to see if enough room RET NC ; Return if ok (no borrow) ; ; Not enough room for real maximum, so calculate the program's maximum ; SELEC5: LD HL,(PRGMAX) ; Maximum free room LD DE,32 ; Each entry is 32 bytes long CALL UDIVID ; Divide to get fake DRM value DEC HL ; Make 1 less (...to be sure) LD (DRM),HL ; For the program ; ; See if use set the flag for no warning (experienced users) ; LD A,(SKPWRN) ; See if we skip this warning CP 0 ; If 0, will give warning message RET NZ ; If <> 0, return and continue ; LD HL,FAK1M ; Tell user about CALL PRNTL ; Limitation of the LD HL,(DRM) ; Number of directory entries CALL PHLFDC ; That the program can handle LD HL,FAK2M ; And prompt for continue CALL PRNTL ; ; See if user wants to continue with fake max number or to abort ; CALL CONBUF ; Get response LD A,(INBUF+2) AND 31 ; "Y", "y", and "^Y" are equiv CP 'Y'-40H RET Z ; Continue if yes ; JP BASE ; Else quit ; ;----------------------------------------------------------------------- ; ; Read/write directory routines ; RDDIR: LD HL,READM ; Read message CALL PRNTL ; Zero last in A register (from message) JP DODIR ; WRDIR: LD A,(NOSWAP) OR A JP NZ,WRDIR1 LD HL,PRESM ; Pre-sorted message CALL PRNTL ; WRDIR1: LD HL,WRITM ; Write message CALL PRNTL LD A,1 ; Put 1 in A register ; DODIR: LD (WRFLAG),A ; Store for read/write LD HL,(SYSTRK) ; Get number of system tracks LD (LOGTRK),HL ; Set the track ; CALL V3CHEK ; Check version JP NC,DODIR2 ; Skip if 3.0 LD BC,(LOGTRK) ; Track to BC LD A,10 ; BIOS SETTRK function CALL XBIOS2 ; Do it for CP/M 2.2 ; DODIR2: LD HL,0 LD (RECORD),HL LD HL,(DRM) ; Get number of directory entries INC HL ; Relative to 1 OR A ; Clear carry for division RR H ; Divide HL by 2 RR L OR A RR H ; Again makes total division by 4 RR L ; To get 128-byte record count LD (DIRCNT),HL LD HL,(PRGBUF) ; Get buffer address LD (DMAADR),HL ; For DMA addr ; DIRLOP: CALL V3CHEK ; Check version JP NC,DIRLP1 ; Skip if 3.0 LD BC,(DMAADR) ; Track to BC LD A,12 ; BIOS SETDMA function CALL XBIOS2 ; Do it for CP/M 2.2 ; DIRLP1: LD DE,(RECORD) ; Current record INC DE ; Increment it LD HL,(SPT) ; Get records per track OR A ; Clear carry for subtract SBC HL,DE ; Subtract SPT - Records EX DE,HL ; Current record to HL JR NC,NOTROV ; ; Track overflow, bump to next ; LD HL,(LOGTRK) ; Get current track INC HL ; Increment it LD (LOGTRK),HL ; Set the track CALL V3CHEK ; Check version JP NC,DIRLP2 ; Skip if 3.0 LD BC,(LOGTRK) ; Track to bc LD A,10 ; Bios settrk func CALL XBIOS2 ; Do it for CP/M 2.2 ; DIRLP2: LD HL,1 ; Rewind record number ; NOTROV: CALL DOREC ; Set current record LD A,(WRFLAG) ; Time to figure out OR A ; If we are reading JP NZ,DWRT ; Or writing ; CALL V3CHEK ; Check version JR C,READ2 ; Skip if 2.2 CALL READ ; Do CP/M 3.0 way JP READ3 ; Skip over 2.2 stuff ; READ2: LD A,13 ; BIOS READ function CALL XBIOS2 ; Do it for CP/M 2.2 ; READ3: OR A ; Test flags on read JR NZ,ERREXT ; NZ=error JP MORE ; Good read, go do more ; ; Write ; DWRT: LD C,1 ; For CP/M 2.2 deblocking BIOSs ; CALL V3CHEK ; Check version JR C,DWRT2 ; Skip if 2.2 CALL WRITE ; Do CP/M 3.0 way JP DWRT3 ; Skip over 2.2 stuff ; DWRT2: LD A,14 ; BIOS WRITE function CALL XBIOS2 ; Do it for CP/M 2.2 ; DWRT3: OR A ; Test flags on write  JR NZ,ERREXT ; NZ=bad directory write ; ; Good read or write ; MORE: LD HL,(DMAADR) ; Get DMA addr LD DE,80H ; Add 128 bytes to it ADD HL,DE ; For next pass LD (DMAADR),HL LD HL,(DIRCNT) ; Countdown entries DEC HL LD (DIRCNT),HL LD A,H ; Test for zero left OR L JP NZ,DIRLOP ; Loop till zero ; LD HL,80H ; Directory I/O done, reset DMA address LD (DMAADR),HL ; Set DMA CALL V3CHEK ; Check version RET NC ; Return if 3.0 ; LD BC,(DMAADR) ; Track to BC LD A,12 ; BIOS SETDMA function CALL XBIOS2 ; Do it for CP/M 2.2 RET ; ; Come here on read/write error ; ERREXT: LD HL,ERORM ; Error message CALL PRNTL JP BASE ; Go warm boot ; ; DOREC: LD (RECORD),HL ; Record update LD B,H LD C,L LD HL,(RECTBL) EX DE,HL DEC BC ; CALL V3CHEK ; Check version JR C,DOREC2 ; Skip if 2.2 CALL RECTRN ; Do for CP/M 3.0 JP DOREC3 ; Skip 2.2 part ; DOREC2: LD A,16 ; BIOS SECTRAN function CALL XBIOS2 ; DOREC3: LD B,H ; Moved to B, but unused LD A,L LD (LOGREC),A ; Set record CALL V3CHEK ; Check version RET NC ; Return if 3.0 ; LD A,(LOGREC) ; Get recorad for CP/M 2.2 LD C,A ; To register 'C' LD A,11 ; BIOS SETREC function CALL XBIOS2 ; Do it for CP/M 2.2 RET ; CLEAN: LD HL,0 ; ICT = 0 ; CLNLOP: LD (ICT),HL CALL INDEX ; HL = BUF + 16 * ICT LD A,(HL) ; Jump if this is a deleted file CP 0E5H JP Z,FILLR1 ; IF DELZRO LD DE,12 ADD HL,DE ; HL = HL + 12 LD A,(HL) ; Check extent field OR A JP NZ,CLBUMP ; Skip if not extent 0 INC HL ; Point to record count field INC HL LD A,(HL) ; Get S2 byte (extended RC) AND 0FH ; For CP/M 2.2, 0 for CP/M 1.4 LD E,A INC HL LD A,(HL) ; Check record count field OR E JP NZ,CLBUMP ; Jump if non-zero LD HL,(ICT) ; Clear all 32 bytes of CALL INDEX ; Directory entry to E5 INC HL LD A,(HL) ; Get first character of filename DEC HL ; MAST.CAT catalog programs CP '-' ; Have diskname of zero length JP Z,CLBUMP ; That start with "-", do not delete ENDIF ; DELZRO ; IF NOT DELZRO JP CLBUMP ; Do not delete this file ENDIF ; NOT DELZRO ; FILLR1: LD (HL),0E5H ; Fill source memory with E5 LD D,H ; Get HL address to DE INC DE ; Destination = source + 1 LD BC,31 ; Copy 31 times LDIR LD DE,17 ; Point to the RC OR A ; Clear carry for subtract SBC HL,DE ; Adjust HL to it, by backing up ; FILLR2: LD (HL),0E5H ; Set RC to E5 unless Morrow hard disk ; CLBUMP: LD DE,(DRM) ; Get count of filenames INC DE ; Increment it LD HL,(ICT) ; Our current count INC HL ; Increment it PUSH HL OR A ; Clear carry for subtract SBC HL,DE ; Subtract DRM from ICT POP HL JP C,CLNLOP ; Loop until all cleaned RET ; ; Sort the directory ; SORT: XOR A LD (NOSWAP),A ; Zero the flag in case pre-sorted LD HL,SORTM ; Sort message CALL PRNTL ; ; Shell-Metzner sort ; LD HL,(ICT) INC HL LD (SNRECW),HL LD HL,(PRGBUF) ; Get buffer address LD (SSTADR),HL PUSH HL ; Save it LD HL,32 LD (SRECLN),HL PUSH HL ; Save it ; ; Now divide number of fields by 2 ; DIVIDE: LD HL,(SNRECW) ; Get value OR A ; Clear carry for division RR H ; Divide HL by 2 RR L LD (SNRECW),HL ; Save result LD A,L ; If SNRECW <> 0 OR H ; Then JP NZ,NDONE ; Not done ; POP BC ; All fields sorted POP DE ; So clean up stack RET ; ; Not done yet ; NDONE: EX DE,HL LD HL,(ICT) INC HL OR A ; Clear carry for subtract SBC HL,DE ; HL = HL - DE LD (SRECLN),HL LD HL,1 LD (SSRTV1),HL LD (SSTADR),HL DEC L POP BC PUSH BC ; NDONE1: ADD HL,DE DEC BC LD A,B OR C JP NZ,NDONE1 LD (SSRTV2),HL EX DE,HL POP BC POP HL PUSH HL PUSH BC ; NDONE2: LD (SSRTV4),HL LD (SSRTV3),HL EX DE,HL ADD HL,DE EX DE,HL ; COMPR: POP BC ; Get count PUSH BC ; Save count PUSH DE ; Save addresses of beginning PUSH HL ; Of these 2 directory entries ; COMPR1: LD A,(DE) ; Get (DE) byte AND 7FH ; Strip parity (in case TO, SYS, etc.) PUSH BC ; Save count EX AF,AF' ; Use alt register - saves (DE) byte LD A,(HL) ; Get (hl) byte AND 7FH ; Strip parity (for same reason) LD B,A ; Save it EX AF,AF' ; Restore register - restores (DE) byte SUB B ; Subtract (HL) from (DE) POP BC ; Restore count JP NZ,NOTEQU ; If not equal, jump over INC HL ; Else bump INC DE ; Both addresses DEC BC ; Decr count LD A,B ; Test OR C ; For zero JP NZ,COMPR1 ; Do more if <> 0, else fall into next ; NSWCH: POP HL ; Clean up the stack POP HL ; And again ; NSWCH1: LD DE,(SSTADR) INC DE LD (SSTADR),DE LD (SSRTV1),DE LD HL,(SRECLN) OR A ; Clear carry SBC HL,DE ; Subtract HL - DE JP C,DIVIDE LD HL,(SSRTV4) POP DE PUSH DE ADD HL,DE LD DE,(SSRTV2) JP NDONE2 ; ; The condition at NOTEQU has to be changed for descending sort ; NOTEQU: JP NC,NSWCH LD A,1 LD (NOSWAP),A POP HL ; Get back addresses of beginning POP DE ; Of these 2 directory entries POP BC ; And the number of bytes PUSH BC ; Leave this on the stack ; SWITCH: LD A,(HL) ; Get (HL) byte EX AF,AF' ; Save it LD A,(DE) ; Get (DE) byte LD (HL),A ; Store in (HL) EX AF,AF' ; Get old (HL) back LD (DE),A ; Store in (DE) INC HL ; Bump both INC DE ; Addresses DEC BC ; Decr count LD A,B ; Test OR C ; For zero JP NZ,SWITCH ; Go do more if <> 0 LD HL,(SNRECW) LD A,H CPL LD D,A LD A,L CPL LD E,A LD HL,(SSRTV1) ADD HL,DE JP NC,NSWCH1 INC HL LD (SSRTV1),HL LD DE,(SSRTV3) LD HL,(SSRTV2) LD A,E ; Subtr DE - HL SUB L LD L,A LD A,D SBC A,H LD H,A ; Result in hl, de unaffected LD (SSRTV3),HL ; Store result here JP COMPR ; PACK: LD HL,0 ; ICT = 0 ; PACK1: LD (ICT),HL CALL INDEX ; HL = BUF + 16 * ICT LD DE,9 ADD HL,DE ; HL = HL + 9 LD A,(HL) ; Jump if filetype not "X$$" SUB '0' JP C,PACK2 CP 10 JP NC,PACK2 LD (JCT),A INC HL LD A,(HL) CP '$' JP NZ,PACK2 INC HL LD A,(HL) CP '$' JP NZ,PACK2 INC HL ; Set extent number to x LD A,(JCT) LD (HL),A DEC HL ; Set filetype to "$$$" LD (HL),'$' DEC HL LD (HL),'$' DEC HL LD (HL),'$' ; PACK2: LD HL,(ICT) INC HL ; ICT = ICT + 1 LD DE,(DRM) INC DE ; DRM = DRM + 1 PUSH HL OR A ; Clear carry for subtract SBC HL,DE ; Subtract DRM from ICT POP HL ; Loop until ICT > DRM JP C,PACK1 RET ; INDEX: ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL LD DE,(PRGBUF) ; Get buffer address ADD HL,DE RET ; ; Print nul terminated string pointed to by HL ; PRNTL: LD A,(HL) ; Get char OR A ; Test for zero RET Z ; If yes, we are done EXX ; Use alt primary regs LD E,A ; Get char to e for output LD C,CONOUF ; Console out func CALL BDOS EXX ; Restore normal primary regs INC HL ; Point to next JP PRNTL ; Try to do more ; ;----------------------------------------------------------------------- ; ; Print HL as decimal chaacters in N-character field, floating point ; where field size is from 1 to 5 characters. ; PHLFDC: PUSH AF ; Save all registers PUSH BC PUSH DE PUSH HL LD B,1 ; B=1 for leading LD DE,10000 ; Print 10,000's CALL PHDC1 LD DE,1000 CALL PHDC1 LD DE,100 CALL PHDC1 LD DE,10 ; Print 10's CALL PHDC1 LD A,L ; Print 1's ADD A,'0' ; Convert to ascii CALL COUT POP HL ; Restore all regs POP DE POP BC POP AF RET ; ; Divide HL by DE and print quotient with leading spaces ; PHDC1: LD C,0 ; Set count ; PHDC2: OR A ; Clear carry for Z80 way SBC HL,DE ; Subtract DE from HL with borrow JR C,PHDC3 ; Done if carry set (further borrow) INC C ; Increment count JP PHDC2 ; PHDC3: OR A ; Clear carry ADC HL,DE ; Add DE to HL with carry Z80 way LD A,C ; Get result OR A ; Check for 0 JR NZ,PHDC4 OR B ; 0=no leading RET NZ ; (A=0, A or B = 0 means B = 0) ; PHDC4: LD B,0 ; Turn off leading space LD A,C ; Get value ADD A,'0' ; Convert to ASCII JP COUT ; COUT: EXX ; Use alt primary regs PUSH AF LD E,A LD C,CONOUF ; Console OUT function CALL BDOS POP AF EXX  ; Restore normal primary registers RET ; CONBUF: LD C,RDCBUF LD DE,INBUF JP BDOS ; INBUF: DB 1,0,0 ; ; Multiply HL by DE, return result in HL ; MULT: PUSH BC PUSH DE EX DE,HL LD B,D LD C,E LD A,B OR C JP NZ,MULCON LD HL,0 ; Filter special case JR MLDONE ; Of multiply by 0 ; MULCON: DEC BC LD D,H LD E,L ; MULTLP: LD A,B OR C JR Z,MLDONE ADD HL,DE DEC BC JP MULTLP ; MLDONE: POP DE POP BC RET ; ; Unsigned division. Divide HL by DE. Destroys register A. ; Return HL = quotient, DE = remainder, carry clear if ok. ; Carry set if DE > HL, or if DE = 0. ; UDIVID: PUSH BC ; Save register LD BC,0 ; Initialize quotient PUSH HL ; Save HL OR A ; Clear carry SBC HL,DE ; Test if divisor > dividend POP HL ; Restore HL JR C,DIVBAD ; If so, it is bad LD A,E ; Else test divisor OR D ; For 0 JP NZ,DIVLOP ; If <> 0 continue ; DIVBAD: POP BC ; Restore registers SCF ; Set cy to show bad RET ; DIVLOP: INC BC ; Increment quotient OR A ; Clear carryy for subtract SBC HL,DE ; Division by subtract JP C,DIVOFL ; If borrow, done dividing JP NZ,DIVLOP ; If <> 0 more to do JP DIVREM ; Was 0, skip calculation of remainder ; DIVOFL: ADD HL,DE ; HL=neg remainder, DE=divisor DEC BC ; The even division was 1 less ; DIVREM: EX DE,HL ; Positive remainder to DE LD H,B ; Put quotient into HL LD L,C POP BC ; Restore registers OR A ; Clear carry (valid result) RET ; ;----------------------------------------------------------------------- ; ; Test for CP/M 3.0, used by above routines ; V3CHEK: LD A,(VERFLG) ; Check for version 3.0 CP 30H ; Carry set if not 3.0 RET ; VERFLG: DB 0 ; Version number of CP/M ; ;----------------------------------------------------------------------- ; ; For CP/M 2.2, enter with BIOS function in A eg., LD A,9 (SELDSK) ; XBIOS2: PUSH DE ; Save DE LD HL,(BASE+1) ; Warm boot address LD L,A ; BIOS function number to 'L' LD E,L ; And to 'E' LD D,0 ; Zero 'D' ADD HL,DE ; Add so that HL ADD HL,DE ; points to BIOS function address POP DE ; Restore DE JP (HL) ; Do it an return to call addr ; ;----------------------------------------------------------------------- ; ; Following is for CP/M 3.0 ; ; Translate records. Records are not translated yet. Wait until we ; know the physical record number. This works fine as long as a program ; trusts the BIOS to do translation. For programs that directly access ; the XLAT table to do their own translation, this may give a wrong idea ; about disk skew but it shouldn't cause harm. ; RECTRN: LD L,C ; Return record in HL LD H,B RET ; ; Read the selected CP/M record ; READ: LD A,1 LD (READOP),A ; Read operation INC A ; A=2 (WRUAL) LD (WRTYPE),A ; Treat as unalloc JP ALLOC ; Perform read ; ; Write the selected CP/M record ; WRITE: XOR A LD (READOP),A ; Not a read operation LD A,C LD (WRTYPE),A ; Save write type CP 2 ; Unallocated block? JP NZ,CHKUNA ; ; Write to first record of unallocated block ; LD A,(BLM) ; Get block shift mask INC A ; Adjust value LD (UNACNT),A ; Unallocated record count LD A,(LOGDSK) ; Set up values for LD (UNADSK),A ; writing to an unallocated LD A,(LOGTRK) ; Block LD (UNATRK),A LD A,(LOGREC) LD (UNAREC),A ; CHKUNA: LD A,(UNACNT) ; Any unallocated records OR A ; In this block JP Z,ALLOC ; Skip if not DEC A LD (UNACNT),A LD A,(LOGDSK) LD HL,UNADSK CP (HL) ; LOGDSK = UNADSK ? JP NZ,ALLOC ; Skip if not LD A,(LOGTRK) CP (HL) ; LOGTRK = UNATRK ? JP NZ,ALLOC ; Skip if not LD A,(LOGREC) LD HL,UNAREC CP (HL) ; LOGTRK = UNAREC ? JP NZ,ALLOC ; Skip if not INC (HL) ; Move to next record LD A,(HL) LD HL,SPT ; Address of SPT CP (HL) ; Record > SPT ? JP C,NOOVF ; Skip if no overflow LD HL,(UNATRK) INC HL LD (UNATRK),HL ; Bump track XOR A LD (UNAREC),A ; Reset record count ; NOOVF: XOR A LD (RCFLAG),A ; Do not pre-read JP RWOPER ; Perform write ; ALLOC: XOR A ; Requires pre-read LD (UNACNT),A INC A LD (RCFLAG),A ; Force pre-read ; RWOPER: XOR A LD (ERFLAG),A ; No errors yet LD A,(PSH) ; Get physical shift factor OR A ; Set flags LD B,A LD A,(LOGREC) ; Logical record LD HL,(HSTBUF) ; Get address of buffer LD DE,128 JP Z,NOBLK ; No blocking EX DE,HL ; Shuffle registers ; SHIFT: EX DE,HL RRCA JP NC,SH1 ADD HL,DE ; Bump buffer address ; SH1: EX DE,HL ADD HL,HL AND 07FH ; Zero high bit DJNZ SHIFT EX DE,HL ; HL=buffer address ; NOBLK: LD (RECHST),A LD (RECBUF),HL LD HL,HSTACT ; Buffer active flag LD A,(HL) LD (HL),1 ; Set buffer active OR A ; Was it already? JP Z,FILHST ; Fill buffer if not LD A,(LOGDSK) LD HL,HSTDSK ; Same disk ? CP (HL) JP NZ,NMATCH LD A,(LOGTRK) LD HL,HSTTRK ; Same track ? CP (HL) JP NZ,NMATCH LD A,(RECHST) ; Same buffer ? LD HL,HSTSEC CP (HL) JP Z,MATCH ; NMATCH: LD A,(HSTWRT) ; Buffer changed? OR A CALL NZ,WRTHST ; Clear buffer ; FILHST: LD A,(LOGDSK) LD (HSTDSK),A LD HL,(LOGTRK) LD (HSTTRK),HL LD A,(RECHST) LD (HSTSEC),A LD A,(RCFLAG) ; Need to read ? OR A CALL NZ,REDHST ; Yes XOR A LD (HSTWRT),A ; No pending write ; MATCH: LD DE,(DMAADR) LD HL,(RECBUF) LD A,(READOP) ; Which way to move ? OR A JP NZ,RWMOVE ; Skip if read LD A,1 LD (HSTWRT),A ; Mark buffer changed EX DE,HL ; Hl=DMA DE=buffer ; RWMOVE: LD BC,128 ; Byte count LDIR ; Block move LD A,(WRTYPE) ; Write type CP 1 ; To directory ? JP NZ,RWEXIT ; Done LD A,(ERFLAG) ; Check for errors OR A JP NZ,RWEXIT ; Do not write directory if so XOR A LD (HSTWRT),A ; Show buffer written CALL WRTHST ; Write buffer ; RWEXIT: LD A,(ERFLAG) RET ; ; Disk read, call BIOS to fill the buffer with one physical record ; REDHST: CALL RWINIT ; Init CP/M 3.0 BIOS LD A,13 ; READ function JP DORW ; Go do it ; ; Disk write, call BIOS to write one phy record from buffer ; WRTHST: CALL RWINIT ; Init CP/M 3.0 BIOS LD A,14 ; WRITE function ; ; Call BIOS to read (or write) 1 physical record to (or from) buffer ; DORW: CALL XBIOS3 ; Go do it to the record LD (ERFLAG),A RET ; ; Translate record, set track, record, DMA buffer and DMA bank ; RWINIT: LD HL,(HSTTRK) ; Get physical track number LD (BCREG),HL ; Put track number in BC LD A,10 ; SETTRK function CALL XBIOS3 LD A,(HSTSEC) ; Get physical record number LD L,A LD H,0 LD (BCREG),HL ; Put record number in BC LD HL,(RECTBL) ; Address of xlate table LD (DEREG),HL ; Xlate address in DE LD A,16 ; SECTRN function CALL XBIOS3 ; Get skewed record number LD A,L LD (ACTREC),A ; Actual record LD (BCREG),HL ; Record number in BC LD A,11 ; SETSEC function CALL XBIOS3 ; Set CP/M 3.0 record LD HL,(HSTBUF) ; Get record buffer address LD (BCREG),HL ; Buffer address in BC LD A,12 ; SETDMA function CALL XBIOS3 LD A,1 ; DMA bank number LD (AREG),A ; Put bank number in A LD A,28 ; SETBNK function CALL XBIOS3 ; Set DMA bank RET ; ; Routine to call banked BIOS routines via BDOS function 50. ; All disk I/O calls are made through here. ; XBIOS3: LD (BIOSPB),A ; Set BIOS function LD C,50 ; Direct BIOS call function LD DE,BIOSPB ; BIOS parameter block JP BDOS ; Jump to BDOS ; ;----------------------------------------------------------------------- ; ; Messages ; NDZ80: DB CR,LF,'Z80 needed',CR,LF,0 ; FAK1M: DB CR,LF,LF,'If MORE than ',0 FAK2M: DB ' directory entries exist,' DB CR,LF,'please reduce entries ' DB 'and/or use DU first.',BEL DB CR,LF,'Continue with MORE will' DB ' destroy directory!' DB CR,LF,LF,'Press ''Y'' to ' DB 'continue, else aborts: ',0 ; DONEM: DB 'Done',CR,LF,0 READM: DB CR,LF,LF,'Read, ',0 PRESM: DB '(Previously-sorted) ',0 WRITM: DB 'Write, ',0 ERORM: DB BS,BS,' Error',BEL,CR,LF,0 SORTM: DB 'Sort, ',0 ; ;----------------------------------------------------------------------- ; ; Data area ; DATA EQU $ ; ; Bios parameter block for CP/M 3.0 ; BIOSPB: DS 1 ; BIOS function AREG: DS 1 ; A register BCREG: DS 2 ; BC register DEREG: DS 2 ; DE register HLREG: DS 2 ; HL register ; HSTBUF: DS 2 ; Cp/m 3.0 disk I/O buffer address ; ;----------------------------------------------------------------------- ; ; Disk parameter block for CP/M 2.2 or 3.0 ; DPB: SPT: DS 2 BSH: DS 1 BLM: DS 1 EXM: DS 1 DSM: DS 2 DRM: DS 2 AL0: DS 1 AL1: DS 1 CKS: DS 2 SYSTRK: DS 2 PSH: DS 1 ; Physical shift count (CP/M 3.0) PSM: DS 1 ; Physical record mask (CP/M 3.0) ; ;----------------------------------------------------------------------- ; ; General data area used by the program ; MEMTOP: DS 2 ; Memory top address PRGBUF: DS 2 ; Address of the program buffer PRGMAX: DS 2 ; Free bytes for program buffer ; DIRCNT: DS 2 ICT: DS 2 JCT: DS 2 NOSWAP: DS 1 RECTBL: DS 2 RECORD: DS 2 WRFLAG: DS 1 SRECLN: DS 2 SSTADR: DS 2 SSRTV1: DS 2 SSRTV2: DS 2 SSRTV3: DS 2 SSRTV4: DS 2 SNRECW: DS 2 ; ;----------------------------------------------------------------------- ; ; Following used by the program and CP/M 3.0 ; LOGDSK: DS 1 ; Logical disk number LOGTRK: DS 2 ; Logical track number LOGREC: DS 1 ; Logical record number ; ;----------------------------------------------------------------------- ; ; Following used by CP/M 3.0 ; HSTDSK: DS 1 ; Physical disk number HSTTRK: DS 2 ; Physical track number HSTSEC: DS 1 ; Physical record number ; ACTREC: DS 1 ; Skewed physical record RECHST: DS 1 ; Tempopary physical record HSTACT: DS 1 ; Buffer active flag, initially 0 HSTWRT: DS 1 ; Buffer changed flag, initially 0 ; UNACNT: DS 1 ; Unallocated record count UNADSK: DS 1 ; Unallocated disk number UNATRK: DS 2 ; Unallocated track number UNAREC: DS 1 ; Unallocated record number RECBUF: DS 2 ; Logical record address in buffer ; ERFLAG: DS 1 ; Error reporting RCFLAG: DS 1 ; Force record read READOP: DS 1 ; 1 if read operation WRTYPE: DS 1 ; Write operation type DMAADR: DS 2 ; Last DMA address ; ;----------------------------------------------------------------------- ; ; Stack and buffer area for the program ; DS 40 ; Minimum stack (20 levels) EVEN EQU ($+255)/256*256 ; Start buffer on even page ; Also increases stack greatly ORG EVEN STACK EQU $-2 DATSIZ EQU $-DATA ; Size of data area ; Buffers start here ; END  increases stack greatly ORG EVEN STACK EQU $-2 DATSIZ EQU $-DATA ; Size of data area ; Buffer$ARCOPY 2.1 (3/88) (c)1988 E.Meyer s1*|gR%.|8&" |2 6<2 > 2 :2 !T >-h !6O#6 Ͷ  0 !~ʧ o#6!~O#>,2 !~O#>=+6 >2 2, !~O>[#,  !  !  !  ! : (! $ ڛ: 2N!\O$! : (! $ ڛ: 2s!\t$!, ! 6~# ((g](cA(/C(1D(3E(5N(7R(9W(;Z(=I à 2 ! w!P V H : (  ( ( ( I à !u S E 7 ) ><2  ( ( (: Oʛ: (y[›:P !uP :u !Pu :P ʛ!P >?>/2 !Pu ?( #>2 *N[sR /2 !uC : (2 2 : ›!C >?>! N(=! F(>2 B : (>2 !O@  82 !@ " :N OO8!_ o: G(~ W +~ k:Ow@ : (O 0    *  " ! 4( ÷I à : 2 ʁ G!@ :t#n : (6: G!@  I à  : O=(7A(0bk  I à !  : G!͆ 0I à !@ " f  * O  4 !Pun  : :s : e!t\  \_: _(( g( e! 5m : ! (E~(!O I -> !t : O(&I y/n? _  Y( N h P h : ott:N : w(#o!X˾O: (!O\  :s ͥ ttʼ :  :N Oʼ 2 !" : ʩGW E_ ͅ * w#"  Ͷ !~ " Õ zR[ R: 2 _ n * w#" (z[ OʕҼ * " ! 4: :: (K !˾# x : (`!gŽo 8w B~#ʎͿ f  >  (=(2 ><2 > ~#ʎͅ ~#ʎh :s !" [ t¼ * " ! 5 :N !" : (0* R>* ( րDG6#" R<}2 : : (`Ķ C:s tʼ !t \ !tl \T : t:  %:N : G!ZO: wO: ! (= Ͷ  I *  " ! 5Á : I NO FILE  : ! Ķ : t: =_: {I ç I ç 6> > #~  ( ( (#4~ #~ ~:6 : 2 : 2\ :(^O!>: R++~08%:0!0_+~08:00W_+ {2 ~ (_+~  {A8 _>8{<2\>: > !#(]#(V.(*( (B[(F>?>  #(+. #( *( ([(>?~ #7 ]> ɯ& >.h #~h !P>J !XJ 2 ~(<#: !~e !x0+!C ~? ~###͏ ~# ( ~# (7!}@}>$!@}N˹# #> ~O (#@ R+ : o$ DM* +! ))DM  B8! 5: 2 ~#h ~#h f (>_ ( ( 0>^h @ ( ( ( 0 >^͞ @_  > ͞ > _a{__ CONCRTLSTPRNNULRDRPUNAUX ARCOPY.DOC ---------- Instructions for ARCOPY.COM Version 2.1 - 3/88 (c)1988 E. Meyer ARCOPY (ARchive COPY) is a small, fast file copy utility for all Z80 CP/M (3.0 or 2.2) systems. With ARCOPY you can easily copy files: * while changing disks * selectively (with Yes/No prompts) * to/from any CP/M user areas * only if new (not on destination) * to different destination names * only if already on destination * to/from the console and printer * only if changed since last backup * with PIP or MSDOS COPY syntax * with deletion of original file 1. AN ALTERNATIVE TO PIP Though ARCOPY has several sophisticated options for selective file copying, it can also simply function as a substitute for PIP. ARCOPY is just as fast, and quite a bit smaller, though it has all the commonly used functions of PIP; in fact you can rename it to PIP.COM if you want. But note the following extra features: *- ARCOPY automatically resets the disks under CP/M 2.2 before copying (including between commands, in resident mode). This means that you can swap disks as desired without BDOS R/O errors. *- Especially when backing up floppy disks, you often have the problem that both disks are relatively full. Despite the fact that the replacement file should fit, PIP may run out of space, as it won't erase the existing file until after it's copied the new one. ARCOPY doesn't require this extra free space; it always erases the existing file first. (Use caution -- or PIP -- in situations where this could be a problem.) *- ARCOPY processes files in alphabetical order. This, combined with the individual Confirm (Y/N) option, makes it a sort of mini-SWEEP utility. *- ARCOPY supports ZCPR-style user numbers "Du u:" (like B12:GORT.TXT) in both arguments. This is a lot easier than PIP's "[Guu]" option. *- ARCOPY can rename groups of files, or copy while renaming: for example, you can copy *.TXT to *.DOC. It's even clever enough to copy *.* to *.BAK, if that will work in a given situation. *- ARCOPY accepts the same syntax as PIP; but it can also operate like the MSDOS COPY command (source then destination, rather than vice versa). One nice result is that if you rename ARCOPY to COPY.COM, you will be able to move files around with the same command as on MSDOS: A>COPY SOURCE DEST. This will make life less complicated for those who (like me) are finally making the transition to MSDOS. Do keep a copy of the "real" PIP.COM around; it's a versatile beast, and ARCOPY can't quite do everything that PIP can. ARCOPY doesn't concatenate files, and supports only a subset of PIP options and devices (see below). 2. SELECTIVE COPYING Keeping backup copies of valuable data is very important; however, it can often be enough of a pain to discourage you from doing it. (Copying an entire disk takes a long time; copying just what you think you might have changed recently is unreliable.) By automating tedious backup tasks, ARCOPY should encourage you to play it safe. Situations that arise when constructing or updating backup disks are: The need to copy onto the backup disk ONLY files that do NOT already exist on the backup disk. (As when you want to copy some new files, but don't need to recopy the old ones.) The need to copy ONLY files that DO already exist on the backup disk. (As when other files are backed up on other disks.) The need to systematically rename a whole group of files while copying them. The need to select just a few assorted files to copy. ARCOPY's ability to do all this, along with the choice of appropriate filenames, makes backing up data far more convenient, even if your CP/M system doesn't support an Archive attribute. If it does, read on... 3. INCREMENTAL BACKUP Suppose you have a disk with a number of files on it, a few of which have been changed recently. How do you update your backup copy? You can always wipe it clean and recopy everything; but it would be nice if you could just copy exactly those things that need it ("incremental backup"). CP/M 3.0 maintains a file attribute (analogous to Read/Only and System) called Archive. Whenever a disk file is modified, this attribute is cleared. Consequently, if this attribute was set in the past, the files which now lack it are those you have created or changed since, and need to back up now. CP/M 3.0 PIP includes an option "[A" which uses this attribute to provide incremen- tal backup: it copies only files which are not marked as Archived, and then marks them so. ARCOPY can do the same thing, even under CP/M 2.2. However, 2.2 does not, as a rule, properly support the Archive attribute: there is no guarantee that it will be cleared as it should be whenever a file is modified (though apparently it will not be accidentally cleared if once set). Some OS enhancements (eg, ZCPRx) may add full support for the Archive bit; check your documentation. Failing this, it is also possible to maintain the "A" attribute manually, using a utility like my DA.COM to set or remove it. In particular, you can set the attribute on all files to begin with, and then remove it from files when they are modified; then ARCOPY will back up just those files. USING ARCOPY ------------ SYNTAX: A>ARCOPY {sourcefil {destfil} {[options]} "{}"=optional or {destfil=sourcefil {[options]}} If the file arguments are separated by an "=", they will be interpreted as PIP would, in destination, source order. If they are only separated by spaces, they will be taken in MSDOS COPY order as source, destination. If NO arguments are given, ARCOPY will load into memory and give you an interactive prompt ("-"). You can then enter one copy command after another. To exit, just press RETURN or ^C. Either filespec may be ambiguous; destination name defaults to same as source. In addition, ZCPR-style Drive/User specifications (Duu:) are supported. If not specified, drive and user default to the current values. Common device names (eg CON:) are also accepted. "Options" must be preceded by "["; separating spaces and a closing "]" are optional. You may select one or more of: ** A - ARCHIVE (incremental backup) N - NEW (NONexisting) files only * C - CONFIRM each file copy (Y/N) * R - READ System files too D - DELETE original after copy * W - WRITE over Read/Only files E - EXISTING files only ! * Z - ZERO parity bit (* - Options in common with CP/M 2.2 PIP; ** - CP/M 3.0 PIP only) ARCOPY resets disks before attempting to copy files. For ambiguous (group) copying, it alphabetically lists each file being processed; you may abort at any time by typing ^C. By DEFAULT, all specified files will be copied from the source to the destination disk. Copies retain the same attributes as the original (except Archive, which is always cleared). Files with the SYStem attribute will be ignored. However: If you specify the "A" option, only files without the Archive attribute will be copied; and as each is backed up, the original will have its Archive attribute set. (See above for further explanation.) If you specify "C", along with an ambiguous filename, you will be prompted "y/n?" for each file about to be copied. Press "Y" to copy the file, or "N" to skip it. (If the "A" option is also in force, the file will be marked as Archived whether copied or not.) If you specify "D", each source file will be deleted after it is copied, allowing you to simply move files from one place to another. NOTE: if the source and destination directories are the same, ARCOPY is clever enough to recognize this as a request to simply RENAME the file(s). If you specify "E", only those files that already EXIST on the destin- ation disk will be copied. (Note that the DESTINATION name is the one consid- ered here, rather the source name, if these differ.) If you specify "N", only those files that are NEW (do NOT already exist on the destination disk) will be copied. (Note that you can't specify both "E" and "N", as they are mutually exclusive.) If you specify "R", SYStem files will be copied as well; otherwise they are ignored. If you specify "W", Read/Only files may be overwritten; otherwise, any attempt to delete them will cause a BDOS R/O error. If you specify "Z", ARCOPY will zero the high (parity) bit on all output data. This can be useful for converting nonstandard data (like Wordstar text files) into standard ASCII form. ABOUT RENAMING If you are copying to a different name, ARCOPY will show you both the original and destination name, eg: OLDFIL.NAM -> NEWFIL.NAM. Copying multiple files to different names can get confusing. If the wildcards match exactly (as in *.DOC to *.BAK) it's straightforward enough (although PIP can't do it). But ARCOPY also lets you do stranger copies, which may or may not work depending on the specific filenames encountered. Suppose you have three files on drive A: GORT.TXT, KLAATU.DOC, NIKTO. You want to create some extra backups on drive B. You might type: A>ARCOPY *.* B:*.BAK Despite the fact that most utilities would refuse this request, it's perfectly clear what you want, and ARCOPY will do it: GORT.TXT, KLAATU.DOC, NIKTO ---> B:GORT.BAK, KLAATU.BAK, NIKTO.BAK But suppose there had been a fourth file A:GORT.NOT. Because both it and GORT.TXT would get copied to the same destination B:GORT.BAK, which is likely to be a bad idea, in this case ARCOPY would refuse the (same!) copy request. Rather than give up in principle, because there might be trouble, ARCOPY looks ahead at what the actual results of copying would be. If no conflict would arise, ARCOPY will do it. If a file would overwrite itself, or two files copy to the same destination, or an illegal filename be constructed, ARCOPY gives an error message, and no files are copied. ARCOPY is also a mass renaming utility. If you use the "D" option when the source and destination directories are the same, ARCOPY realizes it doesn't actually have to copy the file(s), but can just rename them. All the rules above apply. ABOUT DEVICES ARCOPY recognizes several CP/M "device names" as substitutes for filename arguments: CON, CRT, PRN, LST, NUL. Device names may be followed by a colon (eg, CON:) or not, as desired. Devices allow ARCOPY to transfer data between any combination of the terminal, printer, and disk files. Data sent to/from any device is treated as a stream of bytes terminated by a ^Z. CON: Console. When used as a source, ARCOPY accepts anything you type in as input; type ^Z to finish. When used as a destination, the data will be sent to the screen. This device expands tabs, filters control codes (eg, "^[" for ESC), and allows you to quit with ^C or pause output with ^S. CRT: Terminal. A more basic console I/O device that does not expand tabs, filter output, or respond to ^S/^C. Useful for setting screen attributes. PRN: Printer. Can only be used as a destination. Output will be sent to the printer, with a formfeed added every 60 lines to paginate it. LST: List device. A more basic printer output device that does not add any extra formfeeds. Usef"ul for initialization codes, or files with their own pagination. NUL: Null. When used as a source, this creates an immediate EOF (no data). When used as a destination, no output at all is created. Useful for creating empty files, or suppressing output in batch files. In most other respects ARCOPY handles device input/output just as PIP does. One notable exception is that device I/O is buffered in the same way as disk input, rather than sent a character at a time. (Other points: PIP numbers lines on output to PRN:, and doesn't recognize CRT:. ARCOPY doesn't allow RDR, PUN, or AUX; CP/M never supported these devices well enough for them to be useful anyway.) When you use devices you cannot use wildcards or the options "A","C","D", "E", or "N", which refer to file copying only. (Note: as the devices are reserved names, you can't refer to disk files named "CON", etc, unless you do it with wildcards!) USAGE EXAMPLES Each example shows MSDOS COPY syntax on the left, PIP syntax on the right. A>arcopy b:gort [dw] OR A>arcopy a:=b:gort [dw] The file B:GORT will be copied to drive A:, then deleted from drive B:, even if it was Read/Only. A>arcopy *.z80 *.asm [d] OR A>arcopy *.asm=*.z80 [d] Every file A:*.Z80 will get renamed to an extension of ASM instead. (ARCOPY recognizes that no actual copying has to be done.) A>arcopy *.* *.bak OR A>arcopy *.bak=*.* If all goes well, a copy of every file on drive A: will be created with a filetype of ".BAK". However, this might not work; ARCOPY will refuse to do it if filename problems would result. A0>arcopy 1:*.doc *.txt [zc] OR A0>arcopy *.txt=1:*.doc [zc] Each file of type ".DOC" on drive A: user 1 will be copied to user 0, with the same name but type ".TXT" instead of ".DOC". In the process, the high bit will be zeroed out to convert to standard ASCII. You will be asked to confirm each item to copy individually. A>arcopy *.* b: [ae] OR A>arcopy b:=*.* [ae] All files on A: which require archiving (see explanation above), and which already exist on drive B:, will be archived to B:. (This allows you to archive the contents of one directory on more than one floppy disk.) A>arcopy b:*.com [nr] OR A>arcopy a:=b:*.com [nr] All files B:*.COM (including SYStem files) which do not already exist on drive A: will be copied there from drive B:. A>arcopy b:gort.doc con [z] OR A>arcopy con:=b:gort.doc [z] The file B:GORT.DOC will be typed out on screen, stripping off any high bits set. This is a handy way to view Wordstar documents. A0>arcopy 10:prog.asm prn OR A0>arcopy prn:=10:prog.asm The file A10:PROG.ASM will be sent to the printer, with pagination. A>arcopy nul -letters OR A>arcopy -letters=nul: A true empty (0k) file A:-LETTERS will be created. Such files are a popular way to identify or date disk directories. A>arcopy con lst OR A>arcopy lst:=con: Anything you type in will be sent to the printer. Useful for sending setup codes, etc. Exit with ^Z. (If you abort with ^C nothing happens.) A>arcopy con crt OR A>arcopy crt:=con: Anything you type in will be sent back to the screen afterwards, but without control filtering. Useful for setting screen attributes, etc. ERROR MESSAGES These errors indicate that no copying has been done: NO FILE There were no source files matching all given criteria (Filename, existence on destination, archive status) Missing or invalid argument; think (read?) and try again Invalid device name or usage (see DEVICES) ARCOPY can't process more than 255 files; be more specific A file destination would have been the same as its source Two source files would have copied to the same destination A file with an illegal name would have been constructed These errors can occur in the middle of copying: BDOS disk error, probably bad file or full disk Somebody typed ^C If something causes ARCOPY to terminate while copying to disk, an incomplete work file of type ".$$$" may get left behind. Please note that ARCOPY does require a Z80 CPU or equivalent. It will not run on 8080/85 CP/M systems. HISTORY ARCOPY 1.0 (1986) was the first release of this program, chiefly intended to provide incremental backup for CP/M 2.2 users. However, this often didn't work properly under CP/M 2.2 systems, so... ARCOPY 2.0 (1988) added many improvements including MSDOS COPY-like syntax, alphabetization, user number support, and the "E" and "N" options to enhance the choices for selective #backup. f v2.1 (3/88) - copies to/from devices; allows renaming during ambiguous copying; adds "D"elete option; fixes display column bug. ARCOPY and its documentation are (c)1988 E.Meyer, all rights reserved. They may be freely distributed, but not modified or sold for profit without my written consent. The user takes full responsibility for any damages resulting from the use (or misuse) of this program. Please report any problems encountered. Eric Meyer 427 N. Washington #4 CompuServe [74415,1305] Bloomington, IN 47401 ------------------------------------------------------------------------------ @ R+ : o$ DM* +! ))DM  B8! 5: 2 ~#h ~#h f (>_ ( ( 0>^h @ ( ( ( 0 >^͞ @_  > ͞ > _a{__ CONCRTLSTPRNNULRDRPUNAUX This is the release date of the disk. APP20 Z80 |=ARCOPY COM ARCOPY DOC GRCOPY DOC SAPP20 .DOC C3 C2 7040 55 SAPP20 .Z80 E4 CC 32128 251 ARCOPY .COM D8 1A 3072 24 ARCOPY .DOC 1D 68 18176 142 ))[):> :?G[@""w*8:=I+:/G*2: !-I P}"$&EITfm{ Fog Library Disk FOG-CPM.192 Copyright (1988) by Fog International Computer Users Group to the extent not copyrighted by the original author for the exclusive use and enjoyment of its members. Any reproduction or distribution for profit or personal gain is strictly forbidden. For information, contact FOG, P. O. Box 3474, Daly City, CA. 94015-0474. as part of the description of a file indicates that the program is distributed on a "try first, pay if you like it" basis. If you find the program(s) meet your need, please refer to the author's documentation for information on becoming a registered user. Only by registering and paying for the programs you like and use will the authors of such programs continue development. Often, more complete documentation, additional modules, and new releases are available only to registered users. Disk utilities not really intended for the novice user. Be sure to read the documentation carefully and completely as well as making a COMPLETE and CURRENT backup of your system before using these utilities, especially FIRE12 (which does not appear to work on anything other than a Morrow MD 11). Filename Description -06-12 .88 This is the release date of the disk. -CPM192 .DOC This is the description of the disk contents. FIRE12 .COM EED6 4K ver. 12 [File Restoral Util 1 of 11] Utility to restore disk files to "like-new" condition so that the file allocations for each file are in sequential order. ASseMbler (Z80) source is included. As currently released, this program will only work with single partition drives like the Morrow MD11 and is very slow (the author says it took about five hours on his system). FIRE12 .DOC 91D9 9K ver. 12 [File Restoral Util 2 of 11] FIRE12 .Z80 3B87 58K ver. 12 [File Restoral Util 3 of 11] FIRE12 .NOT 64AB 2K ver. 12 [File Restoral Util 4 of 11] DMAP .COM 5FE1 2K [File Restoral Util 5 of 11] Required by FIRE12, this is also a stand-alone program which is a "modified DU3" with only the M option. It prints the disk allocation table on screen, one page at a time. DMAP .DOC 3164 1K ver. 12 [File Restoral Util 6 of 11] RESTORE .BUG 3742 2K ver. 12 [File Restoral Util 7 of 11] RESTORE .DOC 9332 18K ver. 12 [File Restoral Util 8 of 11] SAPP20 .COM B0E8 2K ver. 2.0 [File Restoral Util 9 of 11] Required by FIRE12, SAPP is also a stand-alone program which will sort and pack the directory of most disks under CP/M 2.2 and 3.0. As with all such program, it is strongly recommended that you try it on backup copies of disks until you know for sure it works properly on your system. SAPP20 .DOC C3C2 7K ver. 2.0 [File Restoral Util 10 of 11] SAPP20 .Z80 E4CC 32K ver. 2.0 [File Restoral Util 11 of 11] ARCOPY $ .COM D81A 3K ver. 2.1 [ARchive COPY 1 of 2] A tiny and fast file copy utility for all Z80 CP/M (2.2 or 3.0) systems. It is both an alternative to and a replacement for PIP with all common PIP functions and several useful additions: auto drive reset for CP/M 2.2 systems after disk swaps, alphabetical processing of files with confirmation, ZCPR-like DUU: destination specification, and renaming during copying, etc. ARCOPY .DOC 1D68 18K ver. 2.1 [ARchive COPY 2 of 2] y and fast file copy utility for all Z80 CP/M (2.2 or 3.0) systems. It is both an alternative to and a replacement for PIP with all common PIP functions and several useful additions: auto drive reset for CP/M 2.2 systems after disk swaps, alphabetical processing of files with confirmation, ZCPR-like DUU: destination specification, and renaming during copyi%&'