home *** CD-ROM | disk | FTP | other *** search
- page 75,132
- %title "Object Oriented Memory Management System - TASM 3.0"
- ; Copyright (c) 1991 by Borland International
-
- ifndef MDL
- display "Error: This module requires that you provide a memory model"
- display " definition on the command line. I.E. /dMDL=SMALL."
- display "Also, /M must be given on the command line to enable multiple"
- display " passes."
- MDL equ <SMALL>
- endif
-
- ModuleVersion EQU "0.33"
-
- COMMENT ~
-
- This memory management system shows how to use TASM 3.0 objected oriented
- extensions to create a heap management system. Some features of this heap
- management system are:
- - Extensive use of objects for the heap manager and also individual
- blocks of memory
- - Extensibility to allow changing the structure of heap blocks without
- drastic changes to the heap manager
- - Ability to mix many different types of blocks in the same heap,
- for example, disk heap objects interspersed with regular memory
- blocks.
- - Extensive optimizations
-
- The memory management system is implemented by several object classes.
- All memory accesses are based on requesting a block of memory whose size
- is given in paragraphs. Users of the memory system only need to store
- segment offsets of the memory blocks that are returned. All memory blocks
- can return the offset, where the user may begin storing information,
- whenever needed at a later time.
-
- memory_system is the master heap object. It is responsible for allocating
- a block of memory to be managed as a heap. This block of
- memory may be smaller than the maximum amount of available
- memory, enabling the user of this system to setup multiple
- heaps if desired.
-
- memory_block is the object that has the methods for the operations of
- individual blocks in the heap. It may be used for blocks
- that are used, or unused. Although extensive checks
- of IsFree are made in routines that are not allowed on used
- blocks of memory, (for example splitting a block apart to
- create a smaller block to satisfy an allocation request),
- there are more specialized blocks objects available to
- handle used blocks and the endblock at the end of the heap.
- Consequently, the _DO_ISFREE_ equate may be disabled, and
- routines within memory_block will rely on the method
- overrides in the memory_usedblock class to filter out calls
- that should not happen to memory_blocks. This alone results
- in a 20% speed increase of the memory manager in the
- set of testing benchmarks!
-
- memory_usedblock is an object that handles the operations for blocks on the
- heap that are in use. It is a descendant of the memory_block
- object. It is not strictly necessary that blocks on the heap
- be retyped as memory_usedblock, (by changing the VMT), but
- it should allow faster operations since the usedblock
- automatically traps out routines that it shouldn't be allowed
- to do, thus eliminating the need to check IsFree.
-
- memory_endblock is a specialized object that is placed at the end of the
- heap. It is a descendant of the memory_usedblock object.
- It should have no size, and is strictly a placemarker so
- that all other blocks will have a next pointer other than
- zero, so they will all be able to use segment arithmetic
- to calculate their sizes.
-
-
- Object Hierarchy:
-
- MEMORY_SYSTEM
-
-
-
- MEMORY_BLOCK
- :
- :
- MEMORY_USEDBLOCK
- :
- :
- MEMORY_ENDBLOCK
-
-
-
-
- For compatibility with future memory_block object descendants, a LockBlock
- call should be given for all memory blocks prior to usage, and an UnLockBlock
- call should be given whenever the object is free to move about. When a
- LockBlock call is given, DS:SI will be set on return to point to an actual
- memory address that should be used for operating on the area of memory. The
- UnLockBlock call must give this same memory address. UnlockBlock returns
- a value in DS:SI that can be used for the next LockBlock call.
-
- The ALLOC operation of the memory_system always locks it's block prior to
- returning. Therefore it always returns a real memory pointer. When the memory
- area has been initialized, it may be unlocked, and the return of the
- UnLockBlock call may be stored for future accesses to the memory region.
-
- This convention can allow implementing a special kind of memory block that
- knows how to swap itself to offline storage when it is not in use.
- In practice, this system may be implemented by having objects on the heap
- that represent unlocked blocks of memory. When this block recieves a lock
- call, it creates a region in memory to recieve the memory contents and then
- it loads the actual memory contents from offline storage. A pointer to this
- new block is returned. Later, when this new block is freed, the old lock
- block is informed and the lock block may swap the memory area to disk.
-
- Normal memory_blocks, and memory_usedblocks, do not process the LockBlock or
- UnLockBlock calls in any way. So how do blocks that implement swapping come
- to be included in the memory system? A process outside the memory_system,
- a caller of memory_system, can implement this. The process calls the
- memory_system to give it a block of memory. (This block of memory will
- probably be the size of the swapping_block object.) This block of memory
- can then be retyped by changing it's VMT. The new VMT will then point to
- the method table for a different class of object that implements swapping
- of it's memory region. (The outside process might make another call to
- the swapping_block object that informs the swapping block how big of an
- area it is managing.) As long as the memory_block can respond to normal
- requests that the memory_system makes, (it will probably respond with the
- methods of the memory_usedblock, while any special messages that it takes
- are added to the end of the memory_usedblock VMT), the existance of the
- block will not cause any problems. It can transparently handle LockBlock and
- UnLockBlock calls by creating and destroying new blocks of memory within
- the memory_system that it belongs to, because the outside process would
- have passed it a handle to the memory_system, and also any handles or
- information that it needs for accessing the offline virtual storage that
- it swaps information in and out of.
-
- Of course, it is possible that the memory_system itself might be able to
- transparently create these new types of memory_blocks. If the memory_system
- is passed a pointer to the init method for a new type of block, then
- it would automatically deal with the new type of block. The init method
- for memory_blocks requires the size of the block to be passed. Currently
- this is set to be the same size as the block that the memory_system
- selected.
-
- ~
- jumps
- page
- locals @@
-
- ; Define the following to enable the optimization of the rover pointer
- ; to shorten time to find a free heap area.
- _USE_ROVER_ =1
-
-
- ; Define the following to enable a freed block to be combined with
- ; any free block that might be immediately before it.
- ; Defining this equate causes a tremendous slow down in the deallocation
- ; process, since a scan for previous must be done for every free operation.
- ; This scan for previous has to walk the entire memory block chain up to
- ; the item that is being freed in order to find the previous block and see
- ; if it is free.
- ; Logic has been added instead to the alloc routine to combine adjacent
- ; free blocks that it finds while it is walking forward through the
- ; chain of memory blocks.
- ;_COMBINE_PREV_=1
-
- ; The PREV pointer for unused blocks is not always accurate, since
- ; scanprev is not done during dealloc. Do not define the following.
- ;_DO_SETPREV_ = 1
-
-
- ; Since used blocks have a different VMT pointer than unused blocks,
- ; routines that are not valid for used blocks, (e.g. setprev and setsize),
- ; can be trapped out from the default routine in the memory_block object.
- ; Then those memory block objects can assume that they were called because
- ; the block is free. If this equate is not defined, it is important that
- ; all used blocks, (and end blocks), are not left as regular memory_blocks,
- ; but are instead retyped as what they are by changing the VMT.
- ;__DO_ISFREE_ = 1
-
-
- .model MDL,pascal
- .code
-
- ; Int 21 DOS calls
- DOSINT = 21H
- DOSGETMEMBLOCK = 48H
- DOSRELEASEBLOCK = 49H
- DOSRESIZEBLOCK = 4AH
- DOSTERMINATE = 4CH
-
-
- ; Include the utility macros that ease dealing with
- ; the Virtual Method Tables.
- include vmtutil.inc
-
- IsZero macro reg
- or reg,reg
- endm
-
- ; Include the names of routines that handle display of numbers.
- include display.inc
-
-
- ; Cause the VMT tables to be defined as a part of this module.
- _MAKE_MEMVMT_ = 1
- include mem.inc
-
-
- PAGE
- ;************************************ Routines for MEMORY_BLOCK
-
- memory_block_reserved_size proc
- mov bx,offset @memory_block_used_start
- ret
- memory_block_reserved_size endp
-
-
-
- ; AX has size of block to allocate into the chain.
- memory_block_initget proc pascal previousblock:word,nextblock:word
-
- ret
- memory_block_initget endp
-
-
-
- memory_block_init proc pascal uses ax,\
- previousblock:word,nextblock:word,\
- bsize:word
-
- ; Set the VMT PTR within this block!
- mov [ds:si.@Mptr_memory_block],offset @TableAddr_memory_block
- ;if @CodeSize eq 1
- ; mov word ptr [ds:si.@Mptr_memory_block+2],seg @TableAddr_memory_block
- ;endif
-
- ifdef _DO_SETPREV_
- mov ax,[previousbLock]
- mov word ptr [ds:si.prev],ax
- endif
-
- mov ax,FREE_BLOCK
- mov word ptr [ds:si.next],ax
- mov ax,[nextblock]
- mov word ptr [ds:si.next2],ax
- mov ax,[bsize]
- mov [ds:si.blksize],ax
- xor ax,ax
-
- ; Zero out the unused high words
- mov word ptr [ds:si.next+2],ax
- mov word ptr [ds:si.next2+2],ax
- ifdef _DO_SETPREV_
- mov word ptr [ds:si.prev+2],ax
- endif
- ret
- memory_block_init endp
-
-
-
- memory_block_deinit proc
- ret
- memory_block_deinit endp
-
-
-
- ; AX has the previous block segment
- ; If combine is done, DS points to the previous segment.
- memory_block_combine proc
- ifdef _DO_ISFREE_
- call [ds:si] method memory_block:IsFree
- jnz @@done
- endif
-
- push cx
- mov cx,ax ; Save AX, so we can set it's block to point to the
- ; next of the DS block.
- call [ds:si] method memory_block:GetNext
- mov ds,cx
- call [ds:si] method memory_block:SetNext
- ; Also, need to update size of previous block
- ; AX still contains seg of block after one being freed.
- sub ax,cx
- dec ax
- call [ds:si] method memory_block:SetSize
- pop cx
-
- @@done:
- ret
- memory_block_combine endp
-
-
-
- mb_show proc uses ds es dx ax
- .data
- @@used db "Used$"
- @@free db "Free" ;,"$"
- @@next2 db " Next2:$"
- @@prev db " Prev:$"
- @@blksize db " Blksize:$"
- .code
- call Show_Bracket
- call [ds:si] method memory_block:IsFree
-
- ; Put our pointer into ES:SI
- push ds
- pop es
- mov ax,@data
- mov ds,ax
-
- ; Print a USED or FREE string
- mov ah,DOSPRINTSTRING
- jz @@block_is_free
-
- ; We should never get here if memory_usedblocks are used.
- mov dx,offset @@used
- int DOSINT
- jmp @@done
-
- @@block_is_free:
- mov dx,offset @@free
-
- @@show_msg:
- int DOSINT
-
- ; mov ah,DOSPRINTSTRING
- ; mov dx,offset @@next2
- ; int DOSINT
- mov ax,word ptr [es:si.next2]
- call ShowHexWord
-
- ifdef _DO_SETPREV_
- mov ah,DOSPRINTSTRING
- mov dx,offset @@prev
- int DOSINT
- mov ax,word ptr [es:si.prev]
- call ShowHexWord
- endif
-
- mov ah,DOSPRINTSTRING
- mov dx,offset @@blksize
- int DOSINT
- mov ax,word ptr [es:si.blksize]
- call ShowHexWord
-
- @@done:
- call Show_Endbracket
- ret
- mb_show endp
-
-
-
- mb_findprev proc
- local findprevfor:word
- mov [findprevfor],ax
-
- @@check:
- LoadVMTSeg es,ax
- call [ds:si] method memory_block:GetNext
- cmp ax,[findprevfor]
- je @@previousfound
-
- IsZero ax
- je @@noprevious
-
- ; Load the segment of the next block, and go check it out
- mov ds,ax
- jmp @@check
-
- @@previousfound:
- mov ax,ds ; Store the segment of the current block in AX.
- jmp @@done
-
- @@noprevious:
- xor ax,ax ;Zero out AX to indicate no previous.
- @@done:
- ret
- mb_findprev endp
-
-
-
- mb_getnext proc
- ifdef _DO_ISFREE_
- mov ax,word ptr [si.next]
- cmp ax,FREE_BLOCK
- jne @@done
- endif
- mov ax,word ptr [si.next2]
- @@done:
- ret
- mb_getnext endp
-
-
-
- mb_isfree proc
- ifdef _DO_ISFREE_
- cmp word ptr [si.next],FREE_BLOCK
- else
- ; All we need to do is set the Z flag!
- cmp ax,ax
- endif
- ret
- mb_isfree endp
-
-
-
- mb_setprev proc
- ifdef _DO_SETPREV_
- ifdef _DO_ISFREE_
- LoadVMTSeg es,bx
- call [ds:si] method memory_block:IsFree
- jnz @@1
- endif
- mov word ptr [ds:si.prev],ax
- @@1:
- endif
- ret
- mb_setprev endp
-
-
-
- mb_setsize proc
- ifdef _DO_ISFREE_
- LoadVMTSeg es,bx
- call [ds:si] method memory_block:IsFree
- jnz @@1 ; short @memory_block_setsize_done
- endif
- mov word ptr [ds:si.blksize],ax
- ifdef _DO_ISFREE_
- @@1:
- endif
- ret
- mb_setsize endp
-
-
-
- mb_setnext proc
- ifdef _DO_ISFREE_
- LoadVMTSeg es,bx
- call [ds:si] method memory_block:IsFree
- jnz short @@notfree
- endif
-
- ; If we are here, the block is free.
- mov word ptr [ds:si.next2],ax
- ifdef _DO_ISFREE_
- jmp short @@done
-
- @@notfree:
- mov word ptr [ds:si.next],ax
- @@done:
- endif
- ret
- mb_setnext endp
-
-
-
- mb_scan proc
-
- ; LoadVMTSeg es,bx
-
- @@check:
- call [ds:si] method memory_block:IsFree
- ; cmp word ptr [ds:si.next],FREE_BLOCK ; See if this block is free
- jz @@found
-
- ; It's not free. So go to the next block.
- call [ds:si] method memory_block:GetNext
- IsZero ax ; Check for the last block in the chain (next=0?)
- jz @@done
- mov ds,ax
- jmp @@check
-
- @@found:
- ; Load the size of the current block into AX
- ; Size in paragraphs is (seg of next block) - (seg of this block)
- mov ax,ds
- neg ax
- add ax,word ptr [ds:si.next2]
- dec ax ; Adjust for block bookkeeping information
- ; mov ax,[ds:si.blksize] ; This should produce the same
- ; value for free blocks.
-
- @@done:
- ret
- mb_scan endp
-
-
- mb_rawblocksize proc
- ; LoadVMTSeg es,bx
- call [ds:si] method memory_block:GetNext
- mov bx,ds
- sub ax,bx
- ret
- mb_rawblocksize endp
-
-
-
- mb_breakblock proc uses ds si
- local desiredsize:word,currentnext:word,newnext:word,currblock:word,\
- newblocksize:word
-
- mov [desiredsize],ax
- mov ax,ds
- mov [currblock],ax
- mov [newnext],ax ; We'll use newnext as the address to return in
- ; AX.
-
- ; This routine is only valid for free memory blocks
- ; LoadVMTSeg es,bx
- ifdef _DO_ISFREE_
- call [ds:si] method memory_block:IsFree
- jnz @@done
- endif
-
- call [ds:si] method memory_block:RawBlockSize
- mov bx,ax
- sub bx,[desiredsize]
- dec bx ; Represent bookkeeping information of current block
- dec bx ; For bookkeeping information of the new block
- mov [newblocksize],bx
- cmp bx,2 ; If less than four paragraphs left over, don't
- ; do the breakdown.
- jbe @@done
-
- ; Get the current next block
- call [ds:si] method memory_block:GetNext
- mov [currentnext],ax
-
- ; Calculate the segment of the new block
- mov ax,ds
- add ax,[desiredsize]
- inc ax ; For the size of our bookkeeping info
- mov [newnext],ax
-
- ; Set our next pointer to new block
- call [ds:si] method memory_block:SetNext
-
- ; Set our size
- mov ax,[desiredsize]
- call [ds:si] method memory_block:SetSize
-
- ; Set the new block to point to its neighbors
- mov ds,[newnext]
- call [ds:si] method memory_block:init pascal,[currblock],[currentnext],[newblocksize]
-
- ifdef _DO_SETPREV_
- ; Set the old next block to point to the new block created just
- ; before it.
- mov ds,[currentnext]
- call [ds:si] method memory_block:setprev
- endif
-
- @@done:
- mov ax,[newnext]
- ret
- mb_breakblock endp
-
-
-
- memory_block_markfree proc
- ifdef _DO_ISFREE_
- local previous:word
-
- mov [previous],ax
- call [ds:si] method memory_block:IsFree
- jz @@block_is_free_skip
-
- ; Get the pointer to the next block
- call [ds:si] method memory_block:GetNext
-
- ; Calculate the size of this block
- push ax
-
- mov bx,ax
- mov ax,ds
- sub bx,ax
- dec bx ; For the bookkeeping area
-
- pop ax
- call [ds:si] method memory_block:init pascal,[previous],ax,bx
-
- @@block_is_free_skip:
- endif
- ret
- memory_block_markfree endp
-
-
-
- memory_block_markused proc
- ifdef _DO_ISFREE_
- call [ds:si] method memory_block:IsFree
- jnz @@done
- endif
- mov ax,word ptr [ds:si.next2]
- mov word ptr [ds:si.next],ax
- mov ax,word ptr [ds:si.next2+2]
- mov word ptr [ds:si.next+2],ax
-
- ; Change the block type to be a used block.
- ; Do this by changing the VMT!
- mov [ds:si.@Mptr_memory_block],offset @TableAddr_memory_usedblock
- ;if @CodeSize eq 1
- ; mov word ptr [ds:si.@Mptr_memory_block+2],seg @TableAddr_memory_usedblock
- ;endif
- @@done:
- ret
- memory_block_markused endp
-
-
-
- memory_block_lock proc
-
- ret
- memory_block_lock endp
-
-
-
- memory_block_unlock proc
-
- ret
- memory_block_unlock endp
-
-
- memory_block_allocfail proc
- ret
- memory_block_allocfail endp
-
-
- page
- ;***** Routines for MEMORY_USEDBLOCK
-
-
- memory_usedblock_markfree proc
- local previous:word
-
- mov [previous],ax
- ifdef _DO_ISFREE_
- call [ds:si] method memory_block:IsFree
- jz @@block_is_free_skip
- endif
-
- ; Get the pointer to the next block
- call [ds:si] method memory_block:GetNext
-
- ; Calculate the size of this block
- push ax
-
- mov bx,ax
- mov ax,ds
- sub bx,ax
- dec bx ; For the bookkeeping area
-
- pop ax
- call [ds:si] method memory_block:init pascal,[previous],ax,bx
-
- @@block_is_free_skip:
- ret
- memory_usedblock_markfree endp
-
-
- memory_usedblock_combine proc
- ret ;Can't do a combine with a used block. So ignore it!
- memory_usedblock_combine endp
-
-
-
- memory_usedblock_setnext proc
- mov word ptr [ds:si.next],ax
- ret
- memory_usedblock_setnext endp
-
-
-
- memory_usedblock_invalid proc
- ret
- memory_usedblock_invalid endp
-
-
-
- memory_usedblock_isfree proc uses ax
- mov ax,1 ; Set the Z flag to zero!
- IsZero ax
- ret
- memory_usedblock_isfree endp
-
-
-
- memory_usedblock_scan proc
- ; This block is not free, so quickly pass the call
- ; along to the next object in this chain!
- mov ax,word ptr [ds:si.next]
- mov ds,ax
- IsZero ax ; Check for zero, meaning end of the line!
- jz @@done
-
- ; Use a jump, instead of
- ; call [ds:si] method memory_usedblock:ScanFree
- ; so that we eliminate overflowing the stack during this routine.
- MOV BX,[([DS :SI ]).@Mptr_memory_block]
- ;if @CodeSize eq 1
- ; MOV ES,word ptr [([DS :SI ]).@Mptr_memory_block+2]
- ;endif
- jmp [(@Table_memory_usedblock PTR ES:BX).ScanFree]
-
- @@done:
- ret
- memory_usedblock_scan endp
-
-
-
- memory_usedblock_getnext proc
- mov ax,word ptr [ds:si.next]
- ret
- memory_usedblock_getnext endp
-
-
-
- memory_usedblock_init proc
- ; Set the VMT PTR within this block!
- mov [ds:si.@Mptr_memory_block],offset @TableAddr_memory_usedblock
- ret
- memory_usedblock_init endp
-
-
-
- memory_usedblock_show proc uses ds es dx ax
- .data
- @@type db "USED$"
- @@next db " Next:$"
- .code
- call Show_Bracket
- push ds
- pop es
- mov ax,@data
- mov ds,ax
- mov dx,offset @@type
- mov ah,DOSPRINTSTRING
- int DOSINT
-
- mov ah,DOSPRINTSTRING
- mov dx,offset @@next
- int DOSINT
- mov ax,word ptr [es:si.next]
- call ShowHexWord
-
- call Show_Endbracket
- ret
- memory_usedblock_show endp
-
-
-
- memory_usedblock_lock proc
-
- ret
- memory_usedblock_lock endp
-
-
-
- memory_usedblock_unlock proc
-
- ret
- memory_usedblock_unlock endp
-
-
-
-
- page
- ;***** Routines for MEMORY_ENDBLOCK
-
-
- memory_endblock_ignore proc
- ret
- memory_endblock_ignore endp
-
-
-
- memory_endblock_getnext proc
- xor ax,ax
- ret
- memory_endblock_getnext endp
-
-
-
- memory_endblock_init proc
- ; Set the VMT PTR within this block!
- mov [ds:si.@Mptr_memory_block],offset @TableAddr_memory_endblock
- ;if @CodeSize eq 1
- ; mov word ptr [ds:si.@Mptr_memory_block],seg @TableAddr_memory_endblock
- ;endif
- xor ax,ax
- mov word ptr [ds:si.next],ax
- mov word ptr [ds:si.next+2],ax
- ret
- memory_endblock_init endp
-
-
- memory_endblock_show proc uses ds dx ax
- .data
- @@end db "END$"
- .code
- call Show_Bracket
- mov ax,@data
- mov ds,ax
- mov dx,offset @@end
- mov ah,DOSPRINTSTRING
- int DOSINT
-
- call Show_Endbracket
- ret
- memory_endblock_show endp
-
-
-
-
-
- page
- ;***** Routines for MEMORY_SYSTEM
-
-
- memory_system_init proc uses es ds si bx
- local endblockseg:word
-
- ; Fill in the VMT for the memory system
- mov [ds:si.@Mptr_memory_system],offset @TableAddr_memory_system
-
- ; Get a block of memory.
- mov bx,ax ; Get the desired size, unless it is zero,
- ; then get the largest possible block.
- or bx,bx
- jnz @Memory_system_init_Alloc
-
- dec bx ;mov bx,0FFFFh ; Go for largest possible
- @Memory_system_init_Alloc:
- mov [ds:si.blocksize],bx
- mov ah,DOSGETMEMBLOCK
- int DOSINT
-
- ; Need to handle fail here! (Carryflag set, and BX with size of
- ; largest block available. )
- jnc AllocSuccess
-
- ; Check the high bit of blocksize to see
- ; if we can get a different size.
- ; Note that if we come here a second time, it is because DOS
- ; said a certain size was available, and then it wouldn't give
- ; it when requested. The high bit wont be set on the second call.
- TESTFLAG [ds:si.blocksize],8000h
- jz AllocFailed
-
- ; BX has size of largest block available. Go get it if it is NZ.
- or bx,bx
- jnz @Memory_system_init_Alloc ; Jump back and get it if there
- ; is some available.
- AllocFailed:
- mov [ds:si.blocksize],0
- jmp @Memory_system_init_done
-
- AllocSuccess:
- ; Success: Carryflag clear, and AX initial segment of allocated block
-
- mov [si.root],ax ; Segment of start of block still in AX!
-
- mov [si.rover],ax
-
- ; Load the size of the block into BX
- mov bx,[ds:si.blocksize]
- dec bx ; For bookkeeping
- dec bx ; For the endblock
-
- ; Calculate in CX the segment of the endblock
- mov cx,bx
- inc cx ; For bookkeeping for first block
- add cx,ax ; To get segment
- mov [endblockseg],cx
- mov [si.last],cx
-
-
- ; Save the address of the memory system, so we
- ; can call constructor for the memory block.
- push ds
- push si
-
- ; Load the pointer to the block into DS:SI
- mov ds,ax
- xor si,si
- xor ax,ax ; For pushing zeros for prev & next blocks
-
- ; ; Need to set the VMT for the memory block before calling init!
- ; ; This is only needed if INIT routine is in the VMT!
- ; mov [ds:si.@Mptr_memory_block],offset @TableAddr_memory_block
-
- LoadVMTSeg ES
- call [ds:si] method memory_block:init pascal, ax,cx,bx
-
- ; Now make an endblock
- mov ds,[endblockseg]
- call [ds:si] method memory_endblock:init
-
-
- ; Restore pointre to the memory system
- pop si
- pop ds
-
- @Memory_system_init_done:
- mov [ds:si.usedspace],0
- mov ax,[ds:si.blocksize]
- dec ax ; Bookkeeping for the first block
- dec ax ; Bookkeeping for the end block
- mov [ds:si.freespace],ax
- dec [ds:si.freespace]
- ret
- memory_system_init endp
-
-
-
- memory_system_deinit proc
- ; Return the memory to DOS.
- mov es,[ds:si.root]
- mov ah,DOSRELEASEBLOCK
- int DOSINT
- ret
- memory_system_deinit endp
-
-
-
- memory_system_resetrover proc
- mov bx,[ds:si.root]
- mov [ds:si.rover],root
- ret
- memory_system_resetrover endp
-
-
-
- .data
- mss_initial db "Memory System Show:",CR,LF
- db "System at:$"
- mss_root db " Root block at:$"
- mss_last db " Last block at:$"
- mss_rover db " Rover block at:$"
- mss_free db " Freespace:$"
- mss_used db " Usedspace:$"
- mss_blocks db "Blocks at:$"
- mss_no_blks db "No blocks in heap.$"
- .code
- memory_system_show proc uses ax dx es ds
- ; Move pointer to the memory system from DS:SI, to ES:SI
- push ds
- pop es
- ; Now load pointer to our messages
- mov dx,@data
- mov ds,dx
- ; Show the location of the memory system
- mov dx,offset mss_initial
- mov ah,DOSPRINTSTRING
- int DOSINT
- mov ax,es
- call ShowHexWord
- mov dl,':'
- mov al,DOSPRINTCHAR
- int DOSINT
- mov ax,si
- call ShowHexWord
- call CRLF
-
- ; Show the internal variables of the memory system
- mov dx,offset mss_root
- mov ah,DOSPRINTSTRING
- int DOSINT
- mov ax,[es:si.root]
- call ShowHexWord
- call CRLF
- mov dx,offset mss_last
- mov ah,DOSPRINTSTRING
- int DOSINT
- mov ax,[es:si.last]
- call ShowHexWord
- call CRLF
- mov dx,offset mss_rover
- mov ah,DOSPRINTSTRING
- int DOSINT
- mov ax,[es:si.rover]
- call ShowHexWord
- call CRLF
- mov dx,offset mss_free
- mov ah,DOSPRINTSTRING
- int DOSINT
- mov ax,[es:si.freespace]
- call ShowHexWord
- call CRLF
- mov dx,offset mss_used
- mov ah,DOSPRINTSTRING
- int DOSINT
- mov ax,[es:si.usedspace]
- call ShowHexWord
- call CRLF
-
- ; Save pointer to the memory system
- push es
- push si
- mov ax,[es:si.root]
- xor si,si
-
- ; Check for empty chain
- cmp ax,0
- jz mss_no_blocks
-
- push ax
-
- ; Now we need to walk the chain
- mov dx,offset mss_blocks
- mov ah,DOSPRINTSTRING
- int DOSINT
-
- pop ax
-
-
- mss_show_block:
- mov ds,ax
- call ShowHexWord
-
- LoadVMTSeg ES,AX
- call [ds:si] method memory_block:show
-
- call [ds:si] method memory_block:GetNext
- ; See if it is the last block
- IsZero ax
- jz mss_show_block_done
- push ax
- mov ah,DOSPRINTCHAR
- mov dl,','
- int DOSINT
- pop ax
- jmp mss_show_block
-
- ; Branch here if this heap system is still empty
- mss_no_blocks:
- mov dx,offset mss_no_blks
- mov ah,DOSPRINTSTRING
- int DOSINT
-
- mss_show_block_done:
- pop si
- pop es
-
- call CRLF
-
- ret
- memory_system_show endp
-
-
-
- memory_system_freeall proc
- mov ax,[si.root]
- @@free_another:
- ; If it is the end block, dont try to free it.
- cmp ax,[ds:si.last]
- jz @@done
- push ax
- call [ds:si] method memory_system:free
- pop ax
- push ds
- push si
- mov ds,ax
- xor si,si
- call [ds:si] method memory_block:GetNext
- IsZero ax
- pop si
- pop ds
- jnz @@free_another
- @@done:
- ret
- memory_system_freeall endp
-
-
-
- memory_system_findprev proc uses ds si
- ; local systemseg:word,systemofs:word,findprevfor:word
- ; mov [systemseg],ds
- ; mov [systemofs],si
- ; mov [findprevfor],ax
-
- ; Check if this block is the root block
- cmp ax,[si.root]
- jz @memory_system_findprev_atroot
-
- ; Make pointer to first block in memory system
- mov ds,[si.root]
- xor si,si
-
- call [si] method memory_block:MatchNext
- cmp ax,0
- jne @memory_system_findprev_done
-
- ; The block wasn't found!
-
- @memory_system_findprev_atroot:
-
- @memory_system_findprev_done:
- ; mov ds,[systemseg]
- ; mov si,[systemofs]
- ret
- memory_system_findprev endp
-
-
-
- memory_system_blockofs proc uses ds si
- cmp ax,0
- jne @memory_system_blockofs_getit
- mov ax,[ds:si.root] ; Get the root and use it's offset since
- ; no segment was passed in
- @memory_system_blockofs_getit:
- mov ds,ax
- xor si,si
- call [ds:si] method memory_block:memstart
- ret
- memory_system_blockofs endp
-
-
-
- memory_system_alloc proc uses ds si
- local allocsize:word,\
- allocblock:word,\
- largest:word,\ ; The size of the largest block encountered
- largestseg:word,\
- newfree:word,\
- rootscan:word,\
- memsysaddr:dword
-
- mov [largest],0
- mov [rootscan],0
-
- mov word ptr [memsysaddr],si
- mov word ptr [memsysaddr+2],ds
-
- mov [allocsize],ax
- ifdef _USE_ROVER_
- mov ax,[ds:si.rover]
- else
- mov ax,[ds:si.root]
- endif
- mov ds,ax
- @@scan:
- xor si,si
- call [ds:si] method memory_block:ScanFree
- ; Check if there is enough room in this free block
- cmp ax,[largest]
- jbe @@scan_2
- mov [largest],ax
- mov [largestseg],ds
- @@scan_2:
- cmp ax,[allocsize]
- ja @@found
- mov ax,ds
- IsZero ax
- je @@no_more_blocks
- call [ds:si] method memory_block:GetNext
- IsZero ax
- je @@no_more_blocks
-
-
- ; The following block may also be free. Try to combine them.
- ; Swap DS and AX
- mov bx,ds ; DS has the previous block!
- mov ds,ax
- mov ax,bx ; Make sure AX has the previous block
- call [ds:si] method memory_block:Combine
-
- jmp @@scan
-
- ; No more blocks to scan!
- @@no_more_blocks:
- ifdef _USE_ROVER_
- ; Scan again from the root, because a big enough
- ; block could be prior to rover.
- cmp [rootscan],0
- jne @@scan_root
-
- inc [rootscan]
- lds si,dword ptr [memsysaddr]
- mov ax,[ds:si.root]
-
- ; Check to see that rover wasn't already pointing at the root
- cmp ax,[ds:si.rover]
- je @@scan_root
-
- mov ds,ax
- jmp @@scan
-
- @@scan_root:
- endif
- ; Set rover to point to this new largest memory block,
- ; because combines might have invalidated the rover pointer.
- lds si,dword ptr [memsysaddr]
- mov ax,[largestseg]
- mov [ds:si.rover],ax
- xor ax,ax
- mov bx,[largest]
- jmp @@done
-
- @@found:
- mov [allocblock],ds
- ; See how much is leftover in the block after the memory needed
- ; for this alloc call is taken out
- mov ax,[allocsize]
- call [ds:si] method memory_block:BreakBlock
- ifdef _USE_ROVER_
- mov [newfree],ax
- endif
- ; Mark the block as used!
- call [ds:si] method memory_block:MarkUsed
- call [ds:si] method memory_block:LockBlock
- call [ds:si] method memory_block:memstart
- push bx
-
- ; If enough area was leftover, the extra area has been
- ; broken off to make a separate block.
- lds si,dword ptr [memsysaddr]
- ifdef _USE_ROVER_
- ; Update rover
- mov ax,[newfree]
- mov [ds:si.rover],ax
- endif
-
- mov ax,[allocblock]
- pop bx
- ; call [ds:si] method memory_block:memstart
- @@done:
- ret
- memory_system_alloc endp
-
-
-
- memory_system_free proc uses ds si bx
- local prevblock:word,\ ; Address of the block previous to this one
- freeblock:word,\ ; Address of block that is being freed
- systemaddr:dword
-
- mov word ptr [systemaddr],si
- mov word ptr [systemaddr+2],ds
- mov [freeblock],ax
-
- cmp ax,0
- jz @@done
-
- ; Check the block to make sure that it is not already free
- ; Note that an invalid block address here will likely hang the
- ; memory manager, since the VMT will not be valid.
- mov ds,ax
- xor si,si
- ifdef _DO_ISFREE_
- call [ds:si] method memory_block:IsFree
- jz @@done
- endif
- call [ds:si] method memory_block:UnLockBlock
-
- lds si,dword ptr [systemaddr]
- mov ax,[freeblock]
- cmp ax,[ds:si.root]
- je @@free_root ; Special routine to free the root block
-
- ifdef _COMBINE_PREVIOUS_
- ; Prior to freeing the block, get the address of
- ; the previous block. If no previous block is found,
- ; then this is a bogus FREE request and will be ignored.
- call [ds:si] method memory_system:findprev
- IsZero ax ; Check for zero.
- jz @@done
-
- ; There is a previous block
- mov [prevblock],ax
- endif
-
- ; Mark the current block as free.
- mov ds,[freeblock]
- xor si,si
- call [ds:si] method memory_block:MarkFree
-
- ifdef _COMBINE_PREVIOUS_
- mov ds,[prevblock]
- xor si,si
- call [ds:si] method memory_block:IsFree
- mov ds,[freeblock]
- jnz @@try_combine_next
- else
- jmp @@try_combine_next
- endif
-
- @@combine_previous:
- ; The previous block is free, therefore it can be combined with
- ; this one. Set the previous block to point to our next block.
- call [ds:si] method memory_block:GetNext
- mov ds,[prevblock]
- call [ds:si] method memory_block:SetNext
- ; Also, need to update size of previous block
- ; AX still contains seg of block after one being freed.
- mov bx,ds
- sub ax,bx
- dec ax
- call [ds:si] method memory_block:SetSize
- mov [freeblock],ds
- jmp @@try_combine_next
-
- @@free_root:
- ; At this point we only have to worry is if the block following
- ; a free block so it can be combined with this one.
- ; AX is the block to free.
- mov ds,ax
- xor si,si
- xor ax,ax ; Because there is not previous block
- call [ds:si] method memory_block:MarkFree
-
- @@try_combine_next:
- call [ds:si] method memory_block:GetNext
- mov ds,ax
- call [ds:si] method memory_block:IsFree
- jnz @@No_more_combine
- mov ax,[freeblock]
- mov [prevblock],ax
- jmp @@combine_previous
-
- @@No_more_combine:
- ifdef _USE_ROVER_
- ; Just in case the rover might have pointed to a block that was
- ; combined with another, set rover to the start of the new
- ; free block that was created.
- lds si,dword ptr [systemaddr]
- mov bx,[freeblock]
- mov [ds:si.rover],bx
- endif
-
-
- @@done:
- ret
- memory_system_free endp
-
-
- .data
- LastSeg dw seg zzlastseg
-
-
- .code
- shrink_memory proc
- ; Have to give up extra memory that we don't need
- ; so the memory system objects can get blocks of memory.
- mov bx,[LastSeg]
- mov ax,es
- sub bx,ax ; Size of program as a number of segments is in BX
- mov ah,DOSRESIZEBLOCK
- int DOSINT
-
- ret
- shrink_memory endp
-
-
-
- zzlastseg segment
-
- zzlastseg ends
-
- end
-
-