home *** CD-ROM | disk | FTP | other *** search
- Page 80,132
- Title 'FSORT - Multi-key Shell/Metzner Sort of in core array'
- ;
- ; Preparing FSORT.BIN :
- ;
- ; MASM FSORTM;
- ; LINK FSORTM;
- ; EXE2BIN FSORTM
- ; DEL FSORTM.EXE
- ;
- ; Original sorting code written for SORTF version 1.7 by Vern Buerg
- ;
- ; Modified to fully relocatable code suitable for use as an external
- ; subprogram to a Turbo Pascal program and to support multiple
- ; sort keys.
- ; by
- ; David Gwillim
- ; 29 July 1985
- ;
- ; ******************************
- ;
- ; Turbo Pascal Usage:
- ;
- ; const
- ; MAXKEYS = (however many keys you will use) ;
- ;
- ; type
- ; KeyType = array[1..MAXKEYS] of integer ;
- ;
- ; procedure Sort(RecLen : integer ; {Size of each array record }
- ; NumKeys : integer ; {Number of sort keys being used}
- ; var KeyPos : KeyType ; {Array of sort key positions }
- ; var KeyLen : KeyType ; {Array of sort key lengths }
- ; RecCnt : integer ; {Number of records to sort }
- ; Base : integer ) ; {Segment address of array }
- ; external 'FSORTM.BIN' ;
- ;
- ; IMPORTANT NOTE:
- ; This version of FSORTM is designed for use with arrays created on Turbo's
- ; Heap using the "New" or "GetMem" procedures. It is NOT designed to be used
- ; with arrays that are defined in Turbo Pascal's Data Segment, or anywhere
- ; where the array to be sorted cannot for sure be made to start on a paragraph
- ; (16 byte) boundary in the 8088/8086 address space. This is a compromise that
- ; permits very high speed sorting.
- ;
- ; Also the size of RecLen MUST be a multiple of 16 bytes otherwise
- ; unpredictable happenings will occur during the sort. The sort routine
- ; depends for its operation on the records being adressable with the just
- ; 8088 segment registers. As a result the base address of the array
- ; must have an offset divisible by 16, i.e. the address of the array must be
- ; on a paragraph (16 byte) boundary in the 8088 adress space.
- ; This is guaranteed by the "New" procedure in Turbo Pascal if the sort
- ; array is the FIRST variable allocated memory space on the heap.
- ; Although not covered in the Turbo Pascal Reference Manual the minimum
- ; allocation unit that either "New" or "GetMem" uses is 8 bytes.
- ;
- ; Therefore, byte allocations on Turbo's heap go like this:
- ;
- ; Variable Size in bytes Allocation in bytes
- ; ---------------------- -------------------
- ;
- ; 1 - 8 8
- ; 9 - 16 16
- ; 17 - 24 24
- ; 25 - 32 32
- ; etc. etc.
- ;
- ; So, the current offset of the predefined variable "HeapPtr" can be
- ; checked to see if it zero, and if it isn't, allocate a pointer to type
- ; byte before allocating for the array to be sorted. This would only be needed
- ; if there had been a previous use of "GetMem" and/or "New" in the program.
- ; It can easily done like this:
- ;
- ; var
- ; BytePtr : ^byte ;
- ;
- ; if Ofs(HeapPtr^) <> 0 then
- ; New(BytePtr) ;
- ;
- ; *******************************
- ;
-
- Cseg Segment
- Assume CS:Cseg,DS:Nothing,ES:Nothing
-
- ; Equates for passed parameters from the Turbo Pascal program
-
- RecLen Equ Word Ptr [BP + 20] ;Size of each entry (mod 16 must = 0)
- NumKeys Equ Word Ptr [BP + 18] ;Number of keys being used in the sort
- SKeyPos Equ Word Ptr [BP + 16] ;Segment of key offset array
- OKeyPos Equ Word Ptr [BP + 14] ;Offset of key offset array
- SKeyLen Equ Word Ptr [BP + 12] ;Segment of key length array
- OKeyLen Equ Word Ptr [BP + 10] ;Offset of key length array
- RecCnt Equ Word Ptr [BP + 08] ;Number of entries
- Base Equ Word Ptr [BP + 06] ;Seg addr of array
-
- ; Equates for Stack addressing
-
- DSeg Equ Word Ptr [BP + 04] ;Turbo's DSeg value
-
- WorkArea Equ 20 ;Allocation size for local work area
-
- ; Equates for local work area variables
-
- Loc Equ Word Ptr [BP - 02] ;Array location record number
- SegLen Equ Word Ptr [BP - 04] ;Paras in each entry
- Limit Equ Word Ptr [BP - 06] ;Temporary work variables for Sort
- Incr Equ Word Ptr [BP - 08]
- Index1 Equ Word Ptr [BP - 10]
- Index2 Equ Word Ptr [BP - 12]
- Ptr1 Equ Word Ptr [BP - 14] ;Offset to record Index1
- Ptr2 Equ Word Ptr [BP - 16] ;Offset to record Index2
- CurrKey Equ Word Ptr [BP - 18] ;Current key being sorted on
- TempOfs Equ Word Ptr [BP - 20] ;Offset to sort exchange area
-
- ; The temporary storage space for record exchanges exists in a work area
- ; beneath TempOfs, the size of which is determined at run-time and the offset
- ; to it placed in TempOfs.
-
- CR Equ 13
- LF Equ 10
-
- Sort Proc Near
-
- Jmp short Start
-
- Version db CR
- db 'FSORT - MULTIKEY ' ;^Z at end so version number can be
- db 'V 1.01',CR,LF ; can be shown by TYPE FSORTM.BIN
- db 'Copyright (C) 1985 ' ; from DOS
- db 'by David Gwillim',26
-
- Start: Cld ;Make sure we are incrementing indexes
- Push DS ;Save Turbo environment
- Push BP
- Mov BP,SP ;Make parameters on stack addressable
- Mov AX,WorkArea ;Make space on stack for sort vars
- Add AX,RecLen ; and sort exchange variable
- Sub SP,AX
- Mov TempOfs,SP ;Store offset to sort exchange var
-
- Mov AX,RecCnt ;Set initial value for Loc = RecCnt
- Mov Loc,AX
-
- Mov AX,RecLen ;Calculate number of 16 byte
- Mov CL,4 ; paragraphs in each record
- Shr AX,CL
- Mov SegLen,AX
-
- Check: Cmp Loc,1 ;Check if Loc <= 1
- Jg Check1 ;If not continue
- Jmp Sorted ;If so, we're done, go clean up and
- ; return to Turbo Pascal program
-
- Check1: Mov AX,Loc ;Calculate a new value for Loc
- Sar AX,1
- Or AX,1
- Mov Loc,AX ;New value for Loc = 2 * (Loc/4) + 1
-
- Mov AX,RecCnt ;Calculate the new value for Limit
- Sub AX,Loc
- Mov Limit,AX ;Update value: Limit = RecCnt - Loc
-
- Mov Incr,0 ;Incr=0
-
- Again: Mov CurrKey,0 ;Make sure we use first key when
- Jmp short Cont ; first comparing arrays
-
- Next1: Inc CurrKey ;Tie on CurrKey compare so go to next
- Inc CurrKey ; key by indexing to next array value
- Mov BX,NumKeys ;Allow for KeyType array (2 bytes)
- Shl BX,1 ; for each KeyPos stored
- Cmp CurrKey,BX ;Already sorted on the final key?
- Jbe Next2 ;If so, go sort on the next key
- Jmp Again ;No more keys, leave records where-is
- ; and go compare two other records
-
- Cont: Inc Incr ;Incr=Incr+1
-
- Mov AX,Incr ;Ensure we are still in array bounds
- Cmp AX,Limit ;If Incr > Limit then goto Check
- Jg Check
-
- Mul SegLen ;Allow for record length
- Mov Index1,AX ;New value for Index1 = Incr * SegLen
-
- Mov Index2,AX ;Compute new value for Index2
- Mov AX,Loc ;for next compare
- Mul SegLen ;Allow for record length
- Add Index2,AX ;New value for Index2
- ; = Index1 + (Loc * SegLen)
-
- ; Compare the record keys in Array[Index1] and Array[Index2]
-
- Comp: Mov AX,Index1
- Sub AX,SegLen ;Decrement by length of one record
- Add AX,Base ;Compute the segment address
- Mov ES,AX ;Segment address for Index1
- Mov Ptr1,AX ;Update stored value for Index1
-
- Mov AX,Index2 ;Decrement by length of one record
- Sub AX,SegLen ;Compute the segment address
- Add AX,Base ;Segment address for Index2
- Mov Ptr2,AX ;Update stored value for Index2
-
- ; Get the offsets, within the records, of the keys currently being compared
-
- Next2: Mov DS,SKeyLen ;Get segment address of KeyLen array
- Mov SI,OKeyLen ;Get offset address of KeyLen array
- Add SI,CurrKey ;Use CurrKey's length
- Mov CX,DS:[SI] ;Get KeyLen[CurrKey+1] from Turbo
-
- Mov DS,SKeyPos ;Get segment address of KeyPos array
-
- Mov DI,OKeyPos ;Point DI to offset of KeyPos array
- Add DI,CurrKey ;Use CurrKey's offset
- Mov DI,DS:[DI] ;Get KeyPos[CurrKey + 1] from Turbo
- Dec DI ;Turn key position into key offset
-
- Mov SI,OKeyPos ;Point SI to offset of KeyPos array
- Add SI,CurrKey ;Make index to CurrKey's offset value
- Mov SI,DS:[SI] ;Get KeyPos[CurrKey+1] from Turbo
- Dec SI ;Turn key position into key offset
-
- Mov DS,AX ;Get the segment address for Index2
- Repe Cmpsb ;Compare the two keys
-
- Ja Again ;The compared keys are in order
- Je Next1 ;Keys are equal so check for next key
-
- ; Fall through if Array[Index1] < Array[Index2] - keys not in order
-
- Mov CurrKey,0 ;Make sure we use first key next time
-
- ; Keys were not in order, exchange the records Array[Index1] and Array[Index2]
-
- Swap: Mov AX,SS ;Temporary storage is located in the
- Mov ES,AX ; Stack Segment - point to it
- Mov DI,TempOfs ;Get offset of temp storage record
- Mov CX,Reclen ;Get number of bytes to move
-
- Mov DS,Ptr1 ;Get segment address of Array[Index1]
- Sub SI,SI ;Ensure we at beginning of the record
- Rep Movsb ;Move Array[Index1] to temporary
- ; storage
-
- Mov ES,Ptr1 ;Get segment address of Array[Index1]
- Mov DS,Ptr2 ;Get segment address of Array[Index2]
- Sub DI,DI ;Ensure we at beginning of the records
- Sub SI,SI
- Mov CX,Reclen ;Get number of bytes to move
- Rep Movsb ;Move Array[Index2] into Array[Index1]
-
- Mov AX,SS ;Temp exchange record is in stack seg
- Mov DS,AX ;Source segment = Temp exchange record
- Mov SI,TempOfs ;Point to Temp sort exchange space
- Mov ES,Ptr2 ;Destination segment = Ptr2
- Sub DI,DI ;Make sure we start at beginning
- Mov CX,Reclen ;Get number of bytes to move
- Rep Movsb ;Move Temp to Array[Index2]'s location
-
- Mov AX,Index1 ;Make Index2 = Index1
- Mov Index2,AX
-
- Mov AX,Loc ;Index1=Index1-Loc
- Mul SegLen ;Allow for record length
- Sub Index1,AX ;Update value for Index1
-
- Jg GoComp ;Is Index1 > 0 then
- Jmp Again ; no, then go to Again
- GoComp: Jmp Comp ; yes, go compare keys
-
- Sorted: Mov SP,BP ;Restore Turbo environment
- Pop BP
- Pop DS
- Ret 16 ;Dump the passed parameters
- ; and return to Turbo program
- Sort Endp
-
- Cseg Ends
- End Sort