home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c082_122 / 1.ddi / WINLBSRC.ZIP / ASMMEM.ASM < prev    next >
Encoding:
Assembly Source File  |  1992-06-10  |  23.4 KB  |  923 lines

  1. ;[]-----------------------------------------------------------------[]
  2. ;|      ASMMEM.ASM -- Windows memory suballocator                    |
  3. ;[]-----------------------------------------------------------------[]
  4.  
  5. ;
  6. ;       C/C++ Run Time Library - Version 5.0
  7. ;       Copyright (c) 1991, 1992 by Borland International
  8. ;       All Rights Reserved.
  9.  
  10.     INCLUDE RULES.ASI
  11.  
  12.     locals
  13.  
  14.     extrn   __GET_TEMP_BLOCK        :far
  15.     extrn   __RLS_TEMP_BLOCK        :far
  16.     extrn   __SIZ_TEMP_BLOCK        :far
  17.     extrn   ___GetSubAllocClientData:far
  18.     extrn   __WinAllocFlag          :word
  19.  
  20. Code_Seg@
  21.  
  22. ;=============================================================================
  23. comment @
  24.  
  25.     Memory is allocated from the heap in large chunks ("pages"), which
  26.     are then sub-allocated. All pages are on a doubly linked list that
  27.     starts at the "page_first" member of the application_block structure.
  28.  
  29.     Pages are normally allocated to be "DEFAULT_PAGE_SIZE" bytes in size,
  30.     but if a block larger than "DEFAULT_PAGE_SIZE - overhead" needs to be
  31.     allocated, a large enough page will be allocated.
  32.  
  33.     Every page contains a header at its bottom which contains information
  34.     about its total size, total number of free bytes, and the head of the
  35.     linked list of free blocks. Following the header are all the allocated
  36.     and free blocks, always tightly packed (no gaps) and all starting with
  37.     a block header. The last thing in each page (used as a "sentinel") is
  38.     a minimum-size allocated block.
  39.  
  40.     A block header consists of the block's size (which is always
  41.     a multiple of 4 and includes the block header); free blocks
  42.     have the low bit set in the size word, so that they can be
  43.     distinguished from user blocks.
  44.  
  45.     When all blocks in a page are freed, that page will be deallocated if
  46.     possible, with the exception of the first one; the current number of
  47.     allocated pages is kept in the "page_count" member of the
  48.     application_block structure.
  49.  
  50.     For free blocks, the block size is followed by the offset of the next
  51.     free block in the page. For allocated blocks, the page offset follows.
  52.  
  53.     The following routines are provided:
  54.  
  55.         __INITMEM:  initializes the allocator variables
  56.  
  57.         __GETMEM:   allocates DX:AX bytes, result returned
  58.                     in ES:DI
  59.  
  60.         __RLSMEM:   frees the memory block at ES:DI
  61.  
  62.         __STOPMEM:  shuts down the allocator, releasing all memory
  63.  
  64. @
  65. ;=============================================================================
  66.  
  67. DEFAULT_PAGE_SIZE   equ 4096
  68. RLS_UNUSED_MEM      equ 1
  69.  
  70. ;=============================================================================
  71.  
  72. page_header STRUC
  73.  
  74. page_next       dw  ?,?         ; next page
  75. page_prev       dw  ?,?         ; prev page
  76.  
  77. page_size       dw  ?           ; size of page in bytes
  78. page_free_list  dw  ?           ; head of free list
  79. page_free_count dw  ?           ; size of free area(s) in bytes
  80.  
  81. page_header ENDS
  82.  
  83. ;-----------------------------------------------------------------------------
  84.  
  85. block_header    STRUC
  86.  
  87. block_size  dw  ?       ; first allocated page
  88. block_next  dw  ?       ; next free block
  89.  
  90. block_header    ENDS
  91.  
  92. block_alignment equ 4       ; multiple of block_header size!
  93.  
  94.  
  95. ;-----------------------------------------------------------------------------
  96.  
  97. TSubAllocClientData struc
  98.   page_first    dd  ?   ;address of first page
  99.   page_count    dw  ?   ;number of allocated pages
  100. TSubAllocClientData ends
  101.  
  102. ;-----------------------------------------------------------------------------
  103.  
  104. page_overhead   EQU (size page_header + 2)
  105.  
  106. ;=============================================================================
  107.  
  108. InitApplicationBlock    proc
  109.  
  110.     ;; Returns with ES:DI pointing to the sub allocation client
  111.     ;; data for this application
  112.  
  113.     push    dx
  114.     push    ax
  115.     call    ___GetSubAllocClientData
  116.     mov es, dx
  117.     mov di, ax
  118.     pop ax
  119.     pop dx
  120.     ret
  121.  
  122. InitApplicationBlock    endp
  123.  
  124.     public  __INITMEM
  125.  
  126. __INITMEM   proc    far
  127.  
  128.     push    es
  129.     push    di
  130.  
  131.     call    InitApplicationBlock
  132.     xor ax, ax
  133.     mov word ptr es:[di].page_first, ax ; no pages allocated
  134.     mov word ptr es:[di].page_first+2, ax
  135.     mov es:[di].page_count, ax
  136.  
  137.     pop di
  138.     pop es
  139.     ret
  140.  
  141. __INITMEM endp
  142.  
  143. ;=============================================================================
  144.  
  145.     public  __STOPMEM
  146.  
  147. __STOPMEM   proc    far
  148.  
  149.     local   ApplicationBlockPtr:dword=STACKFRAMESIZE
  150.  
  151.     inc     bp
  152.     push    bp
  153.     mov bp, sp
  154.     sub sp, STACKFRAMESIZE
  155.  
  156.     push    si
  157.     push    di
  158.  
  159.     push    es
  160.     push    di
  161.  
  162.     call    InitApplicationBlock
  163.     mov word ptr [ApplicationBlockPtr+2], es
  164.     mov word ptr [ApplicationBlockPtr], di
  165.  
  166. ifdef   RLS_UNUSED_MEM
  167.  
  168.     les di, es:[di].page_first
  169.     jmp SHORT stopmem_next
  170. ;
  171. ; Here ES:DI points to the current page.
  172. ;
  173. stopmem_loop:
  174.     push    es:[di].page_next   ; save "next" value
  175.     push    es:[di].page_next+2
  176. ;
  177. ; Now free the page at ES:DI.
  178. ;
  179.  
  180.     call    __RLS_TEMP_BLOCK  ; free up the page
  181.     pop es      ; restore the saved "next" value
  182.     pop di
  183. ;
  184. ; Here ES:DI points to the next page to be freed.
  185. ;
  186. stopmem_next:
  187.     mov ax, es          ; any more pages?
  188.     or  ax, di
  189.     jnz stopmem_loop
  190.  
  191.     les di, ApplicationBlockPtr
  192.     mov word ptr es:[di].page_first, ax
  193.     mov word ptr es:[di].page_first+2, ax
  194.     mov es:[di].page_count, ax
  195.  
  196.     pop di
  197.     pop si
  198. endif
  199.  
  200.     mov sp, bp
  201.     pop bp
  202.         dec     bp
  203.  
  204.     ret
  205.  
  206. __STOPMEM     endp
  207.  
  208. ;=============================================================================
  209. ifdef   DEBUG
  210. ;=============================================================================
  211.  
  212. check_page  proc    near
  213.  
  214.         mov bx, es:[di].page_free_list
  215.         and bx, bx
  216.         jz  check_done
  217.  
  218.         mov dx, es:[di].page_size
  219.         add dx, di
  220. check_loop:
  221.         mov si, es:[bx].block_size  ; SI = free block size
  222.  
  223.         test    si, 1           ; LSB must be set
  224.         jz  bad_size
  225.  
  226.         dec si          ; get rid of "free" bit
  227.  
  228.         mov ax, bx
  229.         add ax, si          ; AX = addr of next block
  230.  
  231.         cmp ax, dx
  232.         ja  bad_size
  233.  
  234.         mov cx, es:[bx].block_next
  235.         jcxz    check_done
  236.  
  237.         cmp ax, cx
  238.         ja  bad_size
  239.  
  240.         mov bx, cx
  241.         jmp check_loop
  242.  
  243. check_done: ret
  244.  
  245. bad_size:   int 3
  246.         jmp bad_size
  247.  
  248. check_page  endp
  249.  
  250. ;------------------------------------------------------------------------------
  251.  
  252.     public  __CHECK_PAGES
  253.  
  254. __CHECK_PAGES proc    far
  255.  
  256.     local   ApplicationBlockPtr:dword=STACKFRAMESIZE
  257.  
  258.     pushf
  259.     push    ax
  260.     push    bx
  261.     push    cx
  262.     push    dx
  263.     push    si
  264.     push    di
  265.  
  266.     inc     bp
  267.         push    bp
  268.     mov bp, sp
  269.     sub sp, STACKFRAMESIZE
  270.  
  271.     call    InitApplicationBlock
  272.     mov word ptr [ApplicationBlockPtr+2], es
  273.     mov word ptr [ApplicationBlockPtr], di
  274.  
  275.     mov ax, es
  276.     or  ax, di
  277.     jz  @@Exit
  278.  
  279.     les di, es:[di].page_first
  280.     jmp short check_entry1
  281.  
  282. check_loop1:
  283.     call    check_page
  284.     les di, dword ptr es:[di].page_next
  285. check_entry1:
  286.     mov dx, es
  287.     or  dx, di
  288.     jnz check_loop1
  289.  
  290. @@Exit:
  291.     mov sp, bp
  292.     pop bp
  293.         dec     bp
  294.  
  295.     pop di
  296.     pop si
  297.     pop dx
  298.     pop cx
  299.     pop bx
  300.     pop ax
  301.     popf
  302.  
  303.     ret
  304.  
  305. __CHECK_PAGES endp
  306.  
  307. ;=============================================================================
  308. endif
  309. ;=============================================================================
  310.  
  311.     public  __GETMEM
  312.  
  313. __GETMEM    proc    far
  314.  
  315. ;
  316. ; Function __GETMEM: allocate a memory block
  317. ;
  318. ; Inputs:
  319. ;   DX:AX   =   needed block size in bytes
  320. ; Outputs:
  321. ;       ES:DI   =   allocated block address
  322. ;
  323.  
  324.     local   getmem_size :word, \
  325.         getmem_last :word, \
  326.         ApplicationBlockPtr :dword, \
  327.         applicationSize :dword = STACKFRAMESIZE
  328.  
  329.     inc     bp
  330.     push    bp
  331.     mov bp, sp
  332.     sub sp, STACKFRAMESIZE
  333.  
  334. ; First check to see if the block is bigger than we can handle with
  335. ; the suballocator.  If it is, then we must defer to the underlying
  336. ; memory manager (which will return NULL if it can't process the
  337. ; request).  We make the assumption, in this implementation, that if
  338. ; a block is bigger than we can handle (that is, bigger than 64K
  339. ; minus our block_header size), that the offset portion of the
  340. ; address returned from the underlying memory manager will be 0.
  341. ; Note that this is currently a safe assumption running under MS
  342. ; Windows version 3.x.  It probably won't be safe in the future, but
  343. ; then, this implementation will have to change anyway.
  344.  
  345.     ;; save application-specified size because we'll need the unmodified
  346.     ;; size if we defer to the underlying memory manager.
  347.     mov word ptr [applicationSize+2], dx
  348.     mov word ptr [applicationSize], ax
  349.  
  350.     ;; Don't suballocate blocks if the attributes are different from
  351.     ;; default or GMEM_ZEROINIT.
  352.     mov cx, __WinAllocFlag
  353.     and cx, NOT 40h             ;GMEM_ZEROINIT
  354.     jnz @@GreaterThan64KJmp     ; yes, don't suballocate
  355.  
  356.     ;; add our block overhead (rounding up the size to a multiple
  357.     ;; of block_alignment and space for the block header.
  358.     add ax, (block_alignment-1) + size block_header
  359.     adc dx, 0
  360.     and al, not (block_alignment-1)
  361.     ;; check for greater than 64K
  362.     cmp dx, 0                   ;greater than 64K?
  363.     jnz @@GreaterThan64KJmp     ; yes
  364.     ;; resulting size is less than 64K
  365.     mov [getmem_size], ax       ;we only handle 16-bit sizes internally
  366.  
  367.     call    InitApplicationBlock
  368.     mov word ptr [ApplicationBlockPtr+2], es
  369.     mov word ptr [ApplicationBlockPtr], di
  370.  
  371.     ;; Check to see if our instance data is initialized yet.  If
  372.     ;; not, then use the underlying memory manager.
  373.     mov ax, es
  374.     or  ax, di
  375.     jz  @@GreaterThan64KJmp ;not yet initialized
  376.  
  377. ifdef   DEBUG
  378.     call    __CHECK_PAGES
  379. endif
  380.  
  381.     mov ax, [getmem_size]       ;get block size in AX
  382. ;
  383. ; Now look through all the allocated pages for a big enough free block.
  384. ;
  385. getmem_findfree:
  386.     les di, ApplicationBlockPtr
  387.     les di, es:[di].page_first
  388.     jmp SHORT getfree_test
  389.  
  390. @@GreaterThan64KJmp:
  391.     jmp @@GreaterThan64K
  392.  
  393. ;
  394. ; Here if an allocation has been succesfully made; return to the caller.
  395. ;
  396. getfree_done:
  397.  
  398. ifdef   DEBUG
  399.     push    es
  400.     push    di
  401.     call    __CHECK_PAGES
  402.     pop di
  403.     pop es
  404. endif
  405.  
  406.     jmp @@RealExit
  407. ;
  408. ; ES:DI points to the current page; first see if the total number of free
  409. ; bytes in this page gives us a chance.
  410. ;
  411. getfree_loop:
  412.         cmp es:[di].page_free_count, ax
  413.         jb  getfree_next
  414. ;
  415. ; There is hope; see if we can allocate the block.
  416. ;
  417.         call    near ptr find_free_block; try to get enough space ....
  418.         jc  getfree_done
  419. ;
  420. ; This page was no good, try the next one.
  421. ;
  422. getfree_next:
  423.         les di, dword ptr es:[di].page_next
  424. getfree_test:
  425.         mov dx, es
  426.         or  dx, di
  427.         jnz getfree_loop
  428. ;
  429. ; No luck with free blocks; we'll need to allocate a new page.
  430. ;
  431.         mov ax, [getmem_size]
  432.         cmp ax, DEFAULT_PAGE_SIZE - page_overhead
  433.         ja  bigger_page
  434.         mov ax, DEFAULT_PAGE_SIZE
  435. ;
  436. ; Here to allocate a new page; AX has its size.
  437. ;
  438. getmem_allocpage:
  439.         push    ax          ; save page size
  440. getmem_getpage:
  441.         xor dx, dx          ;high half of zie
  442.         call    __GET_TEMP_BLOCK
  443.         mov es, dx
  444.         mov di, ax
  445. ;
  446. ; Here ES:DI is the newly allocated page.
  447. ;
  448.         pop bx          ; restore page size
  449.         mov ax, es
  450.         or  ax, di
  451.         jz  getmem_error
  452. ;
  453. ; Insert this page at the beginning of the page list.
  454. ;
  455.         push    ds
  456.         lds si, ApplicationBlockPtr
  457.         lds si, ds:[si].page_first
  458.         mov es:[di].page_next  , si
  459.         mov es:[di].page_next+2, ds
  460.         mov ax, ds
  461.         or  ax, si
  462.         jz  getmem_first        ; skip if very first page
  463.         mov page_prev  [si], di
  464.         mov page_prev+2[si], es
  465. getmem_first:
  466.         lds si, ApplicationBlockPtr
  467.         inc ds:[si].page_count
  468. ;
  469. ; The new page is the new first page.
  470. ;
  471.         mov word ptr ds:[si].page_first, di
  472.         mov word ptr ds:[si].page_first+2, es
  473.  
  474.         pop ds
  475.  
  476.         xor ax, ax
  477.  
  478.         mov es:[di].page_prev  , ax
  479.         mov es:[di].page_prev+2, ax
  480. ;
  481. ; Create a fake "0-length" block at the end of the page.
  482. ;
  483.         mov es:[di+bx-2], ax
  484. ;
  485. ; Now initialize the rest of the page header; don't forget to count the
  486. ; fake minimum-size block at the very end of the page.
  487. ;
  488.         mov es:[di].page_size, bx   ; save page size
  489.         sub bx, page_overhead
  490.         mov es:[di].page_free_count, bx
  491.         lea si, [di+size page_header]
  492.         mov es:[di].page_free_list , si
  493. ;
  494. ; Create the free block immediately following the page header.
  495. ;
  496.         inc bx          ; set "free" bit
  497.         mov es:[si].block_size, bx
  498.         mov es:[si].block_next, ax
  499. ;
  500. ; Now the page should be all set for the allocation, so that we can simply
  501. ; jump to the beginning of this routine.
  502. ;
  503.         mov ax, [getmem_size]   ; size expected in AX
  504.         jmp getmem_findfree
  505. ;
  506. ; Here if a bigger-than-default page is needed; AX = application block size.
  507. ;
  508. bigger_page:
  509.         xor dx, dx          ;prepare for overflow check
  510.         add ax, page_overhead + 255 ;add in overhead
  511.         adc dx, 0           ;add in carry
  512.         cmp dx, 0           ;overflow?
  513.         jnz @@GreaterThan64K    ; yes
  514.         xor al, al
  515.         jmp getmem_allocpage
  516.  
  517. getmem_error:
  518.     jmp short @@Exit
  519.  
  520. @@GreaterThan64K:
  521.     ;; The requested block size was greater than 64K (or would
  522.     ;; have exceeded 64K if we tacked on our block header).  Call
  523.     ;; the underlying memory manager to allocate the block.
  524.     ;; The unmodified size is in the applicationSize local variable.
  525.  
  526.     mov dx, word ptr [applicationSize+2]
  527.     mov ax, word ptr [applicationSize]
  528.     call    __GET_TEMP_BLOCK      ;dx:ax is block
  529.     ;; check our assumption that the offset is always 0.  This is
  530.     ;; required by this implementation because we distinguish
  531.     ;; between foreign blocks and our own managed blocks by the
  532.     ;; zero offset.
  533.     or  ax, ax              ;set flags
  534.     jnz @@NonZeroOffset     ;error, non-zero offset
  535. @@Exit:
  536.     mov es, dx
  537.     mov di, ax
  538.  
  539. @@RealExit:
  540.     mov sp, bp
  541.     pop bp
  542.         dec     bp
  543.     ret
  544.  
  545. @@NonZeroOffset:
  546.     ;; The assumption that the underlying memory manager always
  547.     ;; returns blocks with zero offsets was invalid.  Free the
  548.     ;; allocated block and act like the allocation failed.
  549.  
  550.     call    __RLS_TEMP_BLOCK      ;free memory just allocated
  551.  
  552.     ;; return with NULL pointer
  553.     xor dx, dx
  554.     xor ax, ax
  555.     jmp short @@Exit        ;and return with NULL pointer
  556.  
  557. __GETMEM     endp
  558.  
  559. ;------------------------------------------------------------------------------
  560.  
  561. find_free_block proc    near
  562.  
  563. ;
  564. ;   This function looks for a big enough free block in a given page;
  565. ;   it assumes that the desired number of bytes is smaller than the
  566. ;   total number of free bytes in the page.
  567. ;
  568. ; Inputs:
  569. ;     ES:DI =   page header address
  570. ;   AX  =   desired block size
  571. ;
  572. ; Outputs:
  573. ;   CF  =   if successful, will be set
  574. ;     ES:DI =   if successful, will contain the address of the block
  575. ;
  576.         mov bx, es:[di].page_free_list
  577.         lea dx,    [di].page_free_list
  578. ;
  579. ; In this main loop, ES:DI always points to the page header; ES:BX to the
  580. ; current (free) block, and ES:DX to the "next" link of the previous free
  581. ; block (in case we need to remove the current block from the singly-linked
  582. ; list of free blocks).
  583. ;
  584. ; Throughout this function, AX is the size we're trying to allocate.
  585. ;
  586. ffree_loop:
  587.         mov si, es:[bx].block_size  ; SI = free block size
  588.         dec si          ; get rid of "free" bit
  589. ;
  590. ; See if the current free block is big enough.
  591. ;
  592.         cmp ax, si          ; block big enough ?
  593.         jbe ffree_found
  594. ;
  595. ; See if the free block is followed by another free block.
  596. ;
  597.         mov cx, es:[bx+si].block_size
  598.         test    cl, 1
  599.         jz  ffree_notfree       ; skip if not
  600. ;
  601. ; Merge the two (consecutive) free blocks into one, and try again.
  602. ;
  603.         dec cx          ; get rid of "free" bit
  604.         add es:[bx].block_size, cx  ; new total size
  605.         mov cx, es:[bx+si].block_next
  606.         mov es:[bx].block_next, cx  ; new "next" link value
  607.         jmp ffree_loop
  608. ;
  609. ; The adjacent block is not free: follow the linked list of free blocks.
  610. ;
  611. ffree_notfree:
  612.         lea dx,    [bx].block_next  ; update "last link" address
  613.         mov bx, es:[bx].block_next  ; point to next free block
  614.         or  bx, bx
  615.         jnz ffree_loop      ; continue if more free blocks
  616. ;
  617. ; No more free blocks --> return with CF cleared (failure).
  618. ;
  619.         ret
  620. ;
  621. ; We've found a free block; see whether it's larger than we need.
  622. ;
  623. ffree_found:
  624.         sub es:[di].page_free_count, ax
  625.  
  626.         mov cx, es:[bx].block_size
  627.         dec cx
  628.         sub cx, ax          ; CX = excess in bytes
  629.         jz  ffree_remove
  630. ;
  631. ; Free block is larger than we need, shrink it somewhat and return the upper
  632. ; part of it as the allocated block.
  633. ;
  634.         mov dx, di
  635.         mov di, bx          ; point DI to upper part
  636.         add di, cx
  637.         mov es:[di].block_size, ax  ; set allocated block's size
  638.         mov es:[di].block_next, dx  ; set page offset
  639.         inc cx
  640.         mov es:[bx].block_size, cx  ; new free block size
  641.         add di, size block_header
  642. ;
  643. ; Return success - set CF.
  644. ;
  645.         stc
  646.         ret
  647. ;
  648. ; The free block is exactly the right size; simply turn it into an allocated
  649. ; block.
  650. ;
  651. ffree_remove:
  652.         dec es:[bx].block_size  ; get rid of "free" bit
  653. ;
  654. ; To remove the block from the linked list, we need to update its
  655. ; predecessor's "next" link to the address of the next free block.
  656. ;
  657.         mov cx, es:[bx].block_next  ; get next free block address
  658.         mov es:[bx].block_next, di  ; set page offset field
  659.         mov di, dx
  660.         mov es:[di], cx     ; update previous block's link
  661. ;
  662. ; Finally, point ES:DI past the block header and return.
  663. ;
  664.         lea di, [bx+size block_header]
  665.  
  666.         stc
  667.         ret
  668.  
  669. find_free_block endp
  670.  
  671. ;==============================================================================
  672.  
  673.     public  __SIZMEM
  674.  
  675. __SIZMEM    proc    far
  676.  
  677. ; Function __SIZMEM: return the size of the specified block.
  678. ;
  679. ; Inputs:
  680. ;   ES:DI   =   block address
  681. ; Outputs:
  682. ;   DX:AX   =   size of specified block
  683.  
  684.  
  685. ifdef   DEBUG
  686.         push    es
  687.         push    di
  688.         call    __CHECK_PAGES
  689.         pop di
  690.         pop es
  691. endif
  692.  
  693.     ;; Check to see if the requested block is one of ours
  694.     ;; we do this by checking the offset for 0.  If it is
  695.     ;; non-zero, it is one of ours and we can look up its
  696.     ;; size.  If it is not one of ours, we must ask the
  697.     ;; underlying memory manager what the size is.
  698.  
  699.     or  di, di      ;check block address offset
  700.     jz  @@NotOurBlock   ; not one of ours
  701.  
  702.     sub di, size block_header   ; point to block's header
  703.  
  704.     xor dx, dx          ; ours are all less than 64K
  705.     mov ax, es:[di].block_size  ; get block size
  706.     sub ax, size block_header
  707.     ret
  708.  
  709. @@NotOurBlock:
  710.     ;; The block is owned by the underlying memory manager.  Ask
  711.     ;; it what its size is.
  712.  
  713.     call    __SIZ_TEMP_BLOCK      ;dx:ax is block size
  714.     ret
  715.  
  716. __SIZMEM     endp
  717.  
  718.  
  719.     public  __RLSMEM
  720.  
  721. __RLSMEM    proc    far
  722.  
  723. ;
  724. ; Function __RLSMEM: free a memory block.
  725. ;
  726. ; Inputs:
  727. ;      ES:DI    =   block address
  728. ; Outputs:
  729. ;   none
  730. ;
  731.  
  732.     local   ApplicationBlockPtr :dword = STACKFRAMESIZE
  733.  
  734.         inc     bp
  735.     push    bp
  736.     mov bp, sp
  737.     sub sp, STACKFRAMESIZE
  738.  
  739. ifdef   DEBUG
  740.     push    es
  741.     push    di
  742.     call    __CHECK_PAGES
  743.     pop di
  744.     pop es
  745. endif
  746.  
  747.     ;; Check to see if the requested block is one of ours
  748.     ;; we do this by checking the offset for 0.  If it is
  749.     ;; non-zero, it is one of ours and we can look up its
  750.     ;; size.  If it is not one of ours, we must ask the
  751.     ;; underlying memory manager what the size is.
  752.  
  753.     or  di, di          ;check block address offset
  754.     jz  @@NotOurBlockJmp    ; not one of ours
  755.  
  756.     push    es
  757.     push    di
  758.     call    InitApplicationBlock
  759.     mov word ptr [ApplicationBlockPtr+2], es
  760.     mov word ptr [ApplicationBlockPtr], di
  761.     pop di
  762.     pop es
  763.  
  764.     sub di, size block_header   ; point to block's header
  765.  
  766.     mov bx, es:[di].block_next  ; get the page address
  767. ;
  768. ; Here ES:BX points to the page header, ES:DI to the block header.
  769. ;
  770.     mov ax, es:[di].block_size  ; update number of free bytes
  771.     add es:[bx].page_free_count, ax
  772.  
  773. ifdef   RLS_UNUSED_MEM
  774.  
  775. ;
  776. ; See if the page is now completely free.
  777. ;
  778.     mov ax, es:[bx].page_size
  779.     sub ax, page_overhead
  780.     cmp ax, es:[bx].page_free_count
  781.     je  rlsmem_freepage
  782. rlsmem_freeit:
  783.  
  784. endif
  785.  
  786. ;
  787. ; Change the block into a free block.
  788. ;
  789.     inc es:[di].block_size  ; set the "free" bit
  790. ;
  791. ; Now find the last free block that's in front of this block.
  792. ;
  793.     lea dx,    [bx].page_free_list
  794.     mov si, es:[bx].page_free_list
  795.     jmp short rlsmem_next
  796.  
  797. @@NotOurBlockJmp:
  798.     jmp @@NotOurBlock
  799.  
  800. rlsmem_loop:
  801.     cmp si, di          ; this free block above new one?
  802.     ja  rlsmem_insert
  803.     lea dx,    [si].block_next
  804.     mov si, es:[si].block_next
  805. ;
  806. ; See if there are any more free blocks.
  807. ;
  808. rlsmem_next:
  809.     or  si, si          ; more free blocks ?
  810.     jnz rlsmem_loop
  811. ;
  812. ; Here the new free block needs to be inserted after the "next" link at DX,
  813. ; the next free block's address is in SI.
  814. ;
  815. rlsmem_insert:
  816.     mov es:[di].block_next, si  ; set "next" for new block
  817.     mov si, dx
  818.     mov es:[si], di     ; set predecessor's "next" value
  819.  
  820. ifdef   DEBUG
  821.     call    __CHECK_PAGES
  822. endif
  823.  
  824.     jmp short @@ExitRlsMem
  825.  
  826. ifdef   RLS_UNUSED_MEM
  827.  
  828. ;
  829. ; Here if the page is now completely free.
  830. ;
  831. rlsmem_freepage:
  832.  
  833.     push    es
  834.     push    di
  835.     les di, ApplicationBlockPtr
  836.     cmp es:[di].page_count, 1
  837.     pop di
  838.     pop es
  839.     jbe rlsmem_freeit
  840. ;
  841. ; First remove this page from the linked list.
  842. ;
  843.     push    ds
  844.     lds si, dword ptr es:[bx].page_next
  845.     mov ax, ds          ; DS:SI = next page addr
  846.     mov di, es:[bx].page_prev
  847.     mov dx, es:[bx].page_prev+2 ; DX:DI = prev page addr
  848.     or  ax, si
  849.     jz  rlsmem_last
  850.     mov ds:[si].page_prev  , di
  851.     mov ds:[si].page_prev+2, dx
  852. rlsmem_last:
  853.     mov cx, ds
  854.     mov ds, dx
  855.     or  dx, di
  856.     jz  rlsmem_first
  857.     mov ds:[di].page_next  , si
  858.     mov ds:[di].page_next+2, cx
  859.     pop ds
  860. rlsmem_notfirst:
  861. ;
  862. ; Now free the page memory.
  863. ;
  864.     mov di, bx          ; ES:DI = address of page
  865.     call    __RLS_TEMP_BLOCK
  866. ;
  867. ; We now have fewer pages.
  868. ;
  869.     push    es
  870.     push    di
  871.     les di, ApplicationBlockPtr
  872.  
  873.     dec es:[di].page_count
  874.  
  875.     pop di
  876.     pop es
  877.  
  878.     jmp short @@ExitRlsMem
  879. ;
  880. ; Here if removing the first page.
  881. ;
  882. rlsmem_first:
  883.  
  884.     pop ds
  885.  
  886.     push    es
  887.     push    di
  888.     les di, ApplicationBlockPtr
  889.  
  890.     mov word ptr es:[di].page_first, si
  891.     mov word ptr es:[di].page_first+2, cx
  892.  
  893.     pop di
  894.     pop es
  895.  
  896.     pop si
  897.  
  898.     jmp rlsmem_notfirst
  899. endif
  900.  
  901.     jmp short @@ExitRlsMem
  902.  
  903. @@NotOurBlock:
  904.     ;; The specified block is not managed by us.  Ask the
  905.     ;; underlying memory manager to free the block.
  906.  
  907.     call    __RLS_TEMP_BLOCK
  908.  
  909. @@ExitRlsMem:
  910.  
  911.     mov sp, bp
  912.     pop bp
  913.     dec bp
  914.     ret
  915.  
  916. __RLSMEM     endp
  917.  
  918. ;==============================================================================
  919. Code_EndS@
  920.         END
  921.