Title CP/M DIRECTORY SORT V0.1 Subttl Program to produce a sorted directory file .Comment / D. R. Hunt 01.08.80 This program reads in a disk directory from the selected drive and sorts the directory alpha-numerically. The directory is written out in the form of an ASCII file called NAMES.SUB. / .Z80 GLOBAL START LF EQU 0AH ; Line Feed CR EQU 0DH ; Carriage Return CTRLZ EQU 1AH ; ConTRoL Z WBOOT EQU 0000H ; Warm BOOT BDOS EQU 0005H TFCB EQU 005CH ; Temporary File Control Block FCB EQU 0080H ; File Control Block PRS EQU 09H ; PRint String OPNFIL EQU 0FH ; OPeN FILe CLSFIL EQU 10H ; CLoSe FILe SRCHFT EQU 11H ; SeaRCH for FirsT SRCHNX EQU 12H ; SeaRCH for NeXt DELFIL EQU 13H ; DELete FILe WRFIL EQU 15H ; WRite FILe MKFIL EQU 16H ; MaKe FILe DENTLN EQU 11 ; Directory ENTry LeNgth NDENT EQU 128 ; Number of Directory ENTries ; Workspaces DEFS 30H STACK: ; STACK space STKSAV: DEFS 2 ; STacK SAVe location ; TCFB for writing file FNAME: DEFB 0 ; Default drive number DEFM "NAMES SUB" DEFW 0,0 DEFW 0,0,0,0,0,0,0,0 DEFB 0 FNAMEE: ; Messages NOFIL: DEFM "No file found.$",CR,LF NOSPC: DEFM "Can not create NAMES file.$",CR,LF ; Save the stack START: LD HL,0 ADD HL,SP LD (STKSAV),HL LD SP,STACK ; Save requested drive LD A,(TFCB) LD (NCHAR),A ; Erase file NAMES.SUB in case it already exists CALL STTFCB LD DE,TFCB LD C,DELFIL CALL BDOS ; Restore requested drive LD A,(NCHAR) LD (TFCB),A ; Load TFCB with ?'s LD A,"?" LD HL,TFCB+1 LD B,DENTLN LOOP: LD (HL),A INC HL DJNZ LOOP ; Search first LD C,SRCHFT LD DE,TFCB CALL BDOS ; Test for none, if so exit CP 0FFH JP Z,EXIT1 ; Prime buffer and files counter LD DE,DBUF LD B,0 ; Get position in FCB LOOP3: PUSH BC PUSH DE LD DE,20H ; Offset between one entry and the next LD HL,FCB LOOP1: CP 0 ; Test for occurence number JR Z,CONT ; If zero save it ADD HL,DE ; Else ... add in offset PUSH AF ; Save the occurence number XOR A ; Test if HL overflowed OR H JR Z,SKIP ; No overflow so skip ... LD HL,FCB ; ... set HL back SKIP: POP AF ; Restore occurence number and ... DEC A ; ... reduce it by one JR LOOP1 ; Go round again ; Save the file in DBUF CONT: LD B,DENTLN ; Set file length POP DE ; Get DBUF pointer back LOOP5: INC HL LD A,(HL) ; Get a character AND 7FH ; Strip off attribute bit LD (DE),A ; Save in DBUF INC DE DJNZ LOOP5 ; Go for more ? ; Get the next PUSH DE ; Save DBUF pointer LD C,SRCHNX ; Search for next LD DE,TFCB CALL BDOS ; Inc files counter POP DE POP BC ; Get back file counter INC B ; Test for end CP 0FFH JR NZ,LOOP3 ; Go and get more ? ; End, so save count in NENT LD HL,NENT LD (HL),B ; Set up table of directory pointers LD A,B ; Set up counter to No. of entries LD HL,DBUFP ; Point at pointer table start LD DE,DBUF ; Start address of DBUF LD BC,DENTLN ; Load address offset PTRLP: LD (HL),E ; Put DE in DBUFP INC HL LD (HL),D INC HL ; Point to next DBUFP EX DE,HL ; Add in address offset ADD HL,BC EX DE,HL DEC A ; Count one done ... JR NZ,PTRLP ; ... and go again if more. ; Set up for sort LD A,(NENT) ; Put NENT into temp. store. LD (NENT1),A DEC A ; Test if only one entry JR Z,SAVE ; So who wants to sort 1 !!! ; Sort the data CALL SORT ; Save DBUF to disk, skipping spaces and inserting CR, LFs ; Set up TFCB SAVE: CALL STTFCB ; Make file NAMES.SUB LD DE,TFCB LD C,MKFIL CALL BDOS CP 0FFH ; Test for error JP Z,ERR ; Now open the file LD DE,TFCB LD C,OPNFIL CALL BDOS CP 0FFH ; Test for error JP Z,ERR ; Copy down names to FCB, skipping spaces LD IX,DBUFP LD HL,FCB SAVE0: LD E,(IX) LD D,(IX+1) ; Put in FILENAME LD B,8 CALL PUT1 ; Put in `.' LD (HL),"." CALL CHECK ; Test for overflow ; Put in TYP LD B,3 CALL PUT1 ; Put in CR, LF LD (HL),CR CALL CHECK ; Test for overflow LD (HL),LF CALL CHECK ; Test for overflow ; Point to next in DBUFP INC IX INC IX ; Dec counter LD A,(NENT) DEC A LD (NENT),A ; Test for last OR A JR NZ,SAVE0 ; Finish file with CTRLZ LD (HL),CTRLZ ; Send last sector CALL WRITE ; Close file LD DE,TFCB LD C,CLSFIL CALL BDOS CP 0FFH ; Test for error JR Z,ERR JP UCAT ; ******* SUBROUTINES ******* ; Set up TFCB STTFCB: LD HL,FNAME ; Point to name table LD DE,TFCB ; Point to TFCB LD BC,FNAMEE-FNAME LDIR ; Copy down data RET ; This subroutine copies from DBUF to FCB PUT1: LD A,(DE) ; Get the character CP " " ; Is it a space JR Z,PUT2 ; Yup, so don't ... LD (HL),A ; ... copy to FCB CALL CHECK ; Check for FCB full PUT2: INC DE ; Point to next DJNZ PUT1 ; More to do ? RET ; This routine inc HL, checks for overflow ; and write buffer out CHECK: INC HL ; Point to next XOR A ; Clear A OR H ; Text H for overflow RET Z ; No overflow, so go home CALL WRITE ; Buffer full, so write it LD HL,FCB ; Reset FCB RET ; This routine write out the current FCB WRITE: PUSH AF ; Save pointers PUSH DE PUSH BC LD DE,TFCB ; Point to TFCB LD C,WRFIL ; Write to disk CALL BDOS CP 0FFH ; Test for error JR Z,ERR POP BC ; Restore pointers POP DE POP AF RET ; Error so report and go home ERR: LD DE,NOSPC ; Print error message LD C,PRS CALL BDOS JP WBOOT ; Exit to CP/M .Comment / ********************************************* ********* ALPHA-NUMERIC SORT MODULE ********* ********************************************* Works by successively comparing two adjacent names in the main array, and setting the pointers in the pointer array into a new order reflecting the ascending alpha-numeric order of the main array. Requires two arrays, one containing the data to be sorted. All items in this (main) array must be of equal length, and where necessary, the lengths padded with characters of a value equal or less that the lowest value to be sorted. With alpha-numeric strings, these would normally be `spaces'. The second array is a table of pointers pointing to the start of each string in the main array. The routine is entered with pointers to the data sets to be sorted, set up in DBUFP and the count of data sets to be sorted in NENT1. The routine returns with NENT1 decremented to zero, and the pointers in array DBUFP pointing to the data sets in the DBUF array in ascending alpha-numeric order. Workspaces NCHAR, and NENT2 are used internally by the routine. Note that this version sets no limits on the length of the search for a mismatch. This is because in this instance it is assumed that: 1) That there is more that one item to sort. 2) That these data sets are all different in some respect. / SORT: XOR A ; Clear `sorted' counter LD (NCHAR),A LD A,(NENT1) ; Dec number of entries sorted DEC A LD (NENT2),A LD (NENT1),A RET Z ; If A zero, all done. EXIT. LD HL,DBUFP ; Get DBUFP start ; Compare two adjacent arrays until mismatch found SORT1: CALL CP ; Scan for mismatch CALL M,SWOP ; Upper < lower so swop INC HL ; Point to next in DBUFP INC HL LD A,(NENT2) ; Reduce next entry count DEC A LD (NENT2),A JR NZ,SORT1 ; More to do? LD A,(NCHAR) OR A JR NZ,SORT ; This routine compares two arrays until a mismatch found ; The C flag set indicates upper < lower ; (BC) X X X X X ....... ; CP CP CP CP CP ....... ; (HL) Y Y Y Y Y ....... CP: PUSH HL ; Save pointer to DBUFP LD E,(HL) ; Put lower address in DE INC HL LD D,(HL) INC HL LD C,(HL) ; Put upper address in BC INC HL LD B,(HL) EX DE,HL ; Swop HL -> lower CP1: LD A,(BC) ; Compare (BC) with (HL) CP (HL) INC HL ; Point to next char INC BC JR Z,CP1 ; They match, so try next POP HL RET ; C flag set when upper < lower ; This routine reverses two consec addresses in DBUFP SWOP: LD A,1 ; Set NCHAR to show swop LD (NCHAR),A LD C,(HL) ; Get pointer to lower in BC INC HL PUSH HL ; Save HL for return swop LD B,(HL) INC HL LD E,(HL) ; Get pointer to upper in DE ... LD (HL),C ; ... and replace with lower INC HL LD D,(HL) LD (HL),B POP HL ; Get pointer back for return journey LD (HL),D ; Replace lower with upper DEC HL LD (HL),E RET ;******************************************************** ;******************************************************** ; ****************** ; ****** UCAT ****** ; ****************** X0000 EQU 00000H X0005 EQU 00005H Y005D EQU 0005DH Y0067 EQU 00067H UCAT: JP A0131 ; T010A: DEFB 000H DEFB "MAST CAT" D0116: DEFB 000H DEFB 076H,001H,0FEH,03BH DEFB 0C2H,016H,00EH,0CDH,0F4H DEFB 00EH,0CDH,006H,016H DEFB 03AH,005H,030H,0FEH DEFB 004H,0C2H D012A: DEFB 0F6H D012B: DEFB 077H,00CH D012D: DEFB 000H,014H D012F: DEFB 0FEH,00AH A0131: JP A01A1 ; A0134: LD HL,(D012D) EX DE,HL LD HL,(D012F) LD A,L SUB E LD A,H SBC A,D JP C,A018A LD HL,00000H LD (D012F),HL A0148: EX DE,HL LD HL,(D012D) LD A,E SUB L LD A,D SBC A,H JP NC,A017C LD HL,(D012B) ADD HL,DE EX DE,HL LD C,01AH CALL X0005 LD DE,T010A LD C,014H CALL X0005 OR A JP NZ,A0176 LD DE,00080H LD HL,(D012F) ADD HL,DE LD (D012F),HL JP A0148 ; A0176: LD HL,(D012F) LD (D012D),HL A017C: LD DE,00080H LD C,01AH CALL X0005 LD HL,00000H LD (D012F),HL A018A: EX DE,HL LD HL,(D012B) ADD HL,DE EX DE,HL LD HL,(D012D) LD A,L OR H LD A,01AH RET Z LD A,(DE) LD HL,(D012F) INC HL LD (D012F),HL RET ; A01A1: XOR A LD (D0116),A LD (D012A),A LD HL,01400H LD (D012D),HL LD (D012F),HL LD C,00FH LD DE,T010A CALL X0005 INC A JP NZ,A01D9 LD C,009H LD DE,T01C8 CALL X0005 JP EXIT ; T01C8: DEFB CR,LF DEFB "No Mastin file$" A01D9: JP A0203 T01DC: DEFB 000H DEFB "NAMES SUB" D01E8: DEFB 000H,0B5H,00EH DEFB "H USE FACTOR" DEFB 00DH,000H,021H,08EH,02FH D01FC: DEFB 0CDH D01FD: DEFB 077H,020H D01FF: DEFB 000H,014H D0201: DEFB 022H,0E9H A0203: JP A0273 ; A0206: LD HL,(D01FF) EX DE,HL LD HL,(D0201) LD A,L SUB E LD A,H SBC A,D JP C,A025C LD HL,00000H LD (D0201),HL A021A: EX DE,HL LD HL,(D01FF) LD A,E SUB L LD A,D SBC A,H JP NC,A024E LD HL,(D01FD) ADD HL,DE EX DE,HL LD C,01AH CALL X0005 LD DE,T01DC LD C,014H CALL X0005 OR A JP NZ,A0248 LD DE,00080H LD HL,(D0201) ADD HL,DE LD (D0201),HL JP A021A ; A0248: LD HL,(D0201) LD (D01FF),HL A024E: LD DE,00080H LD C,01AH CALL X0005 LD HL,00000H LD (D0201),HL A025C: EX DE,HL LD HL,(D01FD) ADD HL,DE EX DE,HL LD HL,(D01FF) LD A,L OR H LD A,01AH RET Z LD A,(DE) LD HL,(D0201) INC HL LD (D0201),HL RET ; A0273: XOR A LD (D01E8),A LD (D01FC),A LD HL,01400H LD (D01FF),HL LD (D0201),HL LD C,00FH LD DE,T01DC CALL X0005 INC A JP NZ,A02AA LD C,009H LD DE,T029A CALL X0005 JP EXIT ; T029A: DEFB CR,LF DEFB "No NAMES file$" A02AA: JP A02D4 ; T02AD: DEFB 000H DEFB "NEW CAT" D02B9: DEFB 000H JP NZ,A01D9 LD C,009H LD DE,T01C8 CALL X0005 JP EXIT DEFB 00DH,00AH,04EH,04FH,020H D02CD: DEFB 04DH D02CE: DEFB 077H,034H D02D0: DEFB 000H,014H D02D2: DEFB 04EH,020H A02D4: JP A0359 ; A02D7: PUSH AF LD HL,(D02D0) EX DE,HL LD HL,(D02D2) LD A,L SUB E LD A,H SBC A,D JP C,A0349 LD HL,00000H LD (D02D2),HL A02EC: EX DE,HL LD HL,(D02D0) LD A,E SUB L LD A,D SBC A,H JP NC,A033B LD HL,(D02CE) ADD HL,DE EX DE,HL LD C,01AH CALL X0005 LD DE,T02AD LD C,015H CALL X0005 OR A JP NZ,A031A LD DE,00080H LD HL,(D02D2) ADD HL,DE LD (D02D2),HL JP A02EC ; A031A: LD C,009H LD DE,T0326 CALL X0005 POP AF JP EXIT ; T0326: DEFB CR,LF DEFB "Disk full: Mastout$" A033B: LD DE,00080H LD C,01AH CALL X0005 LD HL,00000H LD (D02D2),HL A0349: EX DE,HL LD HL,(D02CE) ADD HL,DE EX DE,HL POP AF LD (DE),A LD HL,(D02D2) INC HL LD (D02D2),HL RET ; A0359: XOR A LD (D02B9),A LD (D02CD),A LD HL,01400H LD (D02D0),HL LD HL,00000H LD (D02D2),HL LD C,013H LD DE,T02AD CALL X0005 LD C,016H LD DE,T02AD CALL X0005 INC A JP NZ,A03A3 LD C,009H LD DE,T038B CALL X0005 JP EXIT ; T038B: DEFB CR,LF DEFB "No directory space: Mastout$" A03A3: JP A03C7 T03A6: DEFB 000H DEFB "MAST BAK" DEFB 000H DEFB 020H,020H,020H,043H DEFB 041H,054H,000H JP NZ,A01D9 LD C,009H LD DE,T01C8 CALL X0005 DEFB 0C3H,0E8H A03C7: CALL A05D1 LD B,00CH LD DE,T081F LD HL,T086A CALL A0738 JP Z,A03C7 LD A,(T081F) CP 02DH JP Z,A0462 LD A,(Y005D) CP 02DH JP NZ,A0426 LD A,(Y0067) CP 020H JP NZ,A040D CALL A07E2 DEFB "++Serial must" T0400: DEFB " be 3 digits$" A040D: LD B,007H LD DE,0005EH LD HL,T082C CALL A072F LD B,003H LD DE,00065H LD HL,T0835 CALL A072F JP A0478 ; A0426: CALL A07E2 DEFB '++No "-NAME.NNN" on disk' DEFB CR,LF DEFB "++Restart with properly set up disk$" A0462: LD B,007H LD DE,T0820 LD HL,T082C CALL A072F LD B,003H LD DE,T0828 LD HL,T0835 CALL A072F A0478: CALL A0134 JP Z,A04CD CP 028H JP NZ,A04CD CALL A02D7 LD HL,T0876 LD BC,T0400 A048C: PUSH BC PUSH HL CALL A0134 JP Z,A04EE PUSH AF CALL A02D7 POP AF POP HL POP BC LD (HL),A INC HL CP 029H JP Z,A0534 DEC BC LD A,B OR C JP NZ,A048C CALL A07E2 DEFB '++ Too many "ignore" names in MAST.CAT$' A04CD: CALL A07E2 DEFB '++ No "ignore" names in MAST.CAT$' A04EE: CALL A07E2 DEFB '++ EOF read in "ignore" names,' DEFB CR,LF DEFB " may be trailing `)' missing after last name.$" A0534: CALL A0134 JP Z,A04EE PUSH AF CALL A02D7 POP AF CP 00AH JP NZ,A0534 JP A054A ; A0547: CALL A05D1 A054A: CALL A0657 A054D: LD A,(T081D) OR A JP Z,A055B LD A,(T081E) OR A JP NZ,A0742 A055B: LD B,019H LD DE,T081F LD HL,T0838 CALL A0738 JP Z,A05C0 JP C,A059D LD B,00CH LD DE,T0845 LD HL,T082C CALL A0738 JP Z,A058B LD B,019H LD DE,T0838 LD HL,T0851 CALL A072F CALL A06C0 JP A054A ; A058B: LD DE,T0597 LD HL,T0838 CALL A06FA JP A054A ; T0597: DEFB "Del: $" A059D: LD B,019H LD DE,T081F LD HL,T0851 CALL A072F CALL A06C0 LD DE,T05BA LD HL,T081F CALL A06FA CALL A05D1 JP A054D ; T05BA: DEFB "Add: $" A05C0: LD B,019H LD DE,T0838 LD HL,T0851 CALL A072F CALL A06C0 JP A0547 ; A05D1: LD HL,T081F LD B,008H CALL A060C LD (HL),02EH INC HL LD B,003H CALL A060C LD (HL),02CH LD DE,T0876 A05E6: LD HL,T081F LD B,00CH A05EB: LD A,(DE) CP (HL) JP Z,A0603 LD A,(HL) CP 020H JP Z,A0604 A05F6: LD A,(DE) INC DE CP 029H RET Z CP 00AH JP NZ,A05F6 JP A05E6 ; A0603: INC DE A0604: INC HL DEC B JP NZ,A05EB JP A05D1 ; A060C: LD A,(T081D) OR A JP NZ,A064A PUSH HL PUSH BC A0615: CALL A0206 CP 00AH JP Z,A0615 POP BC POP HL CP 02EH JP Z,A0642 CP 00DH JP Z,A0642 CP 01AH JP Z,A064A LD (HL),A INC HL DEC B JP NZ,A060C PUSH BC PUSH HL CALL A0206 POP HL POP BC CP 01AH RET NZ LD (T081D),A RET ; A0642: LD (HL),020H INC HL DEC B JP NZ,A0642 RET ; A064A: LD (HL),0FFH INC HL DEC B JP NZ,A064A LD A,001H LD (T081D),A RET ; A0657: LD HL,T0838 CALL A0664 LD (HL),02CH INC HL CALL A0664 RET ; A0664: LD B,008H CALL A0672 LD (HL),02EH INC HL LD B,003H CALL A0672 RET ; A0672: LD A,(T081E) OR A JP NZ,A06B5 PUSH HL PUSH BC A067B: CALL A0134 CP 00AH JP Z,A067B POP BC POP HL CP 02CH JP Z,A06AD CP 02EH JP Z,A06AD CP 00DH JP Z,A06AD CP 01AH JP Z,A06B5 LD (HL),A INC HL DEC B JP NZ,A0672 PUSH BC PUSH HL CALL A0134 POP HL POP BC CP 01AH RET NZ LD (T081E),A RET ; A06AD: LD (HL),020H INC HL DEC B JP NZ,A06AD RET ; A06B5: LD (HL),0FFH INC HL DEC B JP NZ,A06B5 LD (T081E),A RET ; A06C0: LD HL,T07D7 A06C3: LD A,(HL) CP 020H JP NZ,A06CB LD A,030H A06CB: INC A LD (HL),A CP 03AH JP NZ,A06D8 LD (HL),030H DEC HL JP A06C3 ; A06D8: LD HL,T0851 LD B,019H A06DD: LD A,(HL) CP 020H JP Z,A06EA PUSH BC PUSH HL CALL A02D7 POP HL POP BC A06EA: INC HL DEC B JP NZ,A06DD LD A,00DH CALL A02D7 LD A,00AH CALL A02D7 RET ; A06FA: PUSH HL LD C,009H CALL X0005 POP HL LD B,00CH A0703: PUSH HL PUSH BC LD A,(HL) CP 020H JP Z,A0713 PUSH AF LD C,002H LD E,A CALL X0005 POP AF A0713: POP BC POP HL INC HL DEC B JP NZ,A0703 LD A,00DH PUSH AF LD C,002H LD E,A CALL X0005 POP AF LD A,00AH PUSH AF LD C,002H LD E,A CALL X0005 POP AF RET ; A072F: LD A,(DE) LD (HL),A INC DE INC HL DEC B JP NZ,A072F RET ; A0738: LD A,(DE) CP (HL) RET NZ INC DE INC HL DEC B JP NZ,A0738 RET ; A0742: LD A,01AH CALL A02D7 A0747: LD HL,(D02D2) LD A,L AND 07FH JP NZ,A0753 LD (D02D0),HL A0753: LD A,01AH PUSH AF CALL A02D7 POP AF JP NZ,A0747 LD C,010H LD DE,T02AD CALL X0005 INC A JP NZ,A078B LD C,009H LD DE,T0774 CALL X0005 JP A078B ; T0774: DEFB CR,LF DEFB "Cannot close Mastout$" A078B: LD C,013H LD DE,T03A6 CALL X0005 LD C,013H LD DE,T01DC CALL X0005 JP A07B2 ; A079E: PUSH HL LD BC,00010H ADD HL,BC A07A3: LD A,(DE) LD (HL),A INC DE INC HL DEC C JP NZ,A07A3 POP DE LD C,017H CALL X0005 RET ; A07B2: LD HL,T010A LD DE,T03A6 CALL A079E LD HL,T02AD LD DE,T010A CALL A079E CALL A07E2 DEFB "MAST.CAT has " T07D7: DEFB " entries.$" A07E2: POP DE LD C,009H CALL X0005 JR EXIT ; Exit from routine EXIT1: LD DE,NOFIL ; No files found so tell 'em LD C,PRS CALL BDOS EXIT: LD HL,(STKSAV) ; Restore orginal stack LD SP,HL RET ; Go home T081D: DEFB 000H T081E: DEFB 000H T081F: DEFB "X" T0820: DEFB "XXXXXXX." T0828: DEFB "YYY," T082C: DEFB " ." T0835: DEFB " " T0838: DEFB "XXXXXXXX.YYY," T0845: DEFB "XXXXXXXX.YYY" T0851: DEFB "XXXXXXXX.YYY," DEFB "XXXXXXXX.YYY" T086A: DEFB "$$$ .SUB" T0876: DEFB ")" DEFB 000H,000H,000H,000H DEFB 000H,000H,000H,000H NENT: DEFS 1 ; Number of directory ENTries NENT1: DEFS 1 ; Number of directory ENTries work store NENT2: DEFS 1 NCHAR: DEFS 1 DBUFP: DEFS NDENT*2 ; Directory BUFfer Pointers DBUF: ;DEFS NDENT*12 Directory BUFFer END NT2: DEFS 1 N