; SORTV.ASM ver 1.4 ; by Ward Christensen ; (revised 6/29/81) ; ;Simple sort program for sorting lists of names, ;or any other variable length file, with CR/LF ;delimited records. ; ;This is a "simple" program: FILE MUST FIT IN MEMORY. ; ;06/29/81 Cleaned up file and re-tabified it. (KBP) ; ;06/22/81 Changed so MAC is not needed. Changed so alternate or ; standard CP/M may be selected. By Ted Shapin. ; ;01/03/81 Change stack init. By Keith Petersen, W8SDZ ; ;11/15/80 Add @ command (WLC) ; ;10/24/80 Originally written by Ward Christensen ; ;FORMAT: ; SORTV input-name output-name ; OR SORTV name ; ;If the second format is used, the file is read into ;memory, sorted, erased, created, and written back. ; ; The sort will be based on the first characters ; in the file, unless the command is followed by ; an "@" sign, then a string (1 or more characters) ; to skip. If these are present, the line will be ; sorted starting after one of these characters. ; ;Example: SORTV NAMES.SUB @. ; ;Will sort NAMES.SUB by filetype, since it skips past ;the "." before doing the compare. ; EOF EQU 1AH CR EQU 0DH LF EQU 0AH ; BIAS EQU 0 ;0 FOR STANDARD CP/M, 4200H FOR ALTERNATE CP/M ; ;BDOS/CBIOS EQUATES (VERSION 10) ; RDCON EQU 1 WRCON EQU 2 PRINT EQU 9 RDCONBF EQU 10 CONST EQU 11 OPEN EQU 15 CLOSE EQU 16 SRCHF EQU 17 SRCHN EQU 18 ERASE EQU 19 READ EQU 20 WRITE EQU 21 MAKE EQU 22 REN EQU 23 STDMA EQU 26 BDOS EQU 5+BIAS FCB EQU 5CH+BIAS FCB2 EQU 6CH+BIAS FCBEXT EQU FCB+12 FCBRNO EQU FCB+32 ; ORG 100H+BIAS ; ;INIT LOCAL STACK ; LXI SP,STACK ; CALL START DB 'SORTV rev 1.3' DB CR,LF,'$' ; START POP D ;GET ID MVI C,PRINT CALL BDOS ;PRINT ID ; ;START OF PROGRAM EXECUTION ; CALL SVSKIP ;SAVE SKIP INFO CALL CKNAMES ;SEE THAT 2 NAMES ARE THERE CALL OPENIN ;OPEN INPUT FILE CALL READN ;READ THE NAMES CALL SORTN ;SORT THE NAMES CALL WRITEN ;WRITE THE NAMES CALL ERXIT DB '++DONE++$' ; ;====> SUBROUTINES ; ---------------- ; ;====> SAVE "SKIP TO" INFORMATION ; SVSKIP LXI H,81H+BIAS ; SVSKL MOV A,M ORA A RZ ;NO 'SKIP TO' CPI '@' ;SKIP DELIMITER? INX H JNZ SVSKL LXI D,SKIPC ;CHARS TO SKIP ; SVSKL2 MOV A,M STAX D INX H INX D ORA A JNZ SVSKL2 RET ; ;====> CHECK THAT 2 NAMES WERE SUPPLIED ; CKNAMES LDA FCB+1 CPI ' ' JZ NONAME LDA FCB2+1 CPI ' ' JZ SAMENAM CPI '@' ;SKIP PARM? JZ SAMENAM LXI H,FCB2 LXI D,OUTNAME LXI B,12 CALL MOVER RET ; ;OUTPUT NAME = INPUT NAME ; SAMENAM LXI H,FCB LXI D,OUTNAME LXI B,12 CALL MOVER RET ; NONAME CALL ERXIT DB '++Error - ',CR,LF DB 'Command format requires an ' DB 'input name, and an output name.$' ; ;====> OPEN THE INPUT FILE ; OPENIN PUSH B PUSH D PUSH H MVI C,OPEN LXI D,FCB CALL BDOS POP H POP D POP B INR A RNZ ;SUCCESSFUL? RETURN CALL ERXIT DB '++Input file not found$' ; ;====> READ IN THE NAMES ; READN LXI H,SBUFF ;TO FIRST NAME ; READNL CALL READL ;READ ONE LINE RC ;GOT EOF, RETURN CALL CHAIN ;CHAIN THINGS TOGETHER JMP READNL ; ;====> READ ONE LINE ; READL SHLD CURR ;SAVE CURR LINE PTR XRA A ;GET 0 MOV M,A ;INIT FORWARD INX H ; POINTER MOV M,A ; TO INX H ; 0 LXI D,SKIPC ;TO CK SKIP CHARS PRESENT ; READLLP LDA BDOS+2 ;ARE WE DCR A ; OVER- CMP H ; FLOW- JZ OFLO ; ING? PUSH D PUSH H LXI H,EXTFCB CALL RDBYTE ;READ A BYTE POP H POP D CPI EOF ;SET CARRY STC ; AND RETURN RZ ; IF EOF MOV M,A ;STORE CHAR ;TEST FOR SKIP CHAR FOUND MOV B,A ;SAVE FOR COMPARE LDAX D ORA A ;NO MORE SKIP CHARS? JZ READLNS ;NO MORE CMP B ;A SKIP CHAR? JNZ READLNS ;NO, KEEP TRYIN. INX D ;TO NEXT SKIP CHAR ; READLNS INX H ;POINT TO NEXT MOV A,B ;GET CHAR CPI CR ;END OF LINE? JNZ READLLP ; NO, LOOP. PUSH D PUSH H LXI H,EXTFCB CALL RDBYTE ;GOBBLE UP LF POP H POP D LDAX D ;GET SKIP CHAR END ORA A ;TEST IT AND SET "NO EOF" RZ ;ERROR - NO SKIP CHAR LHLD CURR INX H ;SKIP INX H ; POINTER ; ERPLP MOV E,M PUSH B PUSH D PUSH H MVI C,WRCON CALL BDOS POP H POP D POP B MOV A,M INX H CPI CR JNZ ERPLP CALL ERXIT DB LF,'++NO SKIP CHAR FOUND++$' ; OFLO CALL ERXIT DB '++File won''t fit in memory$' ; ;====> CHAIN RECORDS TOGETHER ; CHAIN PUSH H ;SAVE POINTER LHLD CURR ;GET CURRENT XCHG ; TO DE LHLD PREV ;PREV TO HL MOV M,E ;MOVE CURR INX H ; TO MOV M,D ; PREV XCHG ;THEN MOVE SHLD PREV ; PREV TO CURR POP H RET ; ;====> SORT THE NAMES ; SORTN XRA A ;SHOW NO STA SWAPS ; SWAPS LXI H,PTR ;POINT PREV SHLD PREV ; TO PTR LHLD PTR ;POINT TO FIRST ; ;HANDLE WIERD CASE OF ONLY ONE NAME ; MOV A,M ;GET POINTER INX H ;POINT TO NEXT ORA M ;OR TOGETHER DCX H ;BACK UP RZ ;RETURN IF ONLY ONE ; SORTL CALL CMPR ;COMPARE ENTRIES CC SWAP ;SWAP IF WRONG ORDER CALL NEXT ;POINT TO NEXT JNC SORTL ;LOOP IF MORE LDA SWAPS ;ANY ORA A ; SWAPS? JNZ SORTN ;YES, LOOP RET ;NO, RETURN ; ;----> COMPARE TWO NAMES ; CMPR PUSH H ;SAVE POINTER MOV E,M ;GET NEXT INX H ; POINTER MOV D,M ; TO DE INX D ;ALIGN POINTERS ; ;SKIP IF NECESSARY ; LXI B,SKIPC ; TSTSKIP LDAX B ORA A JZ COMPL ;NO SKIP INX B ; SKIP1 INX H CMP M JNZ SKIP1 XCHG ;SWAP ; SKIP2 INX H CMP M JNZ SKIP2 XCHG ;PUT THINGS BACK JMP TSTSKIP ; COMPL INX D ;TO NEXT INX H ;TO NEXT LDAX D ;GET ONE CMP M ;COMPARE JNZ COMPNE ;NO COMPARE CPI CR ;END? JNZ COMPL ; NO, LOOP ; COMPH POP H ;RESTORE POINTER RET ;THEY ARE EQUAL ; ;COMPARE NOT EQUAL - SEE IF END OF ELEMENT, ;AND IF SO, CALL THEM EQUAL ; COMPNE MOV A,M CPI CR JZ COMPH LDAX D CMP M JMP COMPH ;CARRY SET AS APPROP ; ;----> SWAP ENTRIES ; ;LOGIC: PTR POINTS TO SOME ENTRY, WHICH POINTS ;TO ANOTHER ENTRY. THEY ARE NOT IN ORDER. THUS: ;POINT PTR TO THE SECOND, POINT THE SECOND TO ;THE FIRST, AND POINT THE FIRST TO WHAT THE ;SECOND USED TO POINT TO. ; SWAP MVI A,1 STA SWAPS ;SHOW WE SWAPPED ;BC=NEXT MOV C,M INX H MOV B,M DCX H ;CHAIN CURRENT TO NEXT ONES CHAIN LDAX B MOV M,A INX B INX H LDAX B MOV M,A DCX B DCX H ;SAVE CURRENT POINTER IN DE XCHG ;GET POINTER TO PREV LHLD PREV ;POINT PREV TO NEXT MOV M,C INX H MOV M,B ;STORE CURR IN NEXT MOV A,E STAX B INX B MOV A,D STAX B DCX B ;RESTORE CURRENT POINTER XCHG RET ;CURRENT POINTER IN DE ; ;----> GET NEXT ETRY, CARRY IF NOT 2 MORE ; NEXT SHLD PREV ;SAVE POINTER MOV E,M INX H MOV D,M XCHG ;HL= NEXT MOV A,H ;CARRY ON ORA L ; IF HL STC ; = RZ ; 0 MOV A,M ;GET INX H ;SEE IF THERE ORA M ; IS DCX H ; ANOTHER RNZ ;THERE IS ANOTHER STC ;SHOW NOT 2 TO SWAP RET ; ;====> WRITE THE NAMES ; WRITEN LXI H,0 ;INIT SHLD EXTFCB+2 ; EFCB XRA A ;INIT STA FCBEXT ; THE STA FCBRNO ; FCB ;RESTORE NAME LXI H,OUTNAME LXI D,FCB LXI B,12 CALL MOVER PUSH B PUSH D PUSH H MVI C,ERASE LXI D,FCB CALL BDOS POP H POP D POP B PUSH B PUSH D PUSH H MVI C,MAKE LXI D,FCB CALL BDOS POP H POP D POP B INR A ;MAKE OK? JZ BADOUT ; NO, ERROR LHLD PTR ;GET FIRST ; WNLP CALL WRITEL ;WRITE ONE LINE JNC WNLP ;LOOP IF MORE MVI A,EOF ;WRITE EOF CHAR PUSH H LXI H,EXTFCB CALL WRBYTE POP H LXI H,EXTFCB ;FLUSH CALL FLUSH ; BUFFERS PUSH B PUSH D PUSH H MVI C,STDMA ;RESET DMA LXI D,80H+BIAS CALL BDOS POP H POP D POP B PUSH B PUSH D PUSH H MVI C,CLOSE LXI D,FCB CALL BDOS POP H POP D POP B CALL ERXIT ; AND EXIT DB '++DONE++$' ; WRITEL PUSH H ;SAVE POINTER INX H ; WRLP INX H ;TO NEXT CHAR MOV A,M ;GET CHAR PUSH H LXI H,EXTFCB CALL WRBYTE ;WRITE IT POP H MOV A,M ;SEE IF END CPI CR ; OF LINE JNZ WRLP ;NO, LOOP MVI A,LF ;OTHERWISE PUSH H LXI H,EXTFCB CALL WRBYTE ;WRITE LF POP H POP H ;GET POINTER MOV E,M ;GET INX H ; FORWARD MOV D,M ; POINTER XCHG ;PUT IT IN HL MOV A,H ;IS POINTER ORA L ; ZERO? RNZ ;NO, RETURN STC ;CARRY SHOWS END RET ; BADOUT CALL ERXIT DB '++Can''t make output file$' ; ;FOLLOWING FROM 'EQU10.LIB'----> ; ;MOVE, COMPARE SUBROUTINES ; MOVER MOV A,M STAX D INX H INX D DCX B MOV A,B ORA C JNZ MOVER RET ; ; FROM EQU10.LIB: AS OF 07/19/80 ; ;RDBYTE, HL POINTS TO EXTENDED FCB: ; ; 2 BYTE BUFFER ADDR ; 2 BYTE "BYTES LEFT" (INIT TO 0) ; 1 BYTE BUFFER SIZE (IN PAGES) ; 2 BYTE FCB ADDRESS ; RDBYTE MOV E,M INX H MOV D,M ;GET BUFFER ADDR INX H MOV C,M INX H MOV B,M ;BC = BYTES LEFT MOV A,B ;GET COUNT ORA C JNZ RDBNORD ;NO READ ; INX H ;TO BUFFER SIZE MOV A,M ;GET COUNT ADD A ;MULTIPLY BY 2 MOV B,A ;SECTOR COUNT IN B INX H ;TO FCB PUSH H ;SAVE FCB POINTER MOV A,M ;GET.. INX H MOV H,M ;..ADDR.. MOV L,A ;..TO HL ; RDBLP MVI A,1AH ;GET EOF CHAR STAX D ;SAVE IN CASE EOF PUSH D ;SAVE DMA ADDR PUSH H ;SAVE FCB ADDR PUSH B PUSH D PUSH H MVI C,STDMA CALL BDOS ;SET DMA ADDR POP H POP D POP B POP D ;GET FCB PUSH B PUSH D PUSH H MVI C,READ CALL BDOS POP H POP D POP B ORA A POP H ;HL=DMA, DE=FCB JNZ RDBRET ;GOT EOF MOV A,L ADI 80H ;TO NEXT BUFF MOV L,A MOV A,H ACI 0 MOV H,A XCHG ;DMA TO DE, FCB TO HL DCR B ;MORE SECTORS? JNZ RDBLP ;YES, MORE ; RDBRET POP H ;GET FCB POINTER DCX H ;TO LENGTH MOV A,M ;GET LENGTH DCX H ;TO COUNT MOV M,A ;SET PAGE COUNT DCX H ;TO LO COUNT DCX H ;TO HI FCB DCX H ;TO EFCB START JMP RDBYTE ;LOOP THRU AGAIN ; RDBNORD INX H ;TO LENGTH MOV A,M ;GET LENGTH (PAGES) XCHG ;BUFF TO HL ADD H MOV H,A ;HL = END OF BUFF MOV A,L SUB C MOV L,A MOV A,H SBB B MOV H,A ;HL = DATA POINTER MOV A,M ;GET BYTE XCHG ;EFCB BACK TO HL CPI 1AH ;EOF? RZ ;YES, LEAVE POINTERS DCX B ;DECR COUNT DCX H ;"BYTES LEFT" MOV M,B DCX H MOV M,C ;STORE BACK COUNT RET ; ;SAMPLE EFCB: ; ;EFCB DW BUFF ;BUFFER ADDR ; DW 0 ;BYTES LEFT (OR TITE) ; DB 20 ;BUFFER SIZE (IN PAGES) ; DW FCB ;FCB ADDRESS ; ; ;WRBYTE, HL POINTS TO EXTENDED FCB: ; ; 2 BYTE BUFFER ADDR ; 2 BYTE "BYTES LEFT" (INIT TO 0) ; 1 BYTE BUFFER SIZE (IN PAGES) ; 2 BYTE FCB ADDRESS ; WRBYTE MOV E,M INX H MOV D,M ;DE=BUF ADDR INX H MOV C,M INX H MOV B,M ;BC=BYTES IN BUFF PUSH D ;SAVE FCB XCHG DAD B ;TO NEXT BYTE MOV M,A ;STORE IT INX B ;ONE MORE XCHG POP D ; ;SEE IF BUFFER IS FULL ; INX H ;GET MOV A,M ; SIZE CMP B ;FULL? JNZ WRBNOWR ;NO WRITE ; ADD A ;MULTIPLY BY 2 MOV B,A ;SECTOR COUNT IN B INX H ;TO FCB PUSH H ;SAVE FCB POINTER MOV A,M ;GET.. INX H ;..FCB.. MOV H,M ;..ADDR.. MOV L,A ;..TO HL ; WRBLP PUSH D ;SAVE DMA ADDR PUSH H ;SAVE FCB ADDR PUSH B PUSH D PUSH H MVI C,STDMA CALL BDOS ;SET DMA ADDR POP H POP D POP B POP D ;GET FCB PUSH B PUSH D PUSH H MVI C,WRITE CALL BDOS POP H POP D POP B ORA A POP H ;HL=DMA, DE=FCB JNZ WRBERR ;GOT ERR MOV A,L ADI 80H ;TO NEXT BUFF MOV L,A MOV A,H ACI 0 MOV H,A XCHG ;DMA TO DE, FCB TO HL DCR B ;MORE SECTORS? JNZ WRBLP ;YES, MORE ; WRBRET POP H ;GET FCB POINTER DCX H ;TO LENGTH DCX H ;TO COUNT MVI M,0 ;SET 0 TO WRITE DCX H ;TO LO COUNT MVI M,0 PUSH B PUSH D PUSH H MVI C,STDMA LXI D,80H+BIAS CALL BDOS POP H POP D POP B RET ; WRBNOWR DCX H ;TO LENGTH MOV M,B ;SET NEW LENGTH DCX H MOV M,C RET ; ;FLUSH THE EFCB BUFFERS ; FLUSH MOV E,M INX H MOV D,M ;DE=BUF ADDR INX H MOV C,M INX H MOV B,M ;BC=BYTES IN BUFF INX H ;TO COUNT MOV A,B ORA C RZ ;NOTHING TO WRITE MOV A,C ;GET LOW COUNT ADD A ;SHIFT HIGH TO CARRY MOV A,B ;GET LOW COUNTAL RAL ;MULT BY 2, + CARRY INR A ;FUDGE FOR PARTIAL SECT MOV B,A ;SAVE SECTOR COUNT INX H ;TO FCB MOV A,M INX H MOV H,M MOV L,A ;HL=FCB ; FLUSHL PUSH B PUSH D PUSH H MVI C,STDMA CALL BDOS POP H POP D POP B XCHG PUSH B PUSH D PUSH H MVI C,WRITE CALL BDOS POP H POP D POP B XCHG ORA A JNZ WRBERR PUSH H LXI H,80H DAD D XCHG POP H DCR B JNZ FLUSHL XCHG PUSH B PUSH D PUSH H MVI C,CLOSE CALL BDOS POP H POP D POP B INR A RNZ CALL ERXIT DB '++OUTPUT FILE CLOSE ERROR ++$' ; WRBERR CALL ERXIT DB '++OUTPUT FILE WRITE ERROR++$' ; ;EXIT WITH ERROR MESSAGE ; MSGEXIT EQU $ ;EXIT W/"INFORMATIONAL" MSG ; ERXIT POP D ;GET MSG MVI C,PRINT CALL BDOS ; ;EXIT, RESTORING STACK AND RETURN ; EXIT JMP 0+BIAS ; ;====> START OF WORK AREA ; EXTFCB DW DKBUF DW 0 DB 4 DW FCB PREV DW PTR ;POINTER TO PREV POINTER SKIPC DB 0 ;SKIP CHARS END DS 8 ;VARIABLE SKIP CHARS ; DS 100 ;STACK AREA STACK EQU $ ; OUTNAME DS 12 ;OUTPUT FILENAME SWAPS DS 1 CURR DS 2 PTR DS 2 ;POINTER TO FIRST NAME ; ORG ($+255) AND 0FF00H ;TO PAGE ; DKBUF DS 256*4 ;4 PAGES OF BUFFER SBUFF DS 0 ;NAMES READ IN HERE ; END