Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1452 lines
34 KiB

  1. TITLE GCOMPACT - Global memory compactor
  2. .sall
  3. .xlist
  4. include kernel.inc
  5. include protect.inc
  6. .list
  7. WM_COMPACTING = 041h
  8. .286p
  9. DataBegin
  10. externA __AHINCR
  11. externB Kernel_Flags
  12. externW kr2dsc
  13. ;externW WinFlags
  14. externD gcompact_start
  15. externD gcompact_timer
  16. externD pPostMessage
  17. externB DpmiMemory
  18. externW DpmiBlockCount
  19. fSwitchStacks DB 0
  20. fUpDown DB 0
  21. DataEnd
  22. sBegin CODE
  23. assumes CS,CODE
  24. ;externW gdtdsc
  25. externNP glrudel
  26. externNP gmarkfree
  27. externNP gcheckfree
  28. externNP gdel_free
  29. externNP gnotify
  30. externNP Enter_gmove_stack
  31. externNP Leave_gmove_stack
  32. if KDEBUG
  33. externFP ValidateFreeSpaces
  34. endif
  35. externFP SetSelectorBase
  36. externNP get_arena_pointer
  37. externNP get_physical_address
  38. externNP set_physical_address
  39. externNP set_sel_limit
  40. externNP get_rover_2
  41. externNP cmp_sel_address
  42. externNP get_temp_sel
  43. externNP alloc_data_sel_above
  44. externNP alloc_data_sel_below
  45. externNP free_sel
  46. externNP PreallocSel
  47. externNP AssociateSelector
  48. externNP mark_sel_NP
  49. if ALIASES
  50. externNP check_for_alias
  51. endif
  52. externNP DPMIProc
  53. ;-----------------------------------------------------------------------;
  54. ; gcompact ;
  55. ; ;
  56. ; Compacts the global heap. ;
  57. ; ;
  58. ; Arguments: ;
  59. ; DX = minimum #contiguous bytes needed ;
  60. ; DS:DI = address of global heap information ;
  61. ; ;
  62. ; Returns: ;
  63. ; AX = size of largest contiguous free block ;
  64. ; ES:DI = arena header of largest contiguous free block ;
  65. ; DX = minimum #contiguous bytes needed ;
  66. ; ;
  67. ; Error Returns: ;
  68. ; ;
  69. ; Registers Preserved: ;
  70. ; SI ;
  71. ; ;
  72. ; Registers Destroyed: ;
  73. ; CX ;
  74. ; ;
  75. ; Calls: ;
  76. ; gcmpheap ;
  77. ; gcheckfree ;
  78. ; gdiscard ; ;
  79. ; History: ;
  80. ; ;
  81. ; Thu Sep 25, 1986 05:34:32p -by- David N. Weise [davidw] ;
  82. ; Added this nifty comment block. ;
  83. ;-----------------------------------------------------------------------;
  84. assumes ds,nothing
  85. assumes es,nothing
  86. cProc gcompact,<PUBLIC,NEAR>
  87. cBegin nogen
  88. push si
  89. mov ax,40h
  90. mov es,ax
  91. mov ax,es:[6Ch] ; get the BIOS ticker count
  92. SetKernelDS es
  93. sub gcompact_timer.lo,ax
  94. gcompactl:
  95. if KDEBUG
  96. call ValidateFreeSpaces
  97. endif
  98. push dx ; Save requested size
  99. cmp [di].gi_reserve,di ; Is there a reserve swap area?
  100. je gcompact1 ; No, then dont compact lower heap
  101. mov es,[di].hi_first ; Yes, compact lower heap
  102. mov bx,ga_next
  103. call gcmpheap
  104. gcompact1:
  105. test [di].gi_cmpflags,BOOT_COMPACT
  106. jz gcompact1a
  107. pop dx
  108. jmps gcompactx
  109. gcompact1a:
  110. mov es,[di].hi_last ; Compact upper heap
  111. mov bx,ga_prev
  112. call gcmpheap
  113. pop dx ; Get requested size
  114. mov es,ax ; ES points to largest free block
  115. or ax,ax ; Did we find a free block?
  116. jz gcompact2 ; No, try discarding
  117. call gcheckfree ; Yes, see if block big enough
  118. jae gcompactx ; Yes, all done
  119. gcompact2: ; Discarding allowed?
  120. test [di].gi_cmpflags,GA_NODISCARD+GA_NOCOMPACT
  121. jnz gcompactx ; No, return
  122. cmp [di].hi_freeze,di ; Heap frozen?
  123. jne gcompactx ; Yes, return
  124. push es
  125. call gdiscard ; No, try discarding
  126. pop cx ; Saved ES may be bogus if gdiscard
  127. ; discarded anything...
  128. jnz gcompactl ; Compact again if anything discarded
  129. mov es, cx ; Nothing discarded so ES OK.
  130. gcompactx:
  131. push ax
  132. push dx
  133. push es
  134. mov ax,40h
  135. mov es,ax
  136. mov ax,es:[6Ch]
  137. mov si,ax
  138. SetKernelDS es
  139. cmp pPostMessage.sel,0 ; is there a USER around yet?
  140. jz tock
  141. add gcompact_timer.lo,ax
  142. sub ax,gcompact_start.lo
  143. cmp ax,546 ; 30 secs X 18.2 tics/second
  144. jb tock
  145. cmp ax,1092 ; 60 secs
  146. ja tick
  147. mov cx,gcompact_timer.lo ; poor resolution of timer!
  148. jcxz tick
  149. xchg ax,cx
  150. xor dx,dx
  151. xchg ah,al ; shl 8 DX:AX
  152. xchg dl,al
  153. div cx
  154. cmp ax,32 ; < 12.5% ?
  155. jb tick
  156. mov ah,al
  157. mov bx,-1 ; broadcast
  158. mov cx,WM_COMPACTING
  159. xor dx,dx
  160. push es
  161. cCall pPostMessage,<bx, cx, ax, dx, dx>
  162. pop es
  163. tick: mov gcompact_start.lo,si
  164. mov gcompact_timer.lo,0
  165. tock: pop es
  166. pop dx
  167. pop ax
  168. pop si ; Restore SI
  169. ret
  170. cEnd nogen
  171. ;-----------------------------------------------------------------------;
  172. ; gcmpheap ;
  173. ; ;
  174. ; ;
  175. ; Arguments: ;
  176. ; BX = ga_prev or ga_next ;
  177. ; DX = minimum #contiguous bytes needed ;
  178. ; DS:DI = address of global heap information ;
  179. ; ES:DI = first arena to start with ;
  180. ; ;
  181. ; Returns: ;
  182. ; AX = largest free block ;
  183. ; ;
  184. ; Error Returns: ;
  185. ; ;
  186. ; Registers Preserved: ;
  187. ; ;
  188. ; Registers Destroyed: ;
  189. ; AX,CX ;
  190. ; ;
  191. ; Calls: ;
  192. ; gslide ;
  193. ; gbestfit ;
  194. ; ;
  195. ; History: ;
  196. ; ;
  197. ; Thu Sep 25, 1986 05:38:16p -by- David N. Weise [davidw] ;
  198. ; Added this nifty comment block. ;
  199. ;-----------------------------------------------------------------------;
  200. assumes ds,nothing
  201. assumes es,nothing
  202. cProc gcmpheap,<PUBLIC,NEAR>
  203. cBegin nogen
  204. mov cx,[di].hi_count
  205. xor ax,ax ; Nothing found yet
  206. push ax ; Save largest free block so far
  207. gchloop:
  208. cmp es:[di].ga_owner,di
  209. je gchfreefnd
  210. gchnext:
  211. mov es,es:[bx]
  212. loop gchloop
  213. pop ax ; Return largest free block in AX
  214. ret
  215. gchfreefnd:
  216. test [di].gi_cmpflags,GA_NOCOMPACT
  217. jnz gchmaxfree ; No, just compute max free.
  218. cmp [di].hi_freeze,di ; Heap frozen?
  219. jne gchmaxfree ; Yes, just compute max free.
  220. push es
  221. cmp bl,ga_prev ; Compacting upper heap?
  222. jnz no_hack
  223. test [di].gi_cmpflags,GA_DISCCODE
  224. jz no_hack
  225. cmp dx,es:[ga_size]
  226. ja no_hack
  227. mov es,es:[di].ga_next
  228. test es:[di].ga_flags,GA_DISCCODE
  229. jnz hack
  230. cmp es:[di].ga_owner,GA_SENTINAL
  231. jz hack
  232. cmp es:[di].ga_owner,GA_NOT_THERE
  233. jnz no_hack
  234. ; Check to see if anything is locked above us!
  235. gch_hack_check:
  236. test es:[di].ga_flags,GA_DISCCODE
  237. jnz hat_check
  238. mov ax,es:[di].ga_owner
  239. or ax,ax
  240. jz hat_check
  241. cmp ax,GA_NOT_THERE
  242. jz hat_check
  243. cmp ax,GA_SENTINAL
  244. jz hack
  245. jmp short no_hack
  246. hat_check:
  247. mov es,es:[di].ga_next
  248. jmp gch_hack_check
  249. hack: pop es
  250. pop ax
  251. mov ax,es
  252. ret
  253. no_hack:
  254. pop es
  255. call PreallocSel
  256. jz gchmaxfree
  257. push dx
  258. call gslide
  259. pop dx
  260. jnz gchfreefnd
  261. best_it:
  262. push dx
  263. call gbestfit
  264. pop dx
  265. jnz best_it ; Useless to gslide anymore.
  266. gchmaxfree:
  267. cmp bl,ga_prev ; Compacting upper heap?
  268. jne gchnext ; No, dont compute largest free block
  269. pop si ; Recover largest free block so far
  270. mov ax,es ; AX = current free block
  271. cmp si,ax ; Same as current?
  272. je gchmf2 ; Yes, no change then
  273. cmp es:[di].ga_owner,di ; No, is current free?
  274. jne gchmf2 ; No, ignore it then
  275. push es
  276. cmp ds:[di].gi_reserve,di ; Is there a code reserve area?
  277. je gchmf0 ; No, continue
  278. test ds:[di].gi_cmpflags,GA_DISCCODE ; If allocating disc
  279. jz gchmf0 ; code then only use free block if
  280. mov es,es:[di].ga_next
  281. test es:[di].ga_flags,GA_DISCCODE
  282. jnz gchmf0 ; butted up against disc code
  283. cmp es:[di].ga_owner,GA_SENTINAL ; or sentinal.
  284. pop es
  285. jnz gchmf2
  286. push es
  287. gchmf0:
  288. pop es
  289. or si,si ; First time?
  290. jz gchmf1 ; Yes, special case
  291. push es
  292. mov es,si
  293. mov ax,es:[di].ga_size ; No, get size of largest free block
  294. pop es ; Compare with size of this free block
  295. cmp es:[di].ga_size,ax ; Is it bigger?
  296. jb gchmf2 ; No, do nothing
  297. gchmf1: mov si,es ; Yes, remember biggest free block
  298. gchmf2: push si ; Save largest free block so far
  299. jmp gchnext ; Go process next block
  300. cEnd nogen
  301. ;-----------------------------------------------------------------------;
  302. ; gslide ;
  303. ; ;
  304. ; Sees if next/previous block can slide into the passed free block. ;
  305. ; ;
  306. ; Arguments: ;
  307. ; ES:DI = free block ;
  308. ; DS:DI = address of global heap information ;
  309. ; CX = #arena entries left to examine ;
  310. ; BX = ga_next or ga_prev ;
  311. ; ;
  312. ; Returns: ;
  313. ; ZF = 0 if block found and moved into passed free block ;
  314. ; ES:DI points to new free block ;
  315. ; SI:DI points to new free block ;
  316. ; ;
  317. ; ZF = 1 if no block found ;
  318. ; ES:DI points to original free block ;
  319. ; ;
  320. ; Error Returns: ;
  321. ; ;
  322. ; Registers Preserved: ;
  323. ; ;
  324. ; Registers Destroyed: ;
  325. ; AX,DX ;
  326. ; ;
  327. ; Calls: ;
  328. ; ;
  329. ; History: ;
  330. ; ;
  331. ; Thu Sep 25, 1986 05:58:25p -by- David N. Weise [davidw] ;
  332. ; Added this nifty comment block. ;
  333. ;-----------------------------------------------------------------------;
  334. cProc gslide,<PUBLIC,NEAR>
  335. cBegin nogen
  336. push es
  337. mov es,es:[bx]
  338. mov ax,es
  339. mov dx,es:[di].ga_size
  340. call gmoveable
  341. pop es
  342. jz gslide_no_move
  343. cmp bx,ga_next
  344. jnz sliding_up
  345. push es:[bx] ; gslide will invalidate this block
  346. jmps slide
  347. sliding_up:
  348. push es ; gslide will invalidate this block
  349. slide: call gslidecommon ; C - calling convention
  350. call free_sel ; free the pushed selector
  351. gslide_no_move:
  352. ret
  353. cEnd nogen
  354. ; Enter here from gbestfit
  355. cProc gslidecommon,<PUBLIC,NEAR>
  356. cBegin nogen
  357. ; moving non-contiguous blocks
  358. push es:[di].ga_freeprev
  359. push es:[di].ga_freenext
  360. mov si,ax ; Source is busy block
  361. inc dx ; DX = busy.ga_size + 1
  362. cmp bl,ga_next
  363. je gslidedown
  364. ; Here to slide moveable block up to high end of free block.
  365. ;
  366. ; Free and busy block adjacent
  367. ; 0000:0 | | | |
  368. ; |-----------| a -> |-----------|
  369. ; | busy | | free ? |
  370. ; |-----------| | |
  371. ; | free | b -> |-----------|
  372. ; | | | ? busy ? |
  373. ; |-----------| c -> |-----------|
  374. ; FFFF:0 | | | ? |
  375. ;
  376. ;
  377. ; a = busy
  378. ; b = free.ga_next - busy.ga_size - 1
  379. ; c = free.ga_next
  380. ; destination = b
  381. ;
  382. ; Free and busy block NOT adjacent
  383. ; 0000:0 | | | |
  384. ; |-----------| |-----------|
  385. ; | busy | | free |
  386. ; |-----------| |-----------|
  387. ; | | | |
  388. ; |-----------| a -> |-----------|
  389. ; | free | | free ? |
  390. ; | | b -> |-----------|
  391. ; | | | ? busy ? |
  392. ; |-----------| c -> |-----------|
  393. ; FFFF:0 | | | ? |
  394. ;
  395. ;
  396. ; a = free
  397. ; b = free.ga_next - busy.ga_size - 1
  398. ; c = free.ga_next
  399. ; destination = b
  400. ;
  401. gslideup:
  402. mov ax,es:[di].ga_next
  403. push ax ; Save c
  404. cCall alloc_data_sel_below, <ax, dx> ; Bogus arena header
  405. push ax ; Save b
  406. cmp es:[bx],si ; Are blocks adjacent?
  407. je gslideup1
  408. push es ; No, a = free
  409. jmps gslideup2
  410. gslideup1:
  411. push si ; Yes, a = busy
  412. gslideup2:
  413. mov es,ax ; Destination is b
  414. xor ax,ax ; a.ga_prev will remain valid
  415. jmps gslidemove
  416. ; Here to slide moveable block down to low end of free block.
  417. ;
  418. ; Free and busy block adjacent
  419. ; 0000:0 | | | |
  420. ; |-----------| a -> |-----------|
  421. ; | free | | ? busy ? |
  422. ; | | b -> |-----------|
  423. ; |-----------| | ? free ? |
  424. ; | busy | | |
  425. ; |-----------| c -> |-----------|
  426. ; FFFF:0 | | | ? |
  427. ;
  428. ; a = free
  429. ; b = free + busy.ga_size + 1
  430. ; c = busy.ga_next
  431. ; destination = free
  432. ;
  433. ; Free and busy block NOT adjacent
  434. ; 0000:0 | | | |
  435. ; |-----------| a -> |-----------|
  436. ; | free | | ? busy ? |
  437. ; | | b -> |-----------|
  438. ; | | | ? free ? |
  439. ; |-----------| c -> |-----------|
  440. ; | | | ? |
  441. ; |-----------| |-----------|
  442. ; | busy | | free |
  443. ; |-----------| |-----------|
  444. ; FFFF:0 | | | |
  445. ;
  446. ;
  447. ; a = free
  448. ; b = free + busy.ga_size + 1
  449. ; c = free.ga_next
  450. ; destination = free
  451. ;
  452. gslidedown:
  453. cmp es:[bx],si ; Are blocks adjacent?
  454. je gslidedn1
  455. push es:[di].ga_next ; No, c = free.ga_next
  456. jmps gslidedn2
  457. gslidedn1:
  458. push es
  459. mov es,si
  460. mov ax,es:[di].ga_next
  461. pop es
  462. push ax
  463. gslidedn2:
  464. cCall alloc_data_sel_above, <es, dx>
  465. push ax ; Save b
  466. push es ; Save a
  467. mov ax,es:[di].ga_prev ; a.ga_prev must be restored after move
  468. gslidemove:
  469. push ax
  470. push si ; Source arena
  471. mov ax, es
  472. mov es, si ; Save source arena contents
  473. push word ptr es:[di]
  474. push word ptr es:[di+2]
  475. push word ptr es:[di+4]
  476. push word ptr es:[di+6]
  477. push word ptr es:[di+8]
  478. push word ptr es:[di+10]
  479. push word ptr es:[di+12]
  480. push word ptr es:[di+14]
  481. mov es, ax
  482. call gmove
  483. pop word ptr es:[di+14] ; "Copy" source arena to destination
  484. pop word ptr es:[di+12]
  485. pop word ptr es:[di+10]
  486. pop word ptr es:[di+8]
  487. pop word ptr es:[di+6]
  488. pop word ptr es:[di+4]
  489. pop word ptr es:[di+2]
  490. pop word ptr es:[di]
  491. pop si
  492. pop ax
  493. ; update lruentries
  494. mov dx,es:[di].ga_lruprev
  495. or dx,dx
  496. jz no_links
  497. cmp [di].gi_lruchain,si
  498. jnz didnt_slide_head
  499. mov [di].gi_lruchain,es
  500. didnt_slide_head:
  501. cmp dx,si ; Did we move the only entry?
  502. jnz many_entries
  503. mov es:[di].ga_lrunext,es
  504. mov es:[di].ga_lruprev,es
  505. jmps no_links
  506. many_entries:
  507. push ds
  508. mov ds,dx
  509. mov ds:[di].ga_lrunext,es
  510. mov ds,es:[di].ga_lrunext
  511. mov ds:[di].ga_lruprev,es
  512. pop ds
  513. no_links:
  514. mov si,es ; Save new busy block location
  515. pop es ; ES = a
  516. or ax,ax ; Does a.prev need to be restored?
  517. jz gslide1 ; No, continue
  518. mov es:[di].ga_prev,ax ; Yes, do it
  519. gslide1:
  520. ; update arena prev and next pointers
  521. pop ax
  522. mov es:[di].ga_next,ax ; a.ga_next = b
  523. mov dx,es
  524. mov es,ax
  525. mov es:[di].ga_prev,dx ; b.ga_prev = a
  526. pop ax
  527. mov es:[di].ga_next,ax ; b.ga_next = c
  528. mov dx,es
  529. mov es,ax
  530. mov es:[di].ga_prev,dx ; c.ga_prev = b
  531. mov es,si ; ES = new busy block
  532. mov si,es:[di].ga_handle ; SI = handle
  533. or si,si
  534. jz gslide2
  535. cCall AssociateSelector,<si,es>
  536. gslide2:
  537. mov es,es:[bx] ; Move to new free block
  538. push bx
  539. push cx
  540. cCall get_physical_address,<es:[di].ga_next>
  541. mov bx,ax
  542. mov cx,dx
  543. cCall get_physical_address,<es>
  544. sub bx,ax
  545. sbb cx,dx
  546. REPT 4
  547. shr cx,1
  548. rcr bx,1
  549. ENDM
  550. mov ax,bx
  551. pop cx
  552. pop bx
  553. mov si,es
  554. dec ax
  555. mov es:[di].ga_size,ax
  556. mov es:[di].ga_flags,0
  557. ; update the global free list
  558. pop ax ; ga_freenext
  559. pop dx ; ga_freeprev
  560. mov es:[di].ga_freeprev,dx
  561. mov es:[di].ga_freenext,ax
  562. mov es,dx
  563. mov es:[di].ga_freenext,si
  564. mov es,ax
  565. mov es:[di].ga_freeprev,si
  566. mov es,si
  567. if KDEBUG
  568. test si, 1 ; make SI odd for gmarkfree
  569. jnz ok
  570. INT3_WARN
  571. ok:
  572. endif
  573. call gmarkfree ; Coalesce new free block
  574. mov si,es
  575. or ax,ax
  576. ret
  577. cEnd nogen
  578. ;-----------------------------------------------------------------------;
  579. ; gmove ;
  580. ; ;
  581. ; Moves a moveable block into the top part of a free block. ;
  582. ; ;
  583. ; Arguments: ;
  584. ; DS:DI = master object ;
  585. ; ES:0 = arena of destination block ;
  586. ; SI:0 = arena of source block ;
  587. ; ;
  588. ; Returns: ;
  589. ; DS:DI = master object (it may have moved) ;
  590. ; ;
  591. ; Error Returns: ;
  592. ; ;
  593. ; Registers Preserved: ;
  594. ; AX,BX,CX,DX,DI,SI,ES ;
  595. ; ;
  596. ; Registers Destroyed: ;
  597. ; none ;
  598. ; ;
  599. ; Calls: ;
  600. ; gnotify ;
  601. ; ;
  602. ; History: ;
  603. ; ;
  604. ; Thu Sep 25, 1986 03:31:51p -by- David N. Weise [davidw] ;
  605. ; Added this nifty comment block. ;
  606. ;-----------------------------------------------------------------------;
  607. assumes ds,nothing
  608. assumes es,nothing
  609. cProc gmove,<PUBLIC,NEAR>
  610. cBegin nogen
  611. push es
  612. pusha
  613. cCall get_physical_address,<es> ; Destination arena address
  614. add ax, 10h ; Destination is one paragraph
  615. adc dx, 0 ; past the arena header
  616. push ax ; Save to update source
  617. push dx ; selectors after the move
  618. push ax ; Scribbled by SetSelectorBase
  619. push dx
  620. SetKernelDS es
  621. cCall SetSelectorBase,<kr2dsc,dx,ax> ; NOTE kr2dsc has SEG_RING set
  622. ; While we have destination
  623. ; address, see if move will
  624. ; be up or down
  625. pop cx
  626. pop bx ; CX:BX has address of dest
  627. cCall get_physical_address,<si> ; Source address
  628. cmp dx, cx ; cmp src, dest
  629. jne @F
  630. cmp ax, bx
  631. @@:
  632. mov cx, 0 ; move to preserve CARRY
  633. adc ch, 0
  634. push cx ; Save fUpDown, CL = 0
  635. mov es, si ; ES:DI = arena header of source
  636. UnSetKernelDS es
  637. push es:[di].ga_size ; Save #paragraphs
  638. mov bx, es:[di].ga_handle ; BX = handle of source
  639. Handle_To_Sel bl
  640. if KDEBUG
  641. cCall get_arena_pointer,<bx>
  642. cmp ax, si
  643. je @F
  644. INT3_ANCIENT
  645. @@:
  646. endif
  647. mov si, bx ; SI = client data selector of source
  648. mov cx, bx ; CX = client data selector of dest
  649. mov ax, GN_MOVE
  650. call gnotify ; Call global notify procedure
  651. pop dx ; DX = #paragraphs to move
  652. pop cx ; CH has fUpDown, CL = 0
  653. pop bx ; Destination address in registers
  654. pop di ; in case we switch stacks
  655. SetKernelDS es
  656. mov fUpDown, ch
  657. mov ax,ss ; Are we about to move the stack?
  658. cmp ax,si
  659. jne stack_no_move
  660. mov cx, ax ; Selector does not change!
  661. call Enter_gmove_stack ; switch to temporary stack
  662. stack_no_move:
  663. mov [fSwitchStacks],cl ; Remember if we switched
  664. ; Save DS value AFTER call to gnotify, as the master object might be the
  665. ; block we are moving and thus changed by the global heap notify proc.
  666. ; IRRELEVANT SINCE IT IS A SELECTOR!!!
  667. push ds
  668. push di ; Re-save destination address -
  669. push bx ; we may be on different stack
  670. push si ; Save source selector
  671. mov di, kr2dsc ; DI = Destination selector array
  672. mov bx, dx ; # paragraphs to copy
  673. xor cx, cx
  674. ; Calculate length
  675. rept 4
  676. shl bx, 1
  677. rcl cx, 1
  678. endm
  679. push cx
  680. cCall set_sel_limit,<di> ; Set destination selector array
  681. pop cx
  682. sub bx, 1 ; Now turn into limit
  683. sbb cl, 0 ; CX has number of selectors - 1
  684. ;
  685. ; At this point, DX = # paragraphs to be moved, SI = source selector,
  686. ; DI = dest selector
  687. ;
  688. or dx,dx ; just in case...
  689. jnz @f
  690. jmps all_moved
  691. @@:
  692. mov bx,dx ; bx = total # paras to move
  693. cld ; assume moving down
  694. mov dx,__AHINCR ; dx = selector increment
  695. cmp fUpDown,0 ; Moving up or down? Need to
  696. jz start_move ; adjust start descriptors if up
  697. assumes es, nothing
  698. std
  699. ; Moving data up. Fudge the address so we copy
  700. ; from the end of each segment.
  701. push dx
  702. mov ax, dx ; Selector increment
  703. mul cx ; * (number of sels - 1)
  704. add si, ax ; Get source and destination selecotors
  705. add di, ax ; for last piece of copy
  706. pop dx
  707. neg dx ; Going backwards through the array
  708. mov ds, si ; Set source and destination segments
  709. mov es, di
  710. mov cx, bx ; See if partial copy first
  711. and cx, 0FFFh ; 64k-1 paragraphs
  712. jz move_blk ; exact multiple, go for it
  713. ; Not exact, set up first copy by hand
  714. sub bx, cx ; This many paragraphs less
  715. shl cx, 4 ; byte count
  716. mov si, cx
  717. shr cx, 1 ; word count
  718. jmps move_up
  719. start_move:
  720. mov ds,si
  721. mov es,di
  722. move_blk:
  723. mov cx,1000h ; 1000h paras = 64k bytes
  724. cmp bx,cx
  725. jae @f
  726. mov cx,bx ; less than 64k left
  727. @@:
  728. sub bx,cx ; bx = # paras left to do
  729. shl cx,3 ; cx = # words to move this time
  730. xor si,si ; assume going down
  731. cmp dl,0 ; really moving down?
  732. jg @F ; yes, jmp, (yes, SIGNED!)
  733. move_up:
  734. dec si ; up: point to last word in segment
  735. dec si
  736. @@:
  737. mov di,si
  738. rep movsw
  739. or bx,bx ; more to do?
  740. jz all_moved
  741. ; Adjust source/dest selectors up/down by 64k
  742. mov si,ds
  743. add si,dx
  744. mov di,es
  745. add di,dx
  746. jmps start_move ; go do the next block
  747. all_moved:
  748. pop si ; Source selector array
  749. pop dx ; New address for source selectors
  750. pop ax
  751. if ALIASES
  752. cCall check_for_alias,<si,dx,ax>
  753. endif
  754. cCall set_physical_address,<si> ; Update source selector array
  755. pop ds ; Restore DS (it might be different)
  756. SetKernelDS es
  757. cmp [fSwitchStacks], 0 ; Switch to new stack if any
  758. je move_exit
  759. call Leave_gmove_stack
  760. move_exit:
  761. popa
  762. pop es
  763. UnSetKernelDS es
  764. cld ; Protect people like MarkCl from themselves
  765. ret
  766. cEnd nogen
  767. ;-----------------------------------------------------------------------;
  768. ; gbestfit ;
  769. ; ;
  770. ; Searches for the largest moveable block that will fit in the passed ;
  771. ; free block. ;
  772. ; ;
  773. ; Arguments: ;
  774. ; ES:DI = free block ;
  775. ; DS:DI = address of global heap information ;
  776. ; CX = #arena entries left to examine ;
  777. ; BX = ga_next or ga_prev ;
  778. ; ;
  779. ; Returns: ;
  780. ; ZF = 1 if block found & moved into free block w/ no extra room. ;
  781. ; ES:DI = busy block before/after new busy block. ;
  782. ; ;
  783. ; ZF = 0 if ES:DI points to a free block, either the ;
  784. ; original one or what is left over after moving a block ;
  785. ; into it. ;
  786. ; ;
  787. ; Error Returns: ;
  788. ; ;
  789. ; Registers Preserved: ;
  790. ; ;
  791. ; Registers Destroyed: ;
  792. ; DX,SI ;
  793. ; ;
  794. ; Calls: ;
  795. ; ;
  796. ; History: ;
  797. ; ;
  798. ; Thu Sep 25, 1986 05:52:12p -by- David N. Weise [davidw] ;
  799. ; Added this nifty comment block. ;
  800. ;-----------------------------------------------------------------------;
  801. cProc gbestfit,<PUBLIC,NEAR>
  802. cBegin nogen
  803. push es
  804. push cx
  805. xor si,si ; Have not found anything yet
  806. xor ax, ax ; largest block so far
  807. mov dx,es:[di].ga_size ; Compute max size to look for
  808. gbfloop:
  809. cmp es:[di].ga_owner,di ; Is this block busy?
  810. je gbfnext ; No, continue
  811. cmp es:[di].ga_size,dx ; Yes, is block bigger than max size?
  812. ja gbfnext ; Yes, continue
  813. call gmoveable ; Yes, is it moveable
  814. jz gbfnext ; No, continue
  815. cmp es:[di].ga_size,ax ; Is it bigger than the largest so far?
  816. jbe gbfnext ; No, continue
  817. gbf1st:
  818. mov si,es ; Yes, remember biggest block
  819. mov ax,es:[di].ga_size ; ...and size
  820. gbfnext:
  821. mov es,es:[bx] ; Skip past this block
  822. loop gbfloop
  823. pop cx ; All done looking
  824. pop es
  825. or si,si ; Did we find a block?
  826. jz gbestfit1 ; No, return with Z flag
  827. call gmovebusy ; Yes, move it into free block
  828. gbestfit1:
  829. ret
  830. cEnd nogen
  831. ;-----------------------------------------------------------------------;
  832. ; gmovebusy ;
  833. ; ;
  834. ; Subroutine to move a busy block to a free block of the same size, ;
  835. ; preserving the appropriate arena header fields, freeing the old ;
  836. ; busy block and updating the handle table entry to point to the ;
  837. ; new location of the block. ;
  838. ; ;
  839. ; Arguments: ;
  840. ; BX = ga_prev or ga_next ;
  841. ; SI = old busy block location ;
  842. ; ES:DI = new busy block location ;
  843. ; DS:DI = address of global heap information ;
  844. ; ;
  845. ; Returns: ;
  846. ; ES:DI = points to new busy block arena header ;
  847. ; SI:DI = points to free block where block used to be ;
  848. ; (may be coalesced) ;
  849. ; ;
  850. ; Error Returns: ;
  851. ; ;
  852. ; Registers Preserved: ;
  853. ; BX,CX,DX ;
  854. ; ;
  855. ; Registers Destroyed: ;
  856. ; AX ;
  857. ; ;
  858. ; Calls: ;
  859. ; ;
  860. ; History: ;
  861. ; ;
  862. ; Mon Jun 22, 1987 11:39:56p -by- David N. Weise [davidw] ;
  863. ; Made it jump off to gslidecommon when appropriate. ;
  864. ; ;
  865. ; Mon Oct 27, 1986 10:17:16p -by- David N. Weise [davidw] ;
  866. ; Made the lru list be linked arenas, so we must keep the list correct ;
  867. ; here. ;
  868. ; ;
  869. ; Thu Sep 25, 1986 05:49:25p -by- David N. Weise [davidw] ;
  870. ; Added this nifty comment block. ;
  871. ;-----------------------------------------------------------------------;
  872. cProc gmovebusy,<PUBLIC,NEAR>
  873. cBegin nogen
  874. push cx
  875. push dx
  876. mov ax,es
  877. mov cx,es:[di].ga_size ; CX = size of destination
  878. cmp es:[di].ga_owner,di ; Is destination busy?
  879. mov es,si
  880. mov dx,es:[di].ga_size ; DX = size of source
  881. jne gmbexactfit ; Yes, then dont create extra block
  882. cmp cx,dx ; No, are source and destination same size?
  883. je gmbexactfit ; Yes, then dont create extra block
  884. mov es,ax ; ES = destination
  885. cmp es:[di].ga_next,si ; Are the two blocks adjacent?
  886. ; If so we want to do a gslide.
  887. mov ax,si ; AX = source
  888. jnz not_adjacent
  889. cmp bx,ga_next
  890. jnz gmb_sliding_up
  891. push es:[bx] ; gslide will invalidate this block
  892. jmps gmb_slide
  893. gmb_sliding_up:
  894. push es ; gslide will invalidate this block
  895. gmb_slide:
  896. call gslidecommon ; C - calling convention
  897. call free_sel ; free the pushed selector
  898. jmp gmbexit
  899. not_adjacent:
  900. push si ; Save busy block address
  901. call gslidecommon ; Call common code to do the move
  902. inc [di].hi_count ; Just created a new arena entry
  903. mov ax,es ; Save new free block address
  904. pop es ; Get old busy block address
  905. xor si,si
  906. call gmarkfree ; Mark as free and coalesce
  907. mov si,es
  908. mov es,ax ; Restore new free block address
  909. or ax,ax ; Return with Z flag clear.
  910. jmps gmbexit
  911. gmbexactfit:
  912. mov ch,es:[di].ga_count
  913. mov cl,es:[di].ga_flags
  914. push es:[di].ga_owner
  915. push es:[di].ga_lruprev
  916. push es:[di].ga_lrunext
  917. mov es,ax
  918. jne it_wasnt_free
  919. call gdel_free
  920. it_wasnt_free:
  921. pop es:[di].ga_lrunext ; Copy client words to new header
  922. pop es:[di].ga_lruprev
  923. pop es:[di].ga_owner
  924. mov es:[di].ga_flags,cl
  925. mov es:[di].ga_count,ch
  926. cmp es:[di].ga_lruprev,di
  927. jz no_link
  928. cmp [di].gi_lruchain,si
  929. jnz didnt_move_head
  930. mov [di].gi_lruchain,es
  931. didnt_move_head:
  932. push ds
  933. mov ds,es:[di].ga_lruprev
  934. mov [di].ga_lrunext,ax ; Update the lru list
  935. mov ds,es:[di].ga_lrunext
  936. mov [di].ga_lruprev,ax ; Update the lru list
  937. pop ds
  938. no_link:
  939. mov es, ax ; ES is destination of copy
  940. call gmove ; Move the client data
  941. mov es, si
  942. xor si,si
  943. call gmarkfree ; Free old block
  944. mov cx,es
  945. mov es,ax
  946. or si,si
  947. jz gmb1
  948. mov es:[di].ga_handle,si ; Set back link to handle in new block
  949. cCall AssociateSelector,<si,es> ; and associate with new arena
  950. xor dx,dx ; Set Z flag
  951. gmb1:
  952. mov si,cx
  953. gmbexit:
  954. pop dx
  955. pop cx
  956. ret
  957. cEnd nogen
  958. ;-----------------------------------------------------------------------;
  959. ; gmoveable ;
  960. ; ;
  961. ; Tests if an ojbect is moveable. Non moveable blocks are: ;
  962. ; Fixed blocks, moveable blocks that are locked, moveable blocks ;
  963. ; going up, discardable code going down. ;
  964. ; ;
  965. ; Arguments: ;
  966. ; ES:DI = arena header of object ;
  967. ; DS:DI = address of global heap information ;
  968. ; BX = ga_next or ga_prev ;
  969. ; ;
  970. ; Returns: ;
  971. ; ZF = 0 if object moveable ;
  972. ; ZF = 1 if object not moveable ;
  973. ; ;
  974. ; Error Returns: ;
  975. ; ;
  976. ; Registers Preserved: ;
  977. ; All ;
  978. ; ;
  979. ; Registers Destroyed: ;
  980. ; ;
  981. ; Calls: ;
  982. ; nothing ;
  983. ; ;
  984. ; History: ;
  985. ; ;
  986. ; Wed Oct 15, 1986 05:04:39p -by- David N. Weise [davidw] ;
  987. ; Moved he_count to ga_count. ;
  988. ; ;
  989. ; Thu Sep 25, 1986 05:42:17p -by- David N. Weise [davidw] ;
  990. ; Added this nifty comment block. ;
  991. ;-----------------------------------------------------------------------;
  992. cProc gmoveable,<PUBLIC,NEAR>
  993. cBegin nogen
  994. test es:[di].ga_handle,GA_FIXED ; If no handle then fixed
  995. jnz gmfixed
  996. cmp es:[di].ga_count,bh ; If locked then fixed
  997. jne gmfixed
  998. test [di].gi_cmpflags,BOOT_COMPACT ; If fb_init_EMS then all down
  999. jnz gmokay
  1000. test es:[di].ga_flags,GA_DISCCODE ; If discardable code
  1001. jz gmnotcode
  1002. cmp bl,ga_next ; Discardable code can only
  1003. ret ; move up in memory
  1004. gmnotcode:
  1005. cmp [di].gi_reserve,di ; If no reserved code area?
  1006. je gmokay ; Then anything can move up
  1007. cmp bl,ga_prev ; Otherwise can only move down
  1008. ret ; in memory
  1009. gmfixed:
  1010. or bh,bh ; Return with ZF = 1 if
  1011. ret ; not moveable
  1012. gmokay:
  1013. or bl,bl
  1014. ret
  1015. cEnd nogen
  1016. ;-----------------------------------------------------------------------;
  1017. ; gdiscard ;
  1018. ; ;
  1019. ; Subroutine to walk LRU chain, discarding objects until the #paras ;
  1020. ; discarded, plus the biggest free block is greater than the #paras ;
  1021. ; we are looking for. ;
  1022. ; ;
  1023. ; Arguments: ;
  1024. ; AX = size of largest free block so far ;
  1025. ; DX = minimum #paras needed ;
  1026. ; DS:DI = address of global heap information ;
  1027. ; ;
  1028. ; Returns: ;
  1029. ; ZF = 0 if one or more objects discarded. ;
  1030. ; ZF = 1 if no objects discarded. ;
  1031. ; ;
  1032. ; Error Returns: ;
  1033. ; ;
  1034. ; Registers Preserved: ;
  1035. ; AX,DX,DI ;
  1036. ; ;
  1037. ; Registers Destroyed: ;
  1038. ; BX,CX,ES ;
  1039. ; ;
  1040. ; Calls: ;
  1041. ; ;
  1042. ; History: ;
  1043. ; Mon Oct 27, 1986 09:34:45p -by- David N. Weise [davidw] ;
  1044. ; The glru list was reworked to link the arenas, not using the handle ;
  1045. ; table as a middle man. Because of this change glruprev was moved ;
  1046. ; inline and the code shortened up again. ;
  1047. ; ;
  1048. ; Wed Oct 15, 1986 05:04:39p -by- David N. Weise [davidw] ;
  1049. ; Moved he_count to ga_count. ;
  1050. ; ;
  1051. ; Thu Sep 25, 1986 05:45:31p -by- David N. Weise [davidw] ;
  1052. ; Shortened it up a bit and added this nifty comment block. ;
  1053. ;-----------------------------------------------------------------------;
  1054. cProc gdiscard,<PUBLIC,NEAR>
  1055. cBegin nogen
  1056. push ax
  1057. push dx
  1058. mov [di].hi_ncompact,0 ; Clear compaction flag
  1059. sub dx,ax ; How much to discard before
  1060. mov [di].hi_distotal,dx ; compacting again.
  1061. xor bx,bx ; BX = amount of DISCCODE below fence
  1062. test [di].gi_cmpflags,GA_DISCCODE
  1063. jnz fence_not_in_effect0
  1064. mov cx,[di].gi_lrucount
  1065. jcxz fence_not_in_effect0 ; All done if LRU chain empty
  1066. mov es,[di].gi_lruchain ; ES -> most recently used (ga_lruprev
  1067. push dx
  1068. gdloop0: ; is the least recently used)
  1069. mov es,es:[di].ga_lruprev ; Move to next block in LRU chain
  1070. test es:[di].ga_flags,GA_DISCCODE ; Discardable code?
  1071. jz gdloop0a ; No, ignore
  1072. mov ax,es
  1073. cCall get_physical_address,<ax>
  1074. cmp word ptr [di].gi_disfence_hi,dx ; Yes, is this code fenced off?
  1075. ja gdloop0b
  1076. jb gdloop0a ; No, ignore
  1077. cmp word ptr [di].gi_disfence_lo,ax ; Yes, is this code fenced off?
  1078. jbe gdloop0a ; No, ignore
  1079. gdloop0b:
  1080. add bx,es:[di].ga_size ; Yes, accumulate size of discardable
  1081. gdloop0a: ; code below the fence
  1082. loop gdloop0
  1083. pop dx
  1084. fence_not_in_effect0:
  1085. mov es,[di].gi_lruchain
  1086. cmp [di].gi_lrucount, 0
  1087. je gdexit
  1088. push es:[di].ga_lruprev
  1089. push [di].gi_lrucount
  1090. gdloop:
  1091. pop cx
  1092. pop ax
  1093. jcxz gdexit ; No more see if we discarded anything
  1094. mov es, ax ; ES on stack may be invalid if count 0
  1095. dec cx
  1096. push es:[di].ga_lruprev ; Save next handle from LRU chain
  1097. push cx
  1098. cmp es:[di].ga_count,0 ; Is this handle locked?
  1099. jne gdloop ; Yes, ignore it then
  1100. test [di].gi_cmpflags,GA_DISCCODE
  1101. jnz fence_not_in_effect
  1102. test es:[di].ga_flags,GA_DISCCODE
  1103. jz fence_not_in_effect
  1104. or bx,bx ; Discardable code below fence?
  1105. jz gdloop ; No, cant discard then
  1106. cmp bx,es:[di].ga_size ; Yes, more than size of this block?
  1107. jb gdloop ; No, cant discard then
  1108. sub bx,es:[di].ga_size ; Yes, reduce size of code below fence
  1109. fence_not_in_effect:
  1110. push bx
  1111. call DiscardCodeSegment
  1112. pop bx
  1113. jnz discarded_something
  1114. test [di].hi_ncompact,10h ; did a GlobalNotify proc free enough?
  1115. jz gdloop
  1116. jmps enough_discarded
  1117. discarded_something:
  1118. test [di].hi_ncompact,10h ; did a GlobalNotify proc free enough?
  1119. jnz enough_discarded
  1120. or [di].hi_ncompact,1 ; Remember we discarded something
  1121. sub [di].hi_distotal,ax ; Have we discarded enough yet?
  1122. ja gdloop ; No, look at next handle
  1123. enough_discarded:
  1124. pop cx ; Flush enumeration counter
  1125. pop cx ; and saved ES
  1126. gdexit:
  1127. cmp [di].hi_ncompact,0 ; Return with Z flag set or clear
  1128. pop dx
  1129. pop ax
  1130. ret
  1131. cEnd nogen
  1132. ;-----------------------------------------------------------------------;
  1133. ; DiscardCodeSegment ;
  1134. ; ;
  1135. ; Discards the given segment. Calls gnotify to fix stacks, entry ;
  1136. ; points, thunks, and prologs. Then glrudel removes it from the lru ;
  1137. ; list and gmarkfree finally gets rid of it. ;
  1138. ; ;
  1139. ; Arguments: ;
  1140. ; DS:DI => BurgerMaster ;
  1141. ; ES = Address of segment to discard ;
  1142. ; ;
  1143. ; Returns: ;
  1144. ; AX = size discarded ;
  1145. ; ZF = 0 ok ;
  1146. ; ;
  1147. ; Error Returns: ;
  1148. ; ;
  1149. ; Registers Preserved: ;
  1150. ; DI,SI,DS,ES ;
  1151. ; ;
  1152. ; Registers Destroyed: ;
  1153. ; BX,CX,DX ;
  1154. ; ;
  1155. ; Calls: ;
  1156. ; gnotify ;
  1157. ; glrudel ;
  1158. ; gmarkfree ;
  1159. ; ;
  1160. ; History: ;
  1161. ; ;
  1162. ; Fri Jun 12, 1987 -by- Bob Matthews [bobm] ;
  1163. ; Made FAR. ;
  1164. ; ;
  1165. ; Sun Apr 19, 1987 12:05:40p -by- David N. Weise [davidw] ;
  1166. ; Moved it here from InitTask, so that FirstTime could use it. ;
  1167. ;-----------------------------------------------------------------------;
  1168. cProc DiscardCodeSegment,<PUBLIC,NEAR>
  1169. cBegin nogen
  1170. push si
  1171. mov bx,es:[di].ga_handle ; BX = handle
  1172. mov al,GN_DISCARD ; AX = GN_DISCARD
  1173. push es
  1174. call gnotify
  1175. pop es
  1176. jz cant_discard ; Skip this handle if not discardable
  1177. call glrudel ; Delete handle from LRU chain
  1178. push es:[di].ga_owner ; Save owner field
  1179. mov ax,es:[di].ga_size ; Save size
  1180. xor si,si
  1181. call gmarkfree ; Free the block associated with this handle
  1182. mov bx,si
  1183. pop cx ; Owner
  1184. cCall mark_sel_NP,<bx,cx>
  1185. cant_discard:
  1186. pop si
  1187. ret
  1188. cEnd nogen
  1189. ;-----------------------------------------------------------------------
  1190. ; ShrinkHeap
  1191. ;
  1192. ; This routine finds free partitions and returns them to DPMI
  1193. ;
  1194. ; Input:
  1195. ; ds:di=>BurgerMaster
  1196. ;
  1197. ; Output:
  1198. ; none
  1199. ;
  1200. public ShrinkHeap
  1201. ShrinkHeap proc near
  1202. push si
  1203. push cx
  1204. push bx
  1205. push ax
  1206. push ds
  1207. push es
  1208. push bp
  1209. mov bp,sp
  1210. sub sp,8
  1211. SelFirst equ word ptr [bp - 2]
  1212. SelLast equ word ptr [bp - 4]
  1213. SelFree equ word ptr [bp - 6]
  1214. SelBurgerMaster equ word ptr [bp - 8]
  1215. mov SelBurgerMaster,ds
  1216. SetKernelDS
  1217. ;
  1218. ; Iterate over each of the DPMI blocks
  1219. ; there should be fewer dpmi blocks than blocks on the free list
  1220. ;
  1221. mov cx,DpmiBlockCount
  1222. mov si,offset DpmiMemory
  1223. ;
  1224. ; If this block doesn't exist, go on to the next one
  1225. ;
  1226. sh30: mov ax,[si].DBSel
  1227. cmp ax,di
  1228. je sh60
  1229. ;
  1230. ; Found an in use dpmi block
  1231. ;
  1232. dec cx
  1233. ;
  1234. ; Check to see if the next block is free
  1235. ;
  1236. mov SelFirst,ax
  1237. mov es,ax
  1238. mov bx,es:[di].ga_next
  1239. mov es,bx
  1240. mov SelFree,bx
  1241. cmp es:[di].ga_owner,di
  1242. jne sh60
  1243. ;
  1244. ; Check to see if it is followed by a not there block
  1245. ; (Note: we should check for -1 in the future so we can
  1246. ; free the last block as well)
  1247. ;
  1248. mov bx,es:[di].ga_next
  1249. mov SelLast,bx
  1250. mov es,bx
  1251. cmp es:[di].ga_owner,GA_NOT_THERE
  1252. jne sh60
  1253. ;
  1254. ; Found one, so remove the free block
  1255. ;
  1256. mov es,SelFree
  1257. mov ds,SelBurgerMaster
  1258. UnsetKernelDS
  1259. call gdel_free
  1260. ;
  1261. ; Fix up the global information
  1262. ;
  1263. sub [di].hi_count,3
  1264. ;
  1265. ; Unlink from the heap
  1266. ;
  1267. mov es,SelFirst
  1268. mov es,es:[di].ga_prev
  1269. mov ds,SelLast
  1270. mov ds,ds:[di].ga_next
  1271. mov ds:[di].ga_prev,es
  1272. mov es:[di].ga_next,ds
  1273. ;
  1274. ; Free all of the selectors
  1275. ;
  1276. cCall free_sel,<SelFirst>
  1277. cCall free_sel,<SelLast>
  1278. cCall free_sel,<SelFree>
  1279. ;
  1280. ; Give the block back to Dpmi
  1281. ;
  1282. push si
  1283. push di
  1284. SetKernelDS
  1285. mov di,[si].DBHandleLow
  1286. mov si,[si].DBHandleHigh
  1287. DPMICALL 502h
  1288. pop di
  1289. pop si
  1290. ;
  1291. ; Decrease the number of dpmi blocks
  1292. ;
  1293. dec DpmiBlockCount
  1294. ;
  1295. ; Forget the block
  1296. ;
  1297. mov [si].DBSel,di
  1298. ;
  1299. ; move on to the next iteration
  1300. ;
  1301. sh60: add si,size DpmiBlock
  1302. or cx,cx
  1303. jz sh70
  1304. jmp sh30
  1305. sh70: mov sp,bp
  1306. pop bp
  1307. pop es
  1308. pop ds
  1309. pop ax
  1310. pop bx
  1311. pop cx
  1312. pop si
  1313. ret
  1314. ShrinkHeap endp
  1315. sEnd CODE
  1316. end