home *** CD-ROM | disk | FTP | other *** search
- ;
- ; SYSLIB Module Name: SSORT
- ; Author: Richard Conn
- ; SYSLIB Version Number: 3.6
- ; Module Version Number: 1.2
-
- public ssbinit,sort
-
- ;
- ; EXTERNALS
- ;
- EXT MOVEB
- EXT PRINT
-
- ;
- ; BASIC EQUATES
- ;
- CPM EQU 0 ; CP/M WARM BOOT
- BIOS EQU CPM+1 ; BIOS BASE ADDRESS
- BDOS EQU CPM+5 ; BDOS ENTRY POINT
- ESIZE EQU 16 ; NUMBER OF BYTES/ENTRY IN MEMORY BUFFER
- BUFF EQU CPM+80H ; DEFAULT DMA BUFFER
-
- ;
- ; SSBINI -- Initialize SSB
- ; This routine sets the value of FIRSTP and ORDER for the SORT
- ; routine. On Entry, HL pts to first available byte after user's
- ; program (beginning of buffer area) and DE pts to an SSB. On exit,
- ; HL pts to first available byte for the user's in-memory data file
- ; (same address as FIRSTP). If the TPA is overflowed by the ORDER
- ; table that would be generated by SORT, then the Z flag is returned set
- ; (Z); if no error, NZ is returned.
- ; If the user has already loaded his data and just wants to
- ; allocate an ORDER table, then he should have HL before calling SSBINI,
- ; restore HL upon return from SSBINI, and then store HL into his FIRSTP
- ; entry before calling SORT.
- ;
- SSBINIT:
- PUSH DE ; SAVE REGS
- PUSH BC
- PUSH HL ; SAVE NEXT AVAILABLE BYTE ADDRESS
- EX DE,HL ; MAKE HL PT TO USER'S SSB
- LD (USRSSB),HL ; SAVE PTR TO USER'S SSB
- LD DE,SSB ; COPY INTO MY SSB
- LD B,12 ; 12 BYTES
- CALL MOVEB
- POP HL ; GET NEXT AVAILABLE BYTE ADDRESS
- LD (ORDER),HL ; SET PTR TO IT
- LD HL,(RECNT) ; GET NUMBER OF RECORDS
- LD A,L ; DOUBLE IT WITH ERROR CHECKING
- ADD A,L
- LD L,A
- LD A,H
- ADC A,H
- LD H,A
- JP C,SSBOVFL ; OVERFLOW IF CARRY
- EX DE,HL ; SIZE OF ORDER TABLE IN DE
- LD HL,(ORDER) ; BASE ADDRESS OF ORDER TABLE IN HL
- LD A,L ; ADD HL TO DE WITH ERROR CHECKING
- ADD A,E
- LD L,A
- LD A,H
- ADC A,D
- LD H,A ; HL IS NOW ADDRESS OF BYTE AFTER ORDER TABLE
- JP C,SSBOVFL ; OVERFLOW IF CARRY
- LD (FIRSTP),HL ; SET PTR TO NEXT AVAILABLE BYTE
- LD HL,(USRSSB) ; PT TO USER'S SSB
- EX DE,HL ; ... IN DE
- LD HL,SSB ; PT TO USER'S NEW SSB
- LD B,12 ; COPY IT BACK
- CALL MOVEB
- POP BC ; RESTORE REGS
- POP DE
- LD HL,(FIRSTP) ; PT TO NEXT AVAILABLE MEMORY ADDRESS
- LD A,0FFH ; SET NO ERROR CODE
- OR A
- RET
- SSBOVFL:
- POP BC ; RESTORE REGS
- POP DE
- LD HL,(ORDER) ; RESTORE PTR TO USER'S BUFFER
- XOR A ; ERROR CODE
- RET
- USRSSB:
- DS 2 ; BUFFER FOR ADDRESS OF USER'S SSB
-
- ;
- ; SORT -- Sort memory-resident file of fixed-length records in an order
- ; specified by the user; On entry, DE pts to a SORT Specification
- ; Block (SSB) which contains the following information:
- ;
- ; Bytes 0&1: Starting Address of File (1st byte of 1st record)
- ; Bytes 2&3: Number of Records in the File
- ; Bytes 4&5: Size of Each Record (in Bytes)
- ; Bytes 6&7: Address of a Compare Routine Provided by the User
- ; This routine compares two records, one pted
- ; to by HL and the other pted to by DE; if the
- ; record pted to by DE is less in sorting order
- ; than that pted to by HL, this routine returns
- ; with Carry Set (C); if the records are equal
- ; in sorting order, this routine returns with
- ; Zero Set (Z); no registers asside from the PSW
- ; are to be affected by this routine
- ; Bytes 8&9: Address of ORDER Buffer (RECNT*2 in size)
- ; Byte 10: Flag; 0 means to use pointers, 0FFH means to not
- ; use pointers
- ; Byte 11: Unused
- ;
- ; SORT does not have any net affect on any registers
- ;
-
- ;
- ; SORT SPECIFICATION BLOCK (SSB) FOR USE BY SORT ROUTINE
- ;
- SSB:
- FIRSTP:
- DS 2 ; POINTER TO FIRST RECORD IN FILE
- RECNT:
- DS 2 ; NUMBER OF RECORDS IN FILE
- RECSIZ:
- DS 2 ; SIZE OF EACH RECORD
- CMPADR:
- DS 2 ; ADDRESS OF COMPARE ROUTINE
- ORDER:
- DS 2 ; ADDRESS OF ORDER TABLE
- SFLAG:
- DS 1 ; USE POINTERS? 0=NO
- DS 1 ; UNUSED, BUT RESERVED, AT THIS TIME
-
- ;
- ; SORT AND POINTER BUFFERS
- ;
- N:
- DS 2 ; NUMBER OF RECS
- GAP:
- DS 2 ; VARIABLE GAP
- J:
- DS 2 ; REC PTR 1
- JG:
- DS 2 ; REC PTR 2
- IDX:
- DS 2 ; INDEX PTR
- PTPTR:
- DS 2 ; 2-LEVEL INDIRECT PTR
- PTFIL:
- DS 2 ; REC PTR
-
- ;
- ; START OF SORT ROUTINE
- ;
- SORT:
- PUSH HL ; SAVE REGS
- PUSH DE
- PUSH BC
- PUSH AF
- EX DE,HL ; PTR IN HL
- LD DE,SSB ; COPY HIS SORT BLOCK INTO MINE FOR EASIER ACCESS
- LD B,12 ; 12 BYTES LONG
- CALL MOVEB
- LD HL,(RECNT) ; GET RECORD COUNT
- LD (N),HL ; SET "N"
- LD B,H ; ... IN BC
- LD C,L
-
- ;
- ; CHECK FOR TRIVIAL CASE - 0 OR 1; DON'T DO IT IF SO
- ;
- LD A,B ; SEE IF BC=1
- OR A ; B=0?
- JP NZ,SHELL ; DO SORT IF B<>0
- LD A,C ; C=0 OR 1?
- CP 2
- JP C,SEXIT
-
- ;
- ; SHELL SORT --
- ; THIS SORT ROUTINE IS ADAPTED FROM "SOFTWARE TOOLS"
- ; BY KERNIGAN AND PLAUGHER, PAGE 106. COPYRIGHT, 1976, ADDISON-WESLEY.
- ; ON ENTRY, BC=NUMBER OF ENTRIES
- ;
- SHELL:
- LD A,(SFLAG) ; USE POINTERS?
- OR A ; 0=NO
- JP Z,SORT2
- LD HL,(FIRSTP) ; PT TO FIRST RECORD
- EX DE,HL ; POINTER TO 1ST RECORD IN DE
- LD HL,(ORDER) ; PT TO ORDER TABLE
- ;
- ; SET UP ORDER TABLE; HL PTS TO NEXT ENTRY IN ORDER TABLE, DE PTS TO NEXT
- ; REC IN FILE, BC = NUMBER OF RECS REMAINING
- ;
- SORT1:
- LD A,B ; DONE?
- OR C ; 0 IF SO
- JP Z,SORT2
- DEC BC ; COUNT DOWN
- LD (HL),E ; STORE LOW-ORDER ADDRESS
- INC HL ; PT TO NEXT ORDER BYTE
- LD (HL),D ; STORE HIGH-ORDER ADDRESS
- INC HL ; PT TO NEXT ORDER ENTRY
- PUSH HL ; SAVE PTR
- LD HL,(RECSIZ) ; HL=NUMBER OF BYTES/ENTRY
- ADD HL,DE ; PT TO NEXT DIR1 ENTRY
- EX DE,HL ; DE PTS TO NEXT ENTRY
- POP HL ; GET PTR TO ORDER TABLE
- JP SORT1
- ;
- ; THIS IS THE MAIN SORT LOOP FOR THE SHELL SORT IN "SOFTWARE TOOLS" BY K&P
- ;
- SORT2:
- LD HL,(N) ; NUMBER OF ITEMS TO SORT
- LD (GAP),HL ; SET INITIAL GAP TO N FOR FIRST DIVISION BY 2
-
- ; FOR (GAP = N/2; GAP > 0; GAP = GAP/2)
- SRTL0:
- OR A ; CLEAR CARRY
- LD HL,(GAP) ; GET PREVIOUS GAP
- LD A,H ; ROTATE RIGHT TO DIVIDE BY 2
- RRA
- LD H,A
- LD A,L
- RRA
- LD L,A
-
- ; TEST FOR ZERO
- OR H
- JP Z,SDONE ; DONE WITH SORT IF GAP = 0
-
- LD (GAP),HL ; SET VALUE OF GAP
- LD (IDX),HL ; SET I=GAP FOR FOLLOWING LOOP
-
- ; FOR (I = GAP + 1; I <= N; I = I + 1)
- SRTL1:
- LD HL,(IDX) ; ADD 1 TO I
- INC HL
- LD (IDX),HL
-
- ; TEST FOR I <= N
- EX DE,HL ; I IS IN DE
- LD HL,(N) ; GET N
- LD A,L ; COMPARE BY SUBTRACTION
- SUB E
- LD A,H
- SBC A,D ; CARRY SET MEANS I > N
- JP C,SRTL0 ; DON'T DO FOR LOOP IF I > N
-
- LD HL,(IDX) ; SET J = I INITIALLY FOR FIRST SUBTRACTION OF GAP
- LD (J),HL
-
- ; FOR (J = I - GAP; J > 0; J = J - GAP)
- SRTL2:
- LD HL,(GAP) ; GET GAP
- EX DE,HL ; ... IN DE
- LD HL,(J) ; GET J
- LD A,L ; COMPUTE J - GAP
- SUB E
- LD L,A
- LD A,H
- SBC A,D
- LD H,A
- LD (J),HL ; J = J - GAP
- JP C,SRTL1 ; IF CARRY FROM SUBTRACTIONS, J < 0 AND ABORT
- LD A,H ; J=0?
- OR L
- JP Z,SRTL1 ; IF ZERO, J=0 AND ABORT
-
- ; SET JG = J + GAP
- EX DE,HL ; J IN DE
- LD HL,(GAP) ; GET GAP
- ADD HL,DE ; J + GAP
- LD (JG),HL ; JG = J + GAP
-
- ; IF (V(J) <= V(JG))
- CALL ICOMPARE ; J IN DE, JG IN HL
-
- ; ... THEN BREAK
- JP C,SRTL1
-
- ; ... ELSE EXCHANGE
- LD HL,(J) ; SWAP J, JG
- EX DE,HL
- LD HL,(JG)
- CALL ISWAP ; J IN DE, JG IN HL
-
- ; END OF INNER-MOST FOR LOOP
- JP SRTL2
-
- ;
- ; SORT IS DONE -- RESTRUCTURE FILE IN SORTED ORDER IN PLACE
- ;
- SDONE:
- LD A,(SFLAG) ; USE POINTERS?
- OR A ; 0=NO
- JP Z,SEXIT ; DONE IF NO POINTERS
- LD HL,(RECNT) ; NUMBER OF RECORDS
- LD (J),HL ; SAVE COUNT IN J
- LD HL,(ORDER) ; PTR TO ORDERED POINTER TABLE
- LD (PTPTR),HL ; SET PTR PTR
- LD HL,(FIRSTP) ; PTR TO UNORDERED FILE
- LD (PTFIL),HL ; SET PTR TO FILE BUFFER
-
- ; FIND PTR TO NEXT FILE ENTRY
- SRTDN:
- LD HL,(J) ; GET ENTRY COUNT
- LD B,H ; ... IN BC
- LD C,L
- LD HL,(PTPTR) ; PT TO REMAINING POINTERS
- EX DE,HL ; ... IN DE
- LD HL,(PTFIL) ; HL PTS TO NEXT FILE ENTRY
-
- ; FIND PTR TABLE ENTRY
- SRTDN1:
- LD A,(DE) ; GET CURRENT POINTER TABLE ENTRY VALUE
- INC DE ; PT TO HIGH-ORDER POINTER BYTE
- CP L ; COMPARE AGAINST FILE ADDRESS LOW
- JP NZ,SRTDN2 ; NOT FOUND YET
- LD A,(DE) ; LOW-ORDER BYTES MATCH -- GET HIGH-ORDER POINTER BYTE
- CP H ; COMPARE AGAINST FILE ADDRESS HIGH
- JP Z,SRTDN3 ; MATCH FOUND
- SRTDN2:
- INC DE ; PT TO NEXT PTR TABLE ENTRY
- DEC BC ; COUNT DOWN
- LD A,C ; END OF TABLE?
- OR B
- JP NZ,SRTDN1 ; CONTINUE IF NOT
-
- ; FATAL ERROR -- INTERNAL ERROR; POINTER TABLE NOT CONSISTENT
- FERR$PTR:
- CALL PRINT
- DB 0DH,0AH,'SORT Pointer Error',0
- JP CPM
-
- ; FOUND THE POINTER TABLE ENTRY WHICH POINTS TO THE NEXT UNORDERED FILE ENTRY
- ; MAKE BOTH POINTERS (PTR TO NEXT, PTR TO CURRENT UNORDERED FILE ENTRY)
- ; POINT TO SAME LOCATION (PTR TO NEXT FILE ENTRY TO BE ORDERED)
- SRTDN3:
- LD HL,(PTPTR) ; GET PTR TO NEXT ORDERED ENTRY
- DEC DE ; DE PTS TO LOW-ORDER POINTER ADDRESS
- LD A,(HL) ; MAKE PTR TO NEXT UNORDERED REC PT TO BUFFER FOR
- LD (DE),A ; FILE REC TO BE MOVED TO NEXT UNORDERED FILE POS
- INC HL ; PT TO NEXT PTR ADDRESS
- INC DE
- LD A,(HL) ; MAKE HIGH POINT SIMILARLY
- LD (DE),A
-
- ; INTERCHANGE NEXT ENTRY IN LINE WITH THE ENTRY CURRENTLY IN ITS POSITION
- LD HL,(RECSIZ) ; HL=NUMBER OF BYTES/ENTRY
- LD B,H ; BC=NUMBER OF BYTES/ENTRY
- LD C,L
- LD HL,(PTFIL) ; PT TO ENTRY
- EX DE,HL
-
- ; COPY TO-BE-ORDERED FILE ENTRY TO NEXT ORDERED FILE POSITION
- LD HL,(PTPTR) ; POINT TO ITS POINTER
- LD A,(HL) ; GET LOW-ADDRESS POINTER
- INC HL
- LD H,(HL) ; GET HIGH-ADDRESS POINTER
- LD L,A ; HL IS ADDRESS OF UNORDERED ENTRY
- EX DE,HL ; HL PTS TO ORDERED ENTRY TO BE MOVED, DE PTS TO REPL
- SRTDN4:
- PUSH BC ; SAVE COUNT
- LD A,(DE) ; GET BYTES
- LD C,(HL)
- EX DE,HL ; EXCHANGE PTRS
- LD (DE),A ; PUT BYTE
- LD (HL),C
- EX DE,HL ; RESTORE PTRS TO ORIGINAL
- POP BC ; GET COUNT
- INC HL ; PT TO NEXT
- INC DE
- DEC BC ; COUNT DOWN
- LD A,B ; DONE?
- OR C
- JP NZ,SRTDN4
-
- ; POINT TO NEXT TARGET RECORD POSITION
- LD HL,(RECSIZ) ; GET SIZE OF RECORD
- EX DE,HL ; ... IN DE
- LD HL,(PTFIL) ; PT TO ENTRY JUST PLACED
- ADD HL,DE ; PT TO NEXT ENTRY
- LD (PTFIL),HL ; SET POINTER FOR NEXT LOOP
-
- ; POINT TO NEXT ENTRY IN POINTER TABLE
- LD HL,(PTPTR) ; POINTER TO CURRENT REC
- INC HL ; PT TO NEXT REC
- INC HL
- LD (PTPTR),HL
-
- ; COUNT DOWN
- LD HL,(J) ; GET COUNT
- DEC HL ; COUNT DOWN
- LD (J),HL ; PUT COUNT
- LD A,H ; DONE?
- OR L
- JP NZ,SRTDN
-
- ; EXIT
- SEXIT:
- POP AF ; RESTORE REGS
- POP BC
- POP DE
- POP HL
- RET ; DONE
-
- ;
- ; ISWAP -- Perform exchange of elements whose indices are in HL and DE
- ;
- ISWAP:
- LD A,(SFLAG) ; USE POINTERS?
- OR A ; 0=NO
- JP NZ,SWAP ; DO POINTER SWAP
- CALL ADRCOMP ; COMPUTE ADDRESS OF RECORDS FROM THEIR INDICES
- PUSH HL ; SAVE HL PTR
- LD HL,(RECSIZ) ; SIZE OF RECORD
- LD B,H ; ... IN BC
- LD C,L
- POP HL ; GET HL PTR
- SWAPC:
- PUSH BC ; SAVE COUNT
- LD A,(DE) ; GET BYTES
- LD C,(HL)
- LD (HL),A ; PUT BYTES
- LD A,C
- LD (DE),A ; PUT BYTES
- INC HL ; PT TO NEXT
- INC DE
- POP BC ; GET COUNT
- DEC BC ; COUNT DOWN
- LD A,B ; DONE?
- OR C
- JP NZ,SWAPC
- RET
-
- ;
- ; GIVEN INDICES IN HL AND DE, RETURN ADDRESSES OF RECORDS PTED TO BY
- ; THESE INDICES IN HL AND DE, RESP
- ;
- ADRCOMP:
- PUSH HL ; SAVE HL RECORD
- CALL ADROFF ; COMPUTE OFFSET TO RECORD PTED TO BY DE
- LD (REC1),HL ; SAVE ADDRESS OF RECORD
- POP DE ; GET OLD HL INDEX
- CALL ADROFF ; COMPUTE OFFSET TO THE DESIRED RECORD
- EX DE,HL ; ADDRESS IN DE
- LD HL,(REC1) ; GET ADDRESS
- EX DE,HL ; ORIGINAL ORDER
- RET
- REC1:
- DS 2 ; TEMP STORAGE FOR SWAPC
-
- ADROFF:
- LD HL,(RECSIZ) ; SIZE OF RECORD
- LD B,H ; ... IN BC
- LD C,L
- LD HL,0 ; OFFSET IN HL
- ADRO1:
- DEC DE ; DECREMENT BY 1 SO BASE-RELATIVE
- LD A,D ; DONE WITH LOOP?
- OR E
- JP Z,ADRO2
- ADD HL,BC ; ADD IN RECORD SIZE
- JP ADRO1
- ADRO2:
- EX DE,HL ; OFFSET IN DE
- LD HL,(FIRSTP) ; PT TO FIRST ENTRY
- ADD HL,DE ; ADD IN OFFSET
- RET
-
- ;
- ; SWAP (Exchange) the pointers in the ORDER table whose indexes are in
- ; HL and DE
- ;
- SWAP:
- PUSH HL ; SAVE HL
- LD HL,(ORDER) ; ADDRESS OF ORDER TABLE
- LD B,H ; ... IN BC
- LD C,L
- POP HL
- DEC HL ; ADJUST INDEX TO 0...N-1 FROM 1...N
- ADD HL,HL ; HL PTS TO OFFSET ADDRESS INDICATED BY INDEX
- ; OF ORIGINAL HL (0, 2, 4, ...)
- ADD HL,BC ; HL NOW PTS TO POINTER INVOLVED
- EX DE,HL ; DE NOW PTS TO POINTER INDEXED BY HL
- DEC HL ; ADJUST INDEX TO 0...N-1 FROM 1...N
- ADD HL,HL ; HL PTS TO OFFSET ADDRESS INDICATED BY INDEX
- ; OF ORIGINAL DE (0, 2, 4, ...)
- ADD HL,BC ; HL NOW PTS TO POINTER INVOLVED
- LD C,(HL) ; EXCHANGE POINTERS -- GET OLD (DE)
- LD A,(DE) ; -- GET OLD (HL)
- EX DE,HL ; SWITCH
- LD (HL),C ; PUT NEW (HL)
- LD (DE),A ; PUT NEW (DE)
- INC HL ; PT TO NEXT BYTE OF POINTER
- INC DE
- LD C,(HL) ; GET OLD (HL)
- LD A,(DE) ; GET OLD (DE)
- EX DE,HL ; SWITCH
- LD (HL),C ; PUT NEW (DE)
- LD (DE),A ; PUT NEW (HL)
- RET
-
- ;
- ; ICOMPARE - Compares entries whose indices are in HL and DE; on exit,
- ; Carry Set means ((DE)) < ((HL)), Zero Set means ((HL)) = ((DE))
- ;
- ICOMPARE:
- LD A,(SFLAG) ; USE POINTERS?
- OR A ; 0=NO
- JP NZ,COMPARE
- CALL ADRCOMP ; COMPUTE ADDRESSES
- JP CALLCMP ; CALL COMPARE ROUTINE OF USER
-
- ;
- ; COMPARE compares the entry pointed to by the pointer pointed to by HL
- ; with that pointed to by DE (1st level indirect addressing); on entry,
- ; HL and DE contain the numbers of the elements to compare (1, 2, ...);
- ; on exit, Carry Set means ((DE)) < ((HL)), Zero Set means ((HL)) = ((DE)),
- ; and Non-Zero and No-Carry means ((DE)) > ((HL))
- ;
- COMPARE:
- PUSH HL ; SAVE HL
- LD HL,(ORDER) ; ADDRESS OF ORDER
- LD B,H ; ... IN BC
- LD C,L
- POP HL
- DEC HL ; ADJUST INDEX TO 0...N-1 FROM 1...N
- ADD HL,HL ; DOUBLE THE ELEMENT NUMBER TO POINT TO THE PTR
- ADD HL,BC ; ADD TO THIS THE BASE ADDRESS OF THE PTR TABLE
- EX DE,HL ; RESULT IN DE
- DEC HL ; ADJUST INDEX TO 0...N-1 FROM 1...N
- ADD HL,HL ; DO THE SAME WITH THE ORIGINAL DE
- ADD HL,BC
- EX DE,HL
-
- ;
- ; HL NOW POINTS TO THE POINTER WHOSE INDEX WAS IN HL TO BEGIN WITH
- ; DE NOW POINTS TO THE POINTER WHOSE INDEX WAS IN DE TO BEGIN WITH
- ; FOR EXAMPLE, IF DE=5 AND HL=4, DE NOW POINTS TO THE 5TH PTR AND HL
- ; TO THE 4TH POINTER
- ;
- LD C,(HL) ; BC IS MADE TO POINT TO THE OBJECT INDEXED TO
- INC HL ; ... BY THE ORIGINAL HL
- LD B,(HL)
- EX DE,HL
- LD E,(HL) ; DE IS MADE TO POINT TO THE OBJECT INDEXED TO
- INC HL ; ... BY THE ORIGINAL DE
- LD D,(HL)
- LD H,B ; SET HL = OBJECT PTED TO INDIRECTLY BY BC
- LD L,C
-
- ;
- ; COMPARE DIR ENTRY PTED TO BY HL WITH THAT PTED TO BY DE;
- ; NO NET EFFECT ON HL, DE; RET W/CARRY SET MEANS DE<HL
- ; RET W/ZERO SET MEANS DE=HL
- ;
- CALLCMP:
- PUSH HL ; SAVE HL ON STACK
- LD HL,(CMPADR) ; GET ADDRESS OF COMPARE ROUTINE IN HL
- EX (SP),HL ; ADDRESS OF COMPARE ROUTINE ON STACK AND RESTORE HL
- RET ; "CALL" ROUTINE AND RETURN TO SORT CORRECTLY
-
- END