home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
ZSYS
/
SIMTEL20
/
SYSLIB
/
SLIB3.LBR
/
SSORT.Z80
< prev
next >
Wrap
Text File
|
2000-06-30
|
14KB
|
563 lines
;
; 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