; =============================================================================
;
; Litos - System memory allocator
;
; =============================================================================
CODE_SECTION 32
; ------------- Constants (there are hardcoded on many places)
DMAMAXMEM EQU 1000000h ; DMA memory size (16 MB)
MEMAREASIZE EQU 20000h ; size of memory area (128 KB)
MEMAREAMASK EQU ~(MEMAREASIZE-1) ; mask memory area
MEMAREASHIFT EQU 17 ; bit shifts of memory area
MEMAREAPAGES EQU MEMAREASIZE/PAGE_SIZE ; number fo pages per area (32)
MEMAREANUM EQU SYSTEM_SIZE/MEMAREASIZE; number of memory areas (4000h)
MEMNUMCORR EQU SYSTEM_ADDR >> MEMAREASHIFT ; area number correction
; ------------- Block descriptor
struc BLOCK
resb LIST_size ; 0: chain of free memory blocks
BLOCK_Order: resd 1 ; 8: order of memory block
BLOCK_Size: resd 1 ; 0Ch: size of one memory block
BLOCK_Blocks: resd 1 ; 10h: number of blocks per one area
BLOCK_Last: resd 1 ; 14h: offset of last memory block
resd 2 ; 18h: ...reserved (for align)
endstruc ; size 32 bytes (it is hardcoded!)
; ------------- Initialized block descriptor (%1=order of block)
%macro BLOCKHEAD 1
LISTHEAD ; chain of free memory blocks
dd %1 ; order of memory block
dd (1 << %1) ; size of one memory block
dd MEMAREASIZE / (1 << %1) ; number of blocks per one area
dd MEMAREASIZE - (1 << %1) ; offset of last memory block
dd 0,0 ; reserved (for align)
%endmacro
; ------------- DMA memory free block
struc DMAMEM
resb LIST_size ; 0: list of DMA memory free blocks
DMAMEM_Size: resd 1 ; 8: size of DMA memory block (bytes)
endstruc ; size 12 bytes
; -----------------------------------------------------------------------------
; Initialize system memory allocator
; -----------------------------------------------------------------------------
; DESTROYS: All registers
; -----------------------------------------------------------------------------
; ------------- Mark all areas as page-sized
SysMemInit: mov edi,SysMemAreas ; EDI <- map of order of memory areas
mov eax,0c0c0c0ch ; all pages are 12-bits (= 4 KB)
mov ecx,MEMAREANUM/4 ; ECX <- size of map / 4
rep stosd ; initialize map
; ------------- Mark all pages as used
mov edi,SysMemUsed ; EDI <- counters of memory areas
push edi ; push EDI
mov eax,(MEMAREAPAGES<<16) + MEMAREAPAGES ; num. of pages
mov cx,MEMAREANUM/2 ; ECX <- size of map / 2
rep stosd ; initialize counters
pop edi ; pop EDI (counters)
; ------------- Prepare required DMA memory (-> EBP)
mov ebp,[TotalMemory] ; EBP <- total memory
shr ebp,5 ; EBP <- required DMA memory size
cmp ebp,DMAMAXMEM ; overflow DMA maximal address?
jbe SysMemInit1 ; not overflow
mov ebp,DMAMAXMEM ; EBP <- limit DMA memory size
; ------------- Prepare pointers to regions (it uses EAX, ESI, EDI, EBP)
SysMemInit1: mov esi,PageMemMap ; ESI <- page memory map
mov eax,SYSTEM_ADDR ; EAX <- address of memory area
; ------------- Check if this area has any usable pages
SysMemInit2: cmp dword [esi],byte 0 ; area has any free pages?
je SysMemInit8 ; area has no free pages
; ------------- Check if this memory area is whole free
cmp dword [esi],byte -1 ; is whole memory area free?
jne SysMemInit4 ; whole memory area is not free
; ------------- Check if this area should be added to the DMA memory
cmp eax,DMAMAXMEM+SYSTEM_ADDR ; is it valid DMA address?
jae SysMemInit3 ; it is not valid DMA memory
cmp ebp,PAGE_SIZE ; required any next DMA memory?
jb SysMemInit3 ; no other DMA memory required
mov edx,MEMAREASIZE ; EDX <- size of memory area
cmp ebp,edx ; required whole DMA area?
jb SysMemInit4 ; add this area per pages
; ------------- Add area into DMA memory
sub ebp,edx ; EBP <- decrease required memory
xchg eax,edx ; EDX <- address of area, EAX <- size
call DMAMemFree ; free DMA memory area
xchg eax,edx ; EAX <- address of area
jmp short SysMemInit8 ; next memory area
; ------------- Add whole area into free area chain
SysMemInit3: mov ebx,SysMemFreeArea ; free memory areas
call ListAdd ; free memory area
and word [edi],byte 0 ; all pages are free
add word [TotalMemFree+2],byte (MEMAREASIZE >> 16)
jmp short SysMemInit8 ; next memory area
; ------------- Prepare to free single pages
SysMemInit4: mov ecx,B0 ; ECX <- bit mask
push eax ; push EAX
; ------------- Check if this page is available
SysMemInit5: test [esi],ecx ; is this page available?
jz SysMemInit7 ; this page is not available
; ------------- Free this page as DMA memory
cmp eax,DMAMAXMEM+SYSTEM_ADDR ; is it valid DMA address?
jae SysMemInit6 ; it is not valid DMA memory
mov edx,PAGE_SIZE ; EDX <- page size
cmp ebp,edx ; required next page?
jb SysMemInit6 ; no other DMA required
sub ebp,edx ; decrease DMA counter
xchg eax,edx ; EDX <- address of page, EAX <- size
call DMAMemFree ; free DMA page
xchg eax,edx ; EAX <- address of page
jmp short SysMemInit7 ; next page
; ------------- Free this page
SysMemInit6: push eax ; push EAX
call SysMemFree ; free this memory page
pop eax ; pop EAX
; ------------- Next page
SysMemInit7: add eax,PAGE_SIZE ; EAX <- increase page address
shl ecx,1 ; rotate mask
jnc SysMemInit5 ; next memory page
pop eax ; pop EAX
; ------------- Next memory area
SysMemInit8: add esi,byte 4 ; ESI <- increase page memory map
add eax,MEMAREASIZE ; EAX <- increase memory area address
inc edi
inc edi ; EDI <- next counter of memory area
cmp edi,SysMemUsed+MEMAREANUM*2 ; end of memory?
jb SysMemInit2 ; initialize next memory area
; ------------- Free memory bit map
mov eax,PageMemMap ; EAX <- memory map buffer
mov ecx,(SYSTEM_SIZE/PAGE_SIZE/8)/PAGE_SIZE ; pages
mov edx,PAGE_SIZE ; EDX <- page size
SysMemInit9: cmp ebp,edx ; required next DMA page?
jb SysMemInit92 ; no other DMA memory required
sub ebp,edx ; decrease DMA counter
xchg eax,edx ; EDX <- address of page, EAX <- size
call DMAMemFree ; free DMA page
xchg eax,edx ; EAX <- address of page, EDX <- size
jmp short SysMemInit94
SysMemInit92: push eax ; push EAX
call SysMemFree ; free this memory page
pop eax ; pop EAX
SysMemInit94: add eax,edx ; EAX <- address of next page
loop SysMemInit9 ; next page
; ------------- Total DMA memory
mov eax,[TotalDMAMemFree] ; EAX <- free DMA memory
mov [TotalDMAMemory],eax ; total DMA memory
ret
; -----------------------------------------------------------------------------
; Allocate system memory block
; -----------------------------------------------------------------------------
; INPUT: EAX = required size of system memory block (max. 128 KB)
; OUTPUT: CY = insufficient memory (EAX = 0)
; EAX = address of system memory block (0=insufficient memory)
; LOCKS: SysMemLock
; NOTES: Memory blocks are aligned to their size.
; For memory blocks below 8 bytes a 8-byte block is returned.
; -----------------------------------------------------------------------------
; ------------- Push registers
SysMemAlloc: push ebx ; push EBX
push ecx ; push ECX
pushf ; push flags
cli ; disable interrupts
; ------------- Lock access to system memory allocator
%ifdef SMP
mov ebx,SysMemLock ; system memory lock
LOCK_Lock ebx ; lock system memory allocator
%endif
; ------------- Minimal size of memory block (8 bytes is size of LINK entry)
cmp eax,byte 8 ; minimal size of memory block
jae SysMemAlloc2 ; memory block size is OK
mov eax,8 ; minimal size of memory block
; ------------- Transfer size of memory block to its order
SysMemAlloc2: dec eax ; EAX <- block size - 1
bsr eax,eax ; find order of memory block - 1
cmp al,MEMAREASHIFT ; maximal order of memory block
jae SysMemAlloc4 ; invalid size of memory block
; ------------- Prepare pointer to block descriptor (-> EBX)
shl eax,5 ; (order of memory block-1) * 32
lea ebx,[SysMemBlocks+eax-2*BLOCK_size]
; ------------- Check if any free memory block is available
SysMemAlloc3: mov eax,[ebx+LIST_Next] ; first free memory block
cmp eax,ebx ; is it valid memory block?
je SysMemAlloc6 ; it is not valid memory block
; ------------- Detach block from free memory block chain
LISTDELPREV eax,ebx,ecx ; detach block
; ------------- Increase used counter
mov ecx,eax ; ECX <- address of memory block
shr ecx,MEMAREASHIFT ; ECX <- number of memory area
inc word [SysMemUsed+ecx*2-MEMNUMCORR*2] ; increase counter
; ------------- Unlock acces to system memory allocator
%ifdef SMP
LOCK_Unlock SysMemLock ; unlock system memory allocator
%endif
; ------------- Pop registers (and clear error flag)
popf ; pop flags
clc ; clear error flag
pop ecx ; pop ECX
pop ebx ; pop EBX
ret
; ------------- Error - insufficient memory
SysMemAlloc4: xor eax,eax ; EAX <- 0, invalid pointer
; ------------- Unlock acces to system memory allocator (it saves CF)
%ifdef SMP
LOCK_Unlock SysMemLock ; unlock system memory allocator
%endif
; ------------- Pop registers (and set error flag)
popf ; pop flags
stc ; set error flag
pop ecx ; pop ECX
pop ebx ; pop EBX
ret
; ------------- Get next free memory area
SysMemAlloc6: mov ecx,SysMemFreeArea ; free memory areas
mov eax,[ecx+LIST_Next] ; EAX <- next memory area
cmp eax,ecx ; is any area available?
je SysMemAlloc4 ; there is no area available
; ------------- Push registers 2
push edx ; push EDX
push esi ; push ESI
; ------------- Detach memory area from the list
LISTDELPREV eax,ecx,edx ; detach memory area from the list
; ------------- Decrease free memory counter
sub word [TotalMemFree+2],byte MEMAREASIZE >> 16
; ------------- Set order to the map
mov edx,eax ; EDX <- address of the area
shr edx,MEMAREASHIFT ; EDX <- number of the memory area
mov cl,[ebx+BLOCK_Order] ; CL <- order of memory block
mov [SysMemAreas+edx-MEMNUMCORR],cl ; store order of area
; ------------- Link first block to the head
LINKLINK ebx,eax ; link EAX after EBX
; ------------- Prepare registers to initialize memory blocks
mov ecx,[ebx+BLOCK_Blocks] ; ECX <- number of blocks
dec ecx ; without one block
jz SysMemAlloc8 ; only one block is used
mov esi,[ebx+BLOCK_Size] ; ESI <- size of a block
; ------------- Initialize chain of memory blocks
SysMemAlloc7: lea edx,[eax+esi] ; EDX <- address of next memory block
LINKLINK eax,edx ; link EDX after EAX
xchg eax,edx ; EAX <- next block
loop SysMemAlloc7 ; next memory block
; ------------- Link last block to the head
SysMemAlloc8: LINKLINK eax,ebx ; link EBX after EAX
; ------------- Pop registers 2
pop esi ; pop ESI
pop edx ; pop EDX
jmp SysMemAlloc3 ; get new entry
SysMemFree9: ret
; -----------------------------------------------------------------------------
; Free system memory block
; -----------------------------------------------------------------------------
; INPUT: EAX = address of system memory block (it can be NULL)
; OUTPUT: EAX = NULL
; LOCKS: SysMemLock
; -----------------------------------------------------------------------------
; ------------- Check if it is NULL pointer
SysMemFree: or eax,eax ; is it NULL pointer?
jz short SysMemFree9 ; it is NULL pointer
; ------------- Push registers
push ebx ; push EBX
push ecx ; push ECX
push edx ; push EDX
pushf ; push flags
cli ; disable interrupts
; ------------- Lock access to system memory allocator
%ifdef SMP
mov ebx,SysMemLock ; system memory lock
LOCK_Lock ebx ; lock system memory allocator
%endif
; ------------- Number of memory area
mov edx,eax ; EDX <- address of memory block
shr edx,MEMAREASHIFT ; EDX <- number of memory area
; ------------- Pointer to block descriptor
movzx ebx,byte [SysMemAreas+edx-MEMNUMCORR] ; order
shl ebx,5 ; EBX * 32, offset of descriptor
add ebx,SysMemBlocks - 3*BLOCK_size
; ------------- Attach memory block to block descriptor
LISTADD ebx,eax,ecx ; attach block to descriptor
; ------------- Decrease counter of used blocks
dec word [SysMemUsed+edx*2-MEMNUMCORR*2] ; decrease counter
jnz SysMemFree8 ; area is not free
; ------------- Push registers 2
push esi ; push ESI
; ------------- Free block in this memory area
and eax,MEMAREAMASK ; mask memory area
push eax ; push EAX (address of memory area)
mov ecx,[ebx+BLOCK_Blocks] ; ECX <- number of blocks
mov esi,[ebx+BLOCK_Size] ; ESI <- size of a block
SysMemFree4: LISTDEL eax,ebx,edx ; detach memory block
add eax,esi ; EAX <- next memory block
loop SysMemFree4 ; next memory block
pop eax ; pop EAX (address of memory area)
; ------------- Free memory area
mov ebx,SysMemFreeArea ; free memory areas
LISTADD ebx,eax,ecx ; free memory area
; ------------- Increase free memory counter
add word [TotalMemFree+2],byte MEMAREASIZE >> 16
; ------------- Pop registers 2
pop esi ; pop ESI
; ------------- Unlock acces to system memory allocator
SysMemFree8:
%ifdef SMP
LOCK_Unlock SysMemLock ; unlock system memory allocator
%endif
; ------------- Pop registers
popf ; pop flags
pop edx ; pop EDX
pop ecx ; pop ECX
pop ebx ; pop EBX
xor eax,eax ; EAX <- 0, invalid pointer
ret
; -----------------------------------------------------------------------------
; Allocate DMA memory block
; -----------------------------------------------------------------------------
; INPUT: EAX = required size of DMA memory block (max. 64 KB)
; OUTPUT: CY = insufficient memory (EDX = 0)
; EDX = address of DMA memory block (0=insufficient memory)
; LOCKS: DMAMemLock
; NOTES: Size of DMA memory block is rounded up to page size.
; Address of DMA memory block is aligned to page boundary.
; DMA memory block does not overlap 64 KB boundary.
; -----------------------------------------------------------------------------
; ------------- Push registers
DMAMemAlloc: push eax ; push EAX
push ebx ; push EBX
push ecx ; push ECX
push esi ; push ESI
push edi ; push EDI
pushf ; push flags
cli ; disable interrupts
; ------------- Lock access to DMA memory allocator
%ifdef SMP
mov ebx,DMAMemLock ; EBX <- DMA memory lock
LOCK_Lock ebx ; lock access to DMA memory allocator
%endif
; ------------- Correct size of memory block
add eax,PAGE_SIZE-1 ; round up to next page
and eax,PAGE_MASK ; round to page size
jnz DMAMemGet1 ; block size is OK
mov eax,PAGE_SIZE ; EAX <- minimal memory block size
DMAMemGet1: cmp eax,10000h ; maximum memory block size
ja DMAMemGet4 ; error, too big memory block required
; ------------- Prepare registers
mov ebx,DMAMemFreeHead ; EBX <- list head of DMA chain
mov edx,ebx ; EDX <- list head of DMA chain
jmp short DMAMemGet3 ; find free DMA memory block
; ------------- Find suitable DMA memory block
DMAMemGet2: mov edi,[edx+DMAMEM_Size] ; EDI <- size of memory block
sub edi,eax ; EDI <- rest of block
jnc DMAMemGet5 ; DMA block is big enough
DMAMemGet3: mov edx,[edx+LIST_Next] ; EDX <- next DMA block
cmp edx,ebx ; end of DMA memory chain?
jne DMAMemGet2 ; test next DMA memory block
; ------------- Error - insufficient memory
DMAMemGet4: xor edx,edx ; EDX <- 0, invalid memory block
; ------------- Unlock access to DMA memory allocator
%ifdef SMP
LOCK_Unlock DMAMemLock ; unlock access to DMA memory alloc.
%endif
; ------------- Pop registers (and set error flag)
popf ; pop flags
stc ; set error flag
pop edi ; pop EDI
pop esi ; pop ESI
pop ecx ; pop ECX
pop ebx ; pop EBX
pop eax ; pop EAX
ret
; ------------- Check 64 KB boundary
DMAMemGet5: xor esi,esi ; ESI <- 0, size of first part
mov ecx,edx ; ECX <- address of memory block
add ecx,0ffffh ; rounding up to next 64 KB boundary
xor cx,cx ; align to 64 KB boundary
sub ecx,edx ; ECX <- distance to 64 KB boundary
cmp ecx,eax ; is there enough space for the block?
jae DMAMemGet6 ; 64 KB boundary is OK
; ------------- Shift block to next 64 KB boundary
mov esi,ecx ; ESI <- size of first block
sub edi,ecx ; EDI <- size of second block
jc DMAMemGet3 ; error, block is not big enough
; ------------- Decrease size of DMA free memory
DMAMemGet6: sub [TotalDMAMemFree],eax ; decrease free DMA size
; ------------- Resize first part of block or detach it from the chain
mov ebx,edx ; EBX <- prepare previous block
mov [edx+DMAMEM_Size],esi ; new size of first part
or esi,esi ; is any first part of block?
jnz DMAMemGet7 ; first part of block will be valid
mov ebx,[edx+LIST_Prev] ; EBX <- previous block
call ListDelEDX ; detach block from the chain
; ------------- Attach second part of block to the list of free blocks
DMAMemGet7: or edi,edi ; remains any second part?
jz DMAMemGet8 ; no second part remains
add eax,edx ; add address of block
add eax,esi ; add first part of block
mov [eax+DMAMEM_Size],edi ; size of this block
call ListAdd ; add new entry to the chain
; ------------- Address of memory block
DMAMemGet8: add edx,esi ; EDX <- address of memory block
; ------------- Unlock access to DMA memory allocator
%ifdef SMP
LOCK_Unlock DMAMemLock ; unlock access to DMA memory alloc.
%endif
; ------------- Pop registers (and clear error flag)
popf ; pop flags
clc ; clear error flag
pop edi ; pop EDI
pop esi ; pop ESI
pop ecx ; pop ECX
pop ebx ; pop EBX
pop eax ; pop EAX
DMAMemFree9: ret
; -----------------------------------------------------------------------------
; Free DMA memory block
; -----------------------------------------------------------------------------
; INPUT: EAX = size of DMA memory block
; EDX = address of DMA memory block (it can be NULL)
; LOCKS: DMAMemLock
; NOTES: Size and address must be the same as was used in DMAMemGet.
; -----------------------------------------------------------------------------
; ------------- Check NULL pointer
DMAMemFree: or edx,edx ; NULL pointer?
jz short DMAMemFree9 ; NULL pointer
; ------------- Push registers
push eax ; push EAX
push ebx ; push EBX
push ecx ; push ECX
push edx ; push EDX
pushf ; push flags
cli ; disable interrupts
; ------------- Lock access to DMA memory allocator
%ifdef SMP
mov ebx,DMAMemLock ; EBX <- DMA memory lock
LOCK_Lock ebx ; lock access to DMA memory allocator
%endif
; ------------- Correct size of memory block
add eax,PAGE_SIZE-1 ; round up to next page
and eax,PAGE_MASK ; round to page size
jnz DMAMemFree2 ; block size is OK
mov eax,PAGE_SIZE ; EAX <- minimal memory block size
; ------------- Increase size of DMA free memory
DMAMemFree2: add [TotalDMAMemFree],eax ; increase free DMA size
; ------------- Prepare registers
mov ecx,DMAMemFreeHead ; ECX <- list head of DMA chain
mov ebx,ecx ; EBX <- list head of DMA chain
jmp short DMAMemFree4 ; find free DMA memory block
; ------------- Find suitable place for inserting DMA memory block
DMAMemFree3: cmp edx,ebx ; is it suitable address?
ja DMAMemFree5 ; it is suitable block
DMAMemFree4: mov ebx,[ebx+LIST_Prev] ; EBX <- previous DMA block
cmp ebx,ecx ; end of DMA memory chain?
jne DMAMemFree3 ; test next DMA memory block
jmp short DMAMemFree6 ; cannot join block with chain head
; ------------- Join block with previous one
DMAMemFree5: mov ecx,ebx ; ECX <- previous block
add ecx,[ecx+DMAMEM_Size] ; ECX <- end of previous block
cmp ecx,edx ; are blocks neighbors?
jne DMAMemFree6 ; blocks are not neighbors
add eax,[ecx+DMAMEM_Size] ; EAX <- new size of block
mov [ecx+DMAMEM_Size],eax ; correct size of previous block
mov edx,ecx ; EDX <- new block
jmp short DMAMemFree7
; ------------- Insert new block after found one
DMAMemFree6: mov [edx+DMAMEM_Size],eax ; set size of block
xchg eax,edx ; EAX <- address of block, EDX <- size
call ListAdd ; add new block into chain
xchg eax,edx ; EDX <- address of block, EAX <- size
; ------------- Join block with following one
DMAMemFree7: mov ecx,edx ; ECX <- block address
add ecx,eax ; ECX <- end of block
mov ebx,[edx+LIST_Next] ; EBX <- next block
cmp ecx,ebx ; is neighbor with next block?
jne DMAMemFree8 ; no, their are not neighbors
add eax,[ebx+DMAMEM_Size] ; EAX <- new size of block
mov [edx+DMAMEM_Size],eax ; set new size of block
xchg eax,ebx ; EAX <- address of next block
call ListDel ; detach following memory block
; ------------- Unlock access to DMA memory allocator
DMAMemFree8:
%ifdef SMP
LOCK_Unlock DMAMemLock ; unlock access to DMA memory alloc.
%endif
; ------------- Pop registers
popf ; pop flags
pop edx ; pop EDX
pop ecx ; pop ECX
pop ebx ; pop EBX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Data
; -----------------------------------------------------------------------------
DATA_SECTION
; ------------- Total physical RAM memory
align 4, db 0
TotalMemory: dd KernelEnd - SYSTEM_ADDR
; ------------- Free physical RAM memory (counting only free memory areas)
align 4, db 0
TotalMemFree: dd 0
; ------------- End of physical RAM memory
align 4, db 0
MemoryMax: dd KernelEnd - SYSTEM_ADDR
; ------------- System memory lock
%ifdef SMP
align 4, db 0
SysMemLock: SPINLOCK
%endif
; ------------- Free areas
align 8, db 0
SysMemFreeArea: LISTHEAD
; ------------- Table of memory blocks
align 8, db 0
SysMemBlocks: BLOCKHEAD 3 ; 8 B (=size of LIST entry)
BLOCKHEAD 4 ; 16 B
BLOCKHEAD 5 ; 32 B
BLOCKHEAD 6 ; 64 B
BLOCKHEAD 7 ; 128 B
BLOCKHEAD 8 ; 256 B
BLOCKHEAD 9 ; 512 B
BLOCKHEAD 10 ; 1 KB
BLOCKHEAD 11 ; 2 KB
BLOCKHEAD 12 ; 4 KB
BLOCKHEAD 13 ; 8 KB
BLOCKHEAD 14 ; 16 KB
BLOCKHEAD 15 ; 32 KB
BLOCKHEAD 16 ; 64 KB
BLOCKHEAD 17 ; 128 KB (=size of memory area)
; ------------- Total size of DMA memory
align 4, db 0
TotalDMAMemory: dd 0
; ------------- Free DMA memory
align 4, db 0
TotalDMAMemFree:dd 0
; ------------- DMA memory lock
%ifdef SMP
align 4, db 0
DMAMemLock: SPINLOCK
%endif
; ------------- DMA free memory block chain
align 8, db 0
DMAMemFreeHead: LISTHEAD
; -----------------------------------------------------------------------------
; Uninitialized data
; -----------------------------------------------------------------------------
BSS_SECTION
; ------------- Map of order of memory areas (3=8 bytes ... 17=128 KB)
align 4, resb 1
SysMemAreas: resb MEMAREANUM ; size 16 KB
; ------------- Used counters of memory areas (0=free)
align 2, resb 1
SysMemUsed: resw MEMAREANUM ; size 32 KB
|