; SORTV.ASM ver 1.5 ; by Ward Christensen ; (revised 1/26/86) ; ;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. ; ;01/26/86 Changed to z80 code and added ? wildcard skip character ; for fixed column number sort. D. L. Anderton ; ;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 ; LD SP,STACK ; CALL START DEFB 'SORTV rev 1.5' DEFB CR,LF,'$' ; START: POP DE ;GET ID LD 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 DEFB '++DONE++$' ; ;====> SUBROUTINES ; ---------------- ; ;====> SAVE "SKIP TO" INFORMATION ; SVSKIP: LD HL,81H+BIAS ; SVSKL: LD A,(HL) OR A RET Z ;NO 'SKIP TO' CP '@' ;SKIP DELIMITER? INC HL JP NZ,SVSKL LD DE,SKIPC ;CHARS TO SKIP ; SVSKL2: LD A,(HL) LD (DE),A INC HL INC DE OR A JP NZ,SVSKL2 RET ; ;====> CHECK THAT 2 NAMES WERE SUPPLIED ; CKNAMES:LD A,(FCB+1) CP ' ' JP Z,NONAME LD A,(FCB2+1) CP ' ' JP Z,SAMENAM CP '@' ;SKIP PARM? JP Z,SAMENAM LD HL,FCB2 LD DE,OUTNAME LD BC,12 CALL MOVER RET ; ;OUTPUT NAME = INPUT NAME ; SAMENAM:LD HL,FCB LD DE,OUTNAME LD BC,12 CALL MOVER RET ; NONAME: CALL ERXIT DEFB '++Error - ',CR,LF DEFB 'Command format requires an ' DEFB 'input name, and an output name.$' ; ;====> OPEN THE INPUT FILE ; OPENIN: PUSH BC PUSH DE PUSH HL LD C,OPEN LD DE,FCB CALL BDOS POP HL POP DE POP BC INC A RET NZ ;SUCCESSFUL? RETURN CALL ERXIT DEFB '++Input file not found$' ; ;====> READ IN THE NAMES ; READN: LD HL,SBUFF ;TO FIRST NAME ; READNL: CALL READL ;READ ONE LINE RET C ;GOT EOF, RETURN CALL CHAIN ;CHAIN THINGS TOGETHER JP READNL ; ;====> READ ONE LINE ; READL: LD (CURR),HL ;SAVE CURR LINE PTR XOR A ;GET 0 LD (HL),A ;INIT FORWARD INC HL ; POINTER LD (HL),A ; TO INC HL ; 0 LD DE,SKIPC ;TO CK SKIP CHARS PRESENT ; READLLP:LD A,(BDOS+2) ;ARE WE DEC A ; OVER- CP H ; FLOW- JP Z,OFLO ; ING? PUSH DE PUSH HL LD HL,EXTFCB CALL RDBYTE ;READ A BYTE POP HL POP DE CP EOF ;SET CARRY SCF ; AND RETURN RET Z ; IF EOF LD (HL),A ;STORE CHAR ;TEST FOR SKIP CHAR FOUND LD B,A ;SAVE FOR COMPARE LD A,(DE) OR A ;NO MORE SKIP CHARS? JP Z,READLNS ;NO MORE ;TEST FOR SKIP WILDCARD ? CP '?' JP Z,WLDCRD ;OR SKIP CHAR CP B ;A SKIP CHAR? JP NZ,READLNS ;NO, KEEP TRYIN. WLDCRD: INC DE ;TO NEXT SKIP CHAR ; READLNS:INC HL ;POINT TO NEXT LD A,B ;GET CHAR CP CR ;END OF LINE? JP NZ,READLLP ; NO, LOOP. PUSH DE PUSH HL LD HL,EXTFCB CALL RDBYTE ;GOBBLE UP LF POP HL POP DE LD A,(DE) ;GET SKIP CHAR END OR A ;TEST IT AND SET "NO EOF" RET Z ;ERROR - NO SKIP CHAR LD HL,(CURR) INC HL ;SKIP INC HL ; POINTER ; ERPLP: LD E,(HL) PUSH BC PUSH DE PUSH HL LD C,WRCON CALL BDOS POP HL POP DE POP BC LD A,(HL) INC HL CP CR JP NZ,ERPLP CALL ERXIT DEFB LF,'++NO SKIP CHAR FOUND++$' ; OFLO: CALL ERXIT DEFB '++File won''t fit in memory$' ; ;====> CHAIN RECORDS TOGETHER ; CHAIN: PUSH HL ;SAVE POINTER LD HL,(CURR) ;GET CURRENT EX DE,HL ; TO DE LD HL,(PREV) ;PREV TO HL LD (HL),E ;MOVE CURR INC HL ; TO LD (HL),D ; PREV EX DE,HL ;THEN MOVE LD (PREV),HL ; PREV TO CURR POP HL RET ; ;====> SORT THE NAMES ; SORTN: XOR A ;SHOW NO LD (SWAPS),A ; SWAPS LD HL,PTR ;POINT PREV LD (PREV),HL ; TO PTR LD HL,(PTR) ;POINT TO FIRST ; ;HANDLE WIERD CASE OF ONLY ONE NAME ; LD A,(HL) ;GET POINTER INC HL ;POINT TO NEXT OR (HL) ;OR TOGETHER DEC HL ;BACK UP RET Z ;RETURN IF ONLY ONE ; SORTL: CALL CMPR ;COMPARE ENTRIES CALL C,SWAP ;SWAP IF WRONG ORDER CALL NEXT ;POINT TO NEXT JP NC,SORTL ;LOOP IF MORE LD A,(SWAPS) ;ANY OR A ; SWAPS? JP NZ,SORTN ;YES, LOOP RET ;NO, RETURN ; ;----> COMPARE TWO NAMES ; CMPR: PUSH HL ;SAVE POINTER LD E,(HL) ;GET NEXT INC HL ; POINTER LD D,(HL) ; TO DE INC DE ;ALIGN POINTERS ; ;SKIP IF NECESSARY ; LD BC,SKIPC ; TSTSKIP:LD A,(BC) OR A JP Z,COMPL ;NO SKIP INC BC ; SKIP1: INC HL CP '?' JP Z,WLD2 CP (HL) JP NZ,SKIP1 WLD2: EX DE,HL ;SWAP ; SKIP2: INC HL CP '?' JP Z,WLD3 CP (HL) JP NZ,SKIP2 WLD3: EX DE,HL ;PUT THINGS BACK JP TSTSKIP ; COMPL: INC DE ;TO NEXT INC HL ;TO NEXT LD A,(DE) ;GET ONE CP (HL) ;COMPARE JP NZ,COMPNE ;NO COMPARE CP CR ;END? JP NZ,COMPL ; NO, LOOP ; COMPH: POP HL ;RESTORE POINTER RET ;THEY ARE EQUAL ; ;COMPARE NOT EQUAL - SEE IF END OF ELEMENT, ;AND IF SO, CALL THEM EQUAL ; COMPNE: LD A,(HL) CP CR JP Z,COMPH LD A,(DE) CP (HL) JP 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: LD A,1 LD (SWAPS),A ;SHOW WE SWAPPED ;BC=NEXT LD C,(HL) INC HL LD B,(HL) DEC HL ;CHAIN CURRENT TO NEXT ONES CHAIN LD A,(BC) LD (HL),A INC BC INC HL LD A,(BC) LD (HL),A DEC BC DEC HL ;SAVE CURRENT POINTER IN DE EX DE,HL ;GET POINTER TO PREV LD HL,(PREV) ;POINT PREV TO NEXT LD (HL),C INC HL LD (HL),B ;STORE CURR IN NEXT LD A,E LD (BC),A INC BC LD A,D LD (BC),A DEC BC ;RESTORE CURRENT POINTER EX DE,HL RET ;CURRENT POINTER IN DE ; ;----> GET NEXT ETRY, CARRY IF NOT 2 MORE ; NEXT: LD (PREV),HL ;SAVE POINTER LD E,(HL) INC HL LD D,(HL) EX DE,HL ;HL= NEXT LD A,H ;CARRY ON OR L ; IF HL SCF ; = RET Z ; 0 LD A,(HL) ;GET INC HL ;SEE IF THERE OR (HL) ; IS DEC HL ; ANOTHER RET NZ ;THERE IS ANOTHER SCF ;SHOW NOT 2 TO SWAP RET ; ;====> WRITE THE NAMES ; WRITEN: LD HL,0 ;INIT LD (EXTFCB+2),HL ; EFCB XOR A ;INIT LD (FCBEXT),A ; THE LD (FCBRNO),A ; FCB ;RESTORE NAME LD HL,OUTNAME LD DE,FCB LD BC,12 CALL MOVER PUSH BC PUSH DE PUSH HL LD C,ERASE LD DE,FCB CALL BDOS POP HL POP DE POP BC PUSH BC PUSH DE PUSH HL LD C,MAKE LD DE,FCB CALL BDOS POP HL POP DE POP BC INC A ;MAKE OK? JP Z,BADOUT ; NO, ERROR LD HL,(PTR) ;GET FIRST ; WNLP: CALL WRITEL ;WRITE ONE LINE JP NC,WNLP ;LOOP IF MORE LD A,EOF ;WRITE EOF CHAR PUSH HL LD HL,EXTFCB CALL WRBYTE POP HL LD HL,EXTFCB ;FLUSH CALL FLUSH ; BUFFERS PUSH BC PUSH DE PUSH HL LD C,STDMA ;RESET DMA LD DE,80H+BIAS CALL BDOS POP HL POP DE POP BC PUSH BC PUSH DE PUSH HL LD C,CLOSE LD DE,FCB CALL BDOS POP HL POP DE POP BC CALL ERXIT ; AND EXIT DEFB '++DONE++$' ; WRITEL: PUSH HL ;SAVE POINTER INC HL ; WRLP: INC HL ;TO NEXT CHAR LD A,(HL) ;GET CHAR PUSH HL LD HL,EXTFCB CALL WRBYTE ;WRITE IT POP HL LD A,(HL) ;SEE IF END CP CR ; OF LINE JP NZ,WRLP ;NO, LOOP LD A,LF ;OTHERWISE PUSH HL LD HL,EXTFCB CALL WRBYTE ;WRITE LF POP HL POP HL ;GET POINTER LD E,(HL) ;GET INC HL ; FORWARD LD D,(HL) ; POINTER EX DE,HL ;PUT IT IN HL LD A,H ;IS POINTER OR L ; ZERO? RET NZ ;NO, RETURN SCF ;CARRY SHOWS END RET ; BADOUT: CALL ERXIT DEFB '++Can''t make output file$' ; ;FOLLOWING FROM 'EQU10.LIB'----> ; ;MOVE, COMPARE SUBROUTINES ; MOVER: LD A,(HL) LD (DE),A INC HL INC DE DEC BC LD A,B OR C JP NZ,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: LD E,(HL) INC HL LD D,(HL) ;GET BUFFER ADDR INC HL LD C,(HL) INC HL LD B,(HL) ;BC = BYTES LEFT LD A,B ;GET COUNT OR C JP NZ,RDBNORD ;NO READ ; INC HL ;TO BUFFER SIZE LD A,(HL) ;GET COUNT ADD A,A ;MULTIPLY BY 2 LD B,A ;SECTOR COUNT IN B INC HL ;TO FCB PUSH HL ;SAVE FCB POINTER LD A,(HL) ;GET.. INC HL LD H,(HL) ;..ADDR.. LD L,A ;..TO HL ; RDBLP: LD A,1AH ;GET EOF CHAR LD (DE),A ;SAVE IN CASE EOF PUSH DE ;SAVE DMA ADDR PUSH HL ;SAVE FCB ADDR PUSH BC PUSH DE PUSH HL LD C,STDMA CALL BDOS ;SET DMA ADDR POP HL POP DE POP BC POP DE ;GET FCB PUSH BC PUSH DE PUSH HL LD C,READ CALL BDOS POP HL POP DE POP BC OR A POP HL ;HL=DMA, DE=FCB JP NZ,RDBRET ;GOT EOF LD A,L ADD A,80H ;TO NEXT BUFF LD L,A LD A,H ADC A,0 LD H,A EX DE,HL ;DMA TO DE, FCB TO HL DEC B ;MORE SECTORS? JP NZ,RDBLP ;YES, MORE ; RDBRET: POP HL ;GET FCB POINTER DEC HL ;TO LENGTH LD A,(HL) ;GET LENGTH DEC HL ;TO COUNT LD (HL),A ;SET PAGE COUNT DEC HL ;TO LO COUNT DEC HL ;TO HI FCB DEC HL ;TO EFCB START JP RDBYTE ;LOOP THRU AGAIN ; RDBNORD:INC HL ;TO LENGTH LD A,(HL) ;GET LENGTH (PAGES) EX DE,HL ;BUFF TO HL ADD A,H LD H,A ;HL = END OF BUFF LD A,L SUB C LD L,A LD A,H SBC A,B LD H,A ;HL = DATA POINTER LD A,(HL) ;GET BYTE EX DE,HL ;EFCB BACK TO HL CP 1AH ;EOF? RET Z ;YES, LEAVE POINTERS DEC BC ;DECR COUNT DEC HL ;"BYTES LEFT" LD (HL),B DEC HL LD (HL),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: LD E,(HL) INC HL LD D,(HL) ;DE=BUF ADDR INC HL LD C,(HL) INC HL LD B,(HL) ;BC=BYTES IN BUFF PUSH DE ;SAVE FCB EX DE,HL ADD HL,BC ;TO NEXT BYTE LD (HL),A ;STORE IT INC BC ;ONE MORE EX DE,HL POP DE ; ;SEE IF BUFFER IS FULL ; INC HL ;GET LD A,(HL) ; SIZE CP B ;FULL? JP NZ,WRBNOWR ;NO WRITE ; ADD A,A ;MULTIPLY BY 2 LD B,A ;SECTOR COUNT IN B INC HL ;TO FCB PUSH HL ;SAVE FCB POINTER LD A,(HL) ;GET.. INC HL ;..FCB.. LD H,(HL) ;..ADDR.. LD L,A ;..TO HL ; WRBLP: PUSH DE ;SAVE DMA ADDR PUSH HL ;SAVE FCB ADDR PUSH BC PUSH DE PUSH HL LD C,STDMA CALL BDOS ;SET DMA ADDR POP HL POP DE POP BC POP DE ;GET FCB PUSH BC PUSH DE PUSH HL LD C,WRITE CALL BDOS POP HL POP DE POP BC OR A POP HL ;HL=DMA, DE=FCB JP NZ,WRBERR ;GOT ERR LD A,L ADD A,80H ;TO NEXT BUFF LD L,A LD A,H ADC A,0 LD H,A EX DE,HL ;DMA TO DE, FCB TO HL DEC B ;MORE SECTORS? JP NZ,WRBLP ;YES, MORE ; WRBRET: POP HL ;GET FCB POINTER DEC HL ;TO LENGTH DEC HL ;TO COUNT LD (HL),0 ;SET 0 TO WRITE DEC HL ;TO LO COUNT LD (HL),0 PUSH BC PUSH DE PUSH HL LD C,STDMA LD DE,80H+BIAS CALL BDOS POP HL POP DE POP BC RET ; WRBNOWR:DEC HL ;TO LENGTH LD (HL),B ;SET NEW LENGTH DEC HL LD (HL),C RET ; ;FLUSH THE EFCB BUFFERS ; FLUSH: LD E,(HL) INC HL LD D,(HL) ;DE=BUF ADDR INC HL LD C,(HL) INC HL LD B,(HL) ;BC=BYTES IN BUFF INC HL ;TO COUNT LD A,B OR C RET Z ;NOTHING TO WRITE LD A,C ;GET LOW COUNT ADD A,A ;SHIFT HIGH TO CARRY LD A,B ;GET LOW COUNTAL RLA ;MULT BY 2, + CARRY INC A ;FUDGE FOR PARTIAL SECT LD B,A ;SAVE SECTOR COUNT INC HL ;TO FCB LD A,(HL) INC HL LD H,(HL) LD L,A ;HL=FCB ; FLUSHL: PUSH BC PUSH DE PUSH HL LD C,STDMA CALL BDOS POP HL POP DE POP BC EX DE,HL PUSH BC PUSH DE PUSH HL LD C,WRITE CALL BDOS POP HL POP DE POP BC EX DE,HL OR A JP NZ,WRBERR PUSH HL LD HL,80H ADD HL,DE EX DE,HL POP HL DEC B JP NZ,FLUSHL EX DE,HL PUSH BC PUSH DE PUSH HL LD C,CLOSE CALL BDOS POP HL POP DE POP BC INC A RET NZ CALL ERXIT DEFB '++OUTPUT FILE CLOSE ERROR ++$' ; WRBERR: CALL ERXIT DEFB '++OUTPUT FILE WRITE ERROR++$' ; ;EXIT WITH ERROR MESSAGE ; MSGEXIT EQU $ ;EXIT W/"INFORMATIONAL" MSG ; ERXIT: POP DE ;GET MSG LD C,PRINT CALL BDOS ; ;EXIT, RESTORING STACK AND RETURN ; EXIT: JP 0+BIAS ; ;====> START OF WORK AREA ; EXTFCB: DEFW DKBUF DEFW 0 DEFB 4 DEFW FCB PREV: DEFW PTR ;POINTER TO PREV POINTER SKIPC: DEFB 0 ;SKIP CHARS END DEFS 100 ;VARIABLE SKIP CHARS ; DEFS 100 ;STACK AREA STACK EQU $ ; OUTNAME:DEFS 12 ;OUTPUT FILENAME SWAPS: DEFS 1 CURR: DEFS 2 PTR: DEFS 2 ;POINTER TO FIRST NAME ; ORG ($+255) AND 0FF00H;TO PAGE ; DKBUF: DEFS 256*4 ;4 PAGES OF BUFFER SBUFF: DEFS 0 ;NAMES READ IN HERE ; END