;----------------------M O D U L E H E A D E R----------------------------; ; ; ; Module Name: SORT.ASM ; ; ; ; History: SORT.C ; ; Created by Charles Whitmer 12/30/1986 ; ; Modified by Mitchel B. London 08/05/1987 ; ; ; ; SORT.ASM - translation of SORT.C ; ; CreateModule David Weise ; ; Other Modules Amit Chatterjee 08/09/1988 ; ; ; ; Copyright (c) 1985 - 1988 Microsoft Corporation ; ; ; ; General Description: ; ; ; ; The SORT module creates and maintains a tree of nodes, each node ; ; having a KEY value and a TAG field. The KEY field is used to or- ; ; -ganize the tree into a heap. ; ; The heap tree is implemented using an array, where if ; ; parent node occurs in position i, its left child will be at index ; ; (2 * i) and the right chaild at index (2 * i + 1). ; ; The Module ensures that at any instant, the root node ; ; of any subtree has the least key value in the subtree. ; ; First few positions in the array are used for storing ; ; a header for the tree. ; ; ; ; SubModules: ; ; ; ; 1. CreatePQ: ; ; Allocates space for the heaptree and its header ; ; and initializes the header. ; ; 2. InsertPQ: ; ; Inserts a node into the heap ensuring that the heap ; ; property is not violated. ; ; 3. MinPQ: ; ; Returns the tag value associated with the lowest ; ; key in the heap. (node is not deleted) ; ; 4. ExtractPQ: ; ; Return the tag value associated with the lowest key ; ; in the heap and deletes the node. The heap is then ; ; reconstructed with the remaining nodes. ; ; 5. DeletePQ: ; ; Deletes the entire heap by freeing the allocated ; ; area. ; ; 6. SizePQ: ; ; Increases the size of the heap by adding space ; ; for a requested number of entries ; ; Heap Data Structure: ; ; ; ; The heap data structure has two parts, a header and a set of nodes and ; ; these are blocked one after the other. ; ; The header maintains: ; ; (i) A pointer to the next available node slot ; ; relative to start of the node area ; ; (ii) A pointer to the first valid node slot ; ; node slots between the header and this pointer ; ; are actually deleted nodes ; ; (iii) A pointer past the last allocated node slot ; ; (iv) A last key value, which either holds the largest; ; key value as long as the nodes are sequentially ; ; ordered, or a very large value to indicate there; ; is no sequential ordering ; ; ; ; ---------------- ; ; | INDEX | ---- Pointer to next available node slot ; ; ---------------- ; ; | MAXENTRY | ---- Pointer past last allocated node slot ; ; ---------------- ; ; START | LASTKEY | ---- Aslong as possible holds max key ; ; NODE | START | ---- pointer to first active node slot ; ; ---------------- ; ; | KEY | ---- Node 1. ; ; | TAG | ; ; //---------------// ; ; | KEY | ---- Last allocated node slot ; ; | TAG | ; ; ---------------- ; ; ; ; All pointers to nodes are relative to node 1 (pointer to node 1 is ZERO) ; ;----------------------------------------------------------------------------; ;----------------------------------------------------------------------------; ; Include File Section and definitions: ; ; ; .xlist include cmacros.inc include gdimacro.inc include gdimem.inc .286p .list START equ SIZE PQ - SIZE ENTRY ; The header includes Node 0 VERYLARGE equ 4000h ; assumed to be larger than any key PQERROR equ -1 ; return value on error TRUE equ 1 FALSE equ 0 Entry struc e_key dw ? ; key value of node e_tag dw ? ; corresponding tag value Entry ends PQ struc ; HEAP Header Structure + Start Node pq_index dw ? pq_maxentry dw ? ; excludes START NODE pq_lastkey dw ? pq_start dw ? PQ ends externFP GlobalLock externFP GlobalUnlock externFP GlobalReAlloc externFP GlobalFree externFP GlobalAlloc ; Defined in HELPER.ASM createSeg _SORT,SORT,byte,public,CODE sBegin SORT ;-----------------------------------------------------------------------------; ; ; ;CreatePQ: ; ; Inputs: ; ; Max Number of entries the tree will hold ; ; Outputs: ; ; HANDLE to Heap -- if creation successful ; ; PQERROR if failure ; ; Registers Preserved: ; ; DI,SI ; ; ; ; -by- David Weise [davidw] ; ; ; ;-----------------------------------------------------------------------------; assumes cs,SORT assumes ds,nothing cProc farGDIGlobalAlloc, parmd amount cBegin cCall GlobalAlloc, cEnd cProc CreatePQ,, parmW cEntries cBegin mov ax,cEntries ; max no of nodes the tree will hold shl ax,1 shl ax,1 ; ax <---- ax * SIZE ENTRY .errnz (SIZE ENTRY - 4) mov si,ax ; save number of bytes in node array add ax, SIZE PQ ; size of header including NODE 0 xor dx,dx cCall farGDIGlobalAlloc, mov bx,ax ; Handle returned dec ax ; set to -1 if handle returned == 0 .errnz (-1 - PQERROR) or bx,bx ; test for succesfull memory allocation jz cPQ_Done ; error return. push bx cCall GlobalLock, ; lock handle get back segment pop bx mov es,dx mov di,ax ; es:di points to start of structure ; now initialize the header part of the structure with values ; si has size of the node array stosw ; index set to zero .errnz (0 - pq_index) mov ax,si ; pointer past end of node array stosw ; max no of entries .errnz (2 - pq_maxentry) xor ax,ax ; last key = 0, as heap empty stosw .errnz (4 - pq_lastkey) stosw ; START = 0, implies no deleted slot .errnz (6 - pq_start) push bx cCall GlobalUnlock, ; unlock the handle to heap pop ax ; return the handle cPQ_Done: cEnd ;-----------------------------------------------------------------------------; ; ; ; InsertPQ: ; ; Inputs: ; ; hPQ -- handle to heap structure segment ; ; tag -- tag value for new node ; ; key -- key value for new node ; ; Outputs: ; ; return TRUE if insertion was successful ; ; return PQERROR if heap was already packed ; ; Preserves: ; ; DS,SI ; ; ; ; -by- Amit Chatterjee [amitc] Tue Aug 9 10:45:25 ; ;----------------------------------------------------------------------------- assumes cs,SORT assumes ds,nothing cProc InsertPQ,, parmW hPQ parmW tag parmW key cBegin mov di,hPQ cCall GlobalLock, ; lock heap and get back segment addres or ax,dx ; Invalid handle causes zero return jz Ins_Cant_Proceed mov es,dx xor si,si ; offset in segment always zero mov ax,es:[si] ; pointer to next available slot sub ax,es:[si].pq_start ; convert it relative to 1st active node cmp ax,es:[si].pq_maxentry ; compare with pointer past last slot jb Insertion_Possible cCall GlobalUnlock, Ins_Cant_Proceed: mov ax,PQERROR ; error return jmp cInsert_Done Insertion_Possible: push es ; HEAP structure segment smov es,ds ; save local segment in es pop ds ; change DS to heap segment mov ax,[si].pq_index ; next available slot in node area cmp ax,[si].pq_maxentry jb Enough_Space_Atend ; insertion possible w/o compaction ; Deleted nodes exist near the head of the tree, compaction necessary call CompactList ; removes all deleted elements ; LASTKEY still holds the max key value in the tree Enough_Space_Atend: mov bx,[si].pq_index ; pointer to next available slot mov dx,bx ; save value in register add bx,SIZE PQ ; area for header mov ax,tag mov [si][bx].e_tag,ax ; insert new tag and key mov ax,key mov [si][bx].e_key,ax ; key in ax will be used below mov bx,dx ; bx points to last occupied slot add dx,SIZE ENTRY ; available slot points to next node mov [si].pq_index,dx ; save in the structure ; Now test whether the heap property is valid still. ; ax has key, dx has pointer to next slot after addition ; bx points to last valid node cmp ax,[si].pq_lastkey ; compare with new key jb Heap_Violated mov [si].pq_lastkey,ax ; new key is the largest key in tree jmp short Heap_Restructured ; Insertion over comment ~ node i has lchild at 2*i and rchild at 2*i+1. But we maintain their address relative to start of node array. [ie node 1 has addr 0, node 2 has 4, node 3 12 and so on.] so if x happens to be the address of a node, the address of its parent is (x/2 -2) AND 0fffch. if x is the address of a parent, the address of its lchild is 2*x + 4 and that of its rchild is 2*x + 8 end comment ~ Heap_Violated: call CompactList ; make sure heap is compacted first! mov [si].pq_lastkey,VERYLARGE ; to imply heap nolonger seq. ordered mov bx,[si].pq_index ; bx = offset of inserted elem. sub bx,SIZE ENTRY Heap_Walk_Loop: cmp bx,[si].pq_start ; traversed to top of heap ? jz Heap_Restructured mov cx,bx shr cx,1 ; cx points to parent of current node dec cx dec cx and cx,0fffch ; refer to comment above .errnz (SIZE ENTRY - 4) ; Test whether current node has to move up or not, if not it resets carry ; else it swaps the two nodes and sets carry call TestShuffle mov bx,cx ; next node to inspect ifnec. is parent jc Heap_Walk_Loop Heap_Restructured: smov ds,es ; get back own segment in ds cCall GlobalUnlock, ; di still has the handle mov ax,di ; return true cInsert_Done: cEnd ;-----------------------------------------------------------------------------; ; TestShuffle: ; ; ; ; Takes as input the node addresses of a parent and on of it's childs. If the; ; key of the parent is >= key of the child, it returns with carry clear, else; ; it swaps the two nodes and returns with carry set. ; ; ; ; bx has current node address in HEAP, relative to start NODE 1 ; ; cx has address of parent node of bx ; ; ; ; -by- Amit Chatterjee [amitc] Tue Aug 9 12:00:00 ; ;-----------------------------------------------------------------------------; cProc TestShuffle,, cBegin lea di,[si][SIZE PQ] ; di points to node 1 add di,cx ; di points to parent lea si,[bx].SIZE PQ ; si points to child node mov ax,[si].e_key ; childs key cmp ax,[di].e_key ; key of parent jb Nodes_Tobe_Swapped ; ; Carry cleared by comparision, use for return ; jmp short TestShuffle_Ret Nodes_Tobe_Swapped: ; ; Carry has been set by comparision, use for return ; xchg ax,[di].e_key mov [si].e_key,ax mov ax,[si].e_tag xchg ax,[di].e_tag mov [si].e_tag,ax ; swap complete TestShuffle_Ret: cEnd ;-----------------------------------------------------------------------------; ; MinPQ: ; ; Inputs: ; ; hPQ -- Handle to the heap structure ; ; Outputs: ; ; minimum tag value in the tree or PQERROR(if invalid handle) ; ; ; ; Calls Local Procedure GetMinPQ. ; ; GetMinPQ takes the handle and a flag as parameter. ; ; If the flag is TRUE, the node with the least key is deleted ; ; GetMinPQ also returns the tag value of least key in AX ; ; ; ; ; ; -by- Amit Chatterjee [amitc] Tue Aug 9 12:46:10 ; ;-----------------------------------------------------------------------------; assumes cs,SORT assumes ds,nothing cProc MinPQ, ; parmW hPQ cBegin nogen mov cl,FALSE ; to imply node not to be deleted jmpnext ; fall through trick, refer cmacros cEnd nogen ;-----------------------------------------------------------------------------; ; ExtractPQ: ; ; Inputs: ; ; hPQ -- Handle to the heap structure ; ; Outputs: ; ; minimum tag value if heap handle is valid and heap not empty ; ; return PQERROR otherwise ; ; The node with min key is deleted ; ; Calls Local Procedure GetMinPQ ; ; ; ; -by- Amit Chatterjee [amitc] Tue Aug 9 12:54:00 ; ;-----------------------------------------------------------------------------; assumes cs,SORT assumes ds,nothing cProc ExtractPQ, ; parmW hPQ cBegin nogen mov cl,TRUE ; to imply that node to be deleted jmpnext stop ; fall through trick, refer cmacros cEnd nogen ;-----------------------------------------------------------------------------; ; GetMinPQ: ; ; ; ; One of the inputs is a flag. If the flag is FALSE it simply returns the tag ; ; associated with the lease key value in the heap. If the flag is TRUE besides; ; returnning the above tag value it also deletes the node. ; ; ; ; ; ; hPQ --- handle of HEAP segment ; ; cl --- Deletion Flag ( Delete node if TRUE) ; ; ; ; -by- Amit Chatterjee [amitc] Tue Aug 9 13:00:00 ; ;-----------------------------------------------------------------------------; cProc GetMinPQ,, parmW hPQ cBegin mov di,hPQ push cx ; save flag cCall GlobalLock, pop cx ; get back flag into cl or dx,ax ; invalid handle implies zero return jz Min_Cant_Proceed mov es,dx mov si,ax ; ds:si points to heap start mov bx,es:[si].pq_start ; pointer to 1st. available slot cmp bx,es:[si].pq_index ; empty if equal to next available node jb Heap_Not_Empty cCall GlobalUnlock, Min_Cant_Proceed: mov ax,PQERROR jmp cGetMin_Done ; error return ; bx still has [si].pq_start Heap_Not_Empty: push es ; save heap segment smov es,ds ; save local segment is es pop ds ; ds:si points to start of heap lea dx,[si][SIZE PQ] add dx,bx ; points past deleted nodes xchg di,dx ; save di in dx and use dx's value mov ax,[di].e_tag ; get the tag associated with least key xchg di,dx ; get back values or cl,cl ; test for bl = FALSE .errnz (0 - FALSE) jnz Delete_Node ; TRUE implies get tag and delete node jmp cGetMin_Ret ; return after unlocking heap Delete_Node: ; bx retains [si].start add bx,SIZE ENTRY ; one more node deleted cmp bx,[si].pq_index ; is tree empty ? jb Tree_Not_Empty xor cx,cx mov [si].pq_lastkey,cx ; initialize for empty tree mov [si].pq_start,cx ; initialize for empty tree mov [si].pq_index,cx jmp cGetMin_Ret ; return after unlocking heap Tree_Not_Empty: ; ax has return tag value ; bx has [si].pq_start + SIZE ENTRY cmp [si].pq_lastkey,VERYLARGE ; implies keys in random order jae Min_Restructure_Heap ; maybe restructuring necessary mov [si].pq_start,bx ; updates past deleted entry jmp cGetMin_Ret Min_Restructure_Heap: ; dx still has offset to NODE 1, because ; if LASTKEY = VERYLARGE, pq_start has to be zero push ax ; save return tag value mov bx,dx ; offset to first active node xchg di,dx ; get pointer into di ,save di add di,[si].pq_index ; dx points to next available slot sub di,SIZE ENTRY ; point to last filled node mov ax,di ; last node being moved upfront sub ax,SIZE PQ ; point ax one node ahead of last mov [si].pq_index,ax ; update it mov cx,[di].e_key mov [bx].e_key,cx ; move from last position to NODE 1 mov cx,[di].e_tag mov [bx].e_tag,cx xchg di,dx ; restore di,dx xor cx,cx ; start traversing heap from root Min_Traverse_Heap: mov bx,cx shl bx,1 add bx,SIZE ENTRY ; bx has left child addr of parent in cx cmp bx,[si].pq_index ; compare with next available slot jae Min_Heap_Fixed ; heap restored push cx ; save current parent mov cx,bx ; have lchild in cx add cx,SIZE ENTRY ; cx now get address of rchild cmp cx,[si].pq_index ; test against last node jae Right_Child_Not_Present call GetLesserChild ; gets child with lesser key in bx Right_Child_Not_Present: pop cx ; get back parent ; ; cx has node number of parent node and bx has node no of child node with ; least key. If parents key value is greater it should be swapped ; call TestShuffle ; swaps the two nodes if necessary. mov cx,bx ; lesser child is next parent jmp Min_Traverse_Heap Min_Heap_Fixed: pop ax ; get back return tag value cGetMin_Ret: push ax ; save return value smov ds,es ; get back own ds cCall GlobalUnlock, ; unlock heap pop ax ; get back return value cGetMin_Done: cEnd ;-----------------------------------------------------------------------------; ; GetLesserChild: ; ; ; ; Given two child node numbers, it returns the child which has a lesser key ; ; ; ; cx has RCHILD NODE address ; ; bx has LCHILD NODE address ; ; si points to start of heap ; ; will return node address of lesser child in bx ; ; ; ; -by- Amit Chatterjee [amitc] Tue Aug 9 13:50 ; ;-----------------------------------------------------------------------------; cProc GetLesserChild,, cBegin lea di,[si][SIZE PQ] ; dx now points to NODE 1 mov si,di ; si also points to start of NODE 1 add di,cx ; di get address of rchild mov ax,[si+bx].e_key ; rchilds key cmp ax,[di].e_key ; compare with rchild jb Right_Child_Lesser ; bx still has the correct child no. mov bx,cx ; get RCHILD address into bx Right_Child_Lesser: cEnd ; ;-----------------------------------------------------------------------------; ; DeletePQ: ; ; Inputs: ; ; hPQ --- handle to a heap structure ; ; OutPuts: nothing ; ; Preserves: DI ; ; ; ; -by- Amit Chatterjee [amitc] Tue Aug 9 14:15:45 ; ;----------------------------------------------------------------------------- ; assumes cs,SORT assumes ds,nothing cProc DeletePQ, parmW hPQ cBegin cCall GlobalFree, ; free the handle cEnd ;-----------------------------------------------------------------------------; ;SizePQ: ; ; Input: ; ; hPQ --- Handle to a heap structure ; ; entry --- number of nodes by which heap is to be expanded ; ; Output: ; ; Returns the total number of node slots in new heap, if successful ; else return PQERROR ; ; ; ; -by- Amit Chatterjee [amitc] Tue Aug 9 14:31:40 ; ;----------------------------------------------------------------------------- assumes cs,SORT assumes ds,nothing cProc SizePQ,, parmW hPQ parmW cEntry cBegin mov di,hPQ cCall GlobalLock, ; lock to get back segment address or ax,dx ; Invalid handle implies NULL return jz Siz_Cant_Proceed mov es,dx xor si,si ; offset will always be zro mov ax,cEntry ; additional nodes or ax,ax ; if zero return original numof nodes jnz Size_Tobe_Increased mov ax,es:[si].pq_maxentry ; offset past last node shr ax,1 shr ax,1 ; ax <--- ax / SIZE ENTRY .errnz (SIZE ENTRY - 4) jmp short cSize_Ret ; return after unlocking handle Size_Tobe_Increased: shl ax,1 shl ax,1 ; ax <-- ax * SIZE ENTRY, = extra bytes .errnz (SIZE ENTRY - 4) add ax,es:[si].pq_maxentry ; number of byte for new node array cmp ax,es:[si].pq_index ; next available slot jae Valid_Increase mov ax,PQERROR ; error code jmp short cSize_Ret ; return after releasing handle Valid_Increase: push ax ; save number of bytes in node block add ax,SIZE PQ ; size of header push ax cCall GlobalUnlock, ; unlock handle xor dx,dx ; high word for size pop ax ; get back size cCall GlobalReAlloc, or ax,ax jz Siz_Cant_Proceed mov di,ax ; new handle cCall GlobalLock, ; lock it mov es,dx ; set new segment pop cx ; get back total no of nodes into cx jmp short Reloc_Successful Siz_Cant_Proceed: pop cx ; balance stack dec ax .errnz (-1 - PQERROR) jmp short cSize_End Reloc_Successful: mov es:[si].pq_maxentry,cx ; total number of slots now shr cx,1 shr cx,1 ; no of nodes = bytes / SIZE ENTRY .errnz (SIZE ENTRY - 4) mov ax,cx ; return value cSize_Ret: cCall GlobalUnlock, cSize_End: cEnd ;-----------------------------------------------------------------------------; ;CompactList: ; ; Input: ; ; ds:si --- pointer to heap structure ; ; Output: ; ; all deleted elements are removed from heap structure ; ; Registers trashed: ; AX,BX,CX,DX ; ; -by- Ken Sykes [kensy] Tue Nov 12 1991 10:20:00am ; ;----------------------------------------------------------------------------- CompactList proc near mov ax,[si].pq_index ; next available slot in node area sub ax,[si].pq_start ; ax had pointer to available slot mov [si].pq_index,ax ; next available slot will come up lea dx,[si][SIZE PQ] ; points to NODE 1 mov ax,[si].pq_start ; pointer to 1st active node rel to 1 add ax,dx ; ax has offset to first valid node. mov bx,ax mov cx,[si].pq_maxentry ; pointer past end of node slots sub cx,[si].pq_start ; pointer to strt of active block shr cx,1 ; will do a REP MOVSW .errnz (1 and SIZE ENTRY) push es ; es has local segment smov es,ds ; moth es ds point to heap segment push si push di ; save start to heap and its handle mov si,bx ; si points to start of valid nodes mov di,dx ; dx points to node 1 cld rep movsw ; Compacted pop di pop si pop es ; restore local segment in es mov [si].pq_start,cx ; after compaction deleted nodes = 0 ret CompactList endp ;-----------------------------------------------------------------------------; sEnd SORT end