; SORTV.ASM ver 1.2 ; by Ward Christensen ; (revised 1/3/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. ; ;--->NEEDS MAC TO ASSEMBLE<--- ; ;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. ; ;EX: 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 ; ;(FROM EQU10.LIB...) ; MF SET 0 ;SHOW MOVE NOT REQUESTED CF SET 0 ;SHOW COMP NOT REQUESTED ; ;DEFINE SOME MACROS TO MAKE THINGS EASIER ; ;DEFINE DATA MOVE MACRO: MOVE from,to,length ; from may be addr, or quoted string ; MOVE MACRO ?F,?T,?L MCSUB ?F,?T,?L ;;HANDLE ARGS CALL MOVER MF SET -1 ;;SHOW EXPANSION ENDM ; ;COMPARE MACRO COMP MACRO ?F,?T,?L MCSUB ?F,?T,?L ;;HANDLE ARGS CALL COMPR CF SET -1 ;;SHOW EXPANSION ENDM ; ;MCSUB - HANDLES MOVE, COMPARE ARGUMENTS MCSUB MACRO ?F,?T,?L IF NOT NUL ?F IRPC ?C,?F ?Q SET '&?C&?C' ;;TEST FOR QUOTE EXITM ENDM IF ?Q EQ '''' LOCAL ?B,?Z CALL ?Z ?B DB ?F ?Z POP H ;GET FROM LXI B,?Z-?B ;GET LEN ELSE LXI H,?F ENDIF ENDIF IF NOT NUL ?T LXI D,?T ENDIF IF NOT NUL ?L LXI B,?L ENDIF ENDM ; ;DEFINE CP/M MACRO - CPM FNC,PARM [,NOSAVE] ; CPM MACRO ?F,?P,?N IF NUL ?N PUSH B PUSH D PUSH H ENDIF IF NOT NUL ?F MVI C,?F ENDIF IF NOT NUL ?P LXI D,?P ENDIF CALL BDOS IF NUL ?N POP H POP D POP B ENDIF ENDM ; RDB MACRO ?F PUSH D PUSH H LXI H,?F CALL RDBYTE POP H POP D ENDM ; WRB MACRO ?F PUSH H LXI H,?F CALL WRBYTE POP H ENDM ; EFCB MACRO ?B,?P,?F DW ?B DW 0 DB ?P DW ?F ENDM ; ORG 100H ; ;INIT LOCAL STACK ; LXI SP,STACK ; CALL START DB 'SORTV rev 1.2' 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 ; 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 MOVE FCB2,OUTNAME,12 RET ; ;OUTPUT NAME = INPUT NAME ; SAMENAM MOVE FCB,OUTNAME,12 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 CPM OPEN,FCB INR A RNZ ;SUCCESSFUL? RETURN CALL ERXIT DB '++Input file not found$' ; ;====> READ IN THE NAMES ; READN LXI H,BUFF ;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? RDB EXTFCB ;READ A BYTE 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. RDB EXTFCB ;GOBBLE UP LF 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 CPM WRCON 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 NECESS ; 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 ENTRY, 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 MOVE OUTNAME,FCB,12 ;RESTORE NAME CPM ERASE,FCB CPM MAKE,FCB 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 WRB EXTFCB ; CHAR LXI H,EXTFCB ;FLUSH CALL FLUSH ; BUFFERS CPM STDMA,80H ;RESET DMA CPM CLOSE,FCB ;CLOSE, CALL ERXIT ; AND EXIT DB '++DONE++$' ; WRITEL PUSH H ;SAVE POINTER INX H ; WRLP INX H ;TO NEXT CHAR MOV A,M ;GET CHAR WRB EXTFCB ;WRITE IT MOV A,M ;SEE IF END CPI CR ; OF LINE JNZ WRLP ;NO, LOOP MVI A,LF ;OTHERWISE WRB EXTFCB ; WRITE LF 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 ; IF MF ;MACRO EXPANSION FLAG SET? MOVER MOV A,M STAX D INX H INX D DCX B MOV A,B ORA C JNZ MOVER RET ENDIF ; IF CF ;MACRO EXPANSION FLAG SET? COMPR LDAX D CMP M RNZ INX D INX H DCX B MOV A,B ORA C JNZ COMPR RET ENDIF ; ; 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 ;..FCB.. 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 CPM STDMA ;SET DMA ADDR POP D ;GET FCB CPM READ 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 ;BACK TO "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 TO WRITE) ; 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 CPM STDMA ;SET DMA ADDR POP D ;GET FCB CPM WRITE 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 CPM STDMA,80H 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 CPM STDMA XCHG CPM WRITE XCHG ORA A JNZ WRBERR PUSH H LXI H,80H DAD D XCHG POP H DCR B JNZ FLUSHL XCHG CPM CLOSE 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 ; ;====> START OF WORK AREA ; EXTFCB EFCB DKBUF,4,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 BUFF EQU $ ;NAMES READ IN HERE ; ;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 FCB EQU 5CH FCB2 EQU 6CH FCBEXT EQU FCB+12 FCBRNO EQU FCB+32 ; END