; =============================================================================
;
;                            Litos8 memory manager
;
; =============================================================================

; Memory manager uses 1KB pages. There are 64 of 1KB pages total. Each page
; can be divided into small memory segment of size 8, 16, 32, 64, 128, 256,
; or 512 bytes. There can be only segment of the same size, in one page.
; Each size of small memory blocks (i.e. 8..512) has its own bit table of used
; memory segments. Bit table of 8 byte blocks has size: 10000h/8/8=1024 bytes,
; bit table of 16 byte: 512 bytes, etc.

; -----------------------------------------------------------------------------
; Exported user functions (9):
;	MemAlloc - allocate memory block
;	MemFree - free memory block
;	MemResize - resize memory block
;	MemDup - duplicate memory block
;	MemGetSize - get memory block size, check block address validity
;	MemMaxFree - get maximum available memory block
;	MemGetFreeTotal - get total free memory
;	MemGetUsedTotal - get total used memory
;	GetSysMemSize - get size of system memory
; -----------------------------------------------------------------------------

PAGE_SIZE	EQU	1024		; size of memory page
PAGE_MASK	EQU	PAGE_SIZE-1	; page mask
PAGE_BITS	EQU	10		; number of bits per memory page
PAGE_NUM	EQU	64		; total number of pages

MEM_SIZE_FREE	EQU	0		; page is free
MEM_SIZE8	EQU	1		; block size 8 bytes
MEM_SIZE16	EQU	2		; block size 16 bytes
MEM_SIZE32	EQU	3		; block size 32 bytes
MEM_SIZE64	EQU	4		; block size 64 bytes
MEM_SIZE128	EQU	5		; block size 128 bytes
MEM_SIZE256	EQU	6		; block size 256 bytes
MEM_SIZE512	EQU	7		; block size 512 bytes
MEM_SIZEKB	EQU	8		; block size xKB (number_of_KB + 7)
MEM_SIZE_USED	EQU	255		; flag - used block of xKB block string

MEM_KB_BIAS	EQU	7		; bias of block size in KB


		CODE_SECTION

; -----------------------------------------------------------------------------
;                          Initialize memory manager
; -----------------------------------------------------------------------------
; NOTES:	Should be called before loading user program.
; -----------------------------------------------------------------------------

; ------------- push registers

MemInit:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	di		; push DI

; ------------- initialize memory statistic

		xor	ax,ax		; AX <- 0
		mov	word [MemFreeTotal],ax ; total free memory = 64 KB
		mov	word [MemUsedTotal],ax ; total used memory = 0

; ------------- clear tables (here AX = 0)

		mov	di,TablesStart	; DI <- tables
		mov	cx,(TablesEnd-TablesStart)/2 ; CX <- size of tables
		rep	stosw		; clear table

; ------------- allocate reserved memory 0...0fffh (4 KB)

		xor	bx,bx		; BX <- reserved start address
		mov	al,MEM_KB_BIAS+4 ; AL <- block size, 4 KB
		call	MemMark		; mark block of memory

; ------------- allocate kernel memory

		mov	bx,KernelStart	; BX <- kernel start address
		and	bx,~PAGE_MASK	; round kernel start down
		mov	ax,KernelEnd	; AX <- kernel end address
		add	ax,PAGE_SIZE-1	; round kernel end up
		and	ax,~PAGE_MASK	; mask kernel end address
		sub	ax,bx		; AX <- kernel size
		mov	cl,PAGE_BITS	; CL <- number of bits per page
		shr	ax,cl		; AX <- number of pages
		add	al,MEM_KB_BIAS	; AL <- block size
		call	MemMark		; mark block of memory

; ------------- pop registers

		pop	di		; pop DI
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Get total free memory
; -----------------------------------------------------------------------------
; OUTPUT:	AX = total free memory (in bytes)
; -----------------------------------------------------------------------------

MemGetFreeTotal:mov	ax,[MemFreeTotal]
		ret

; -----------------------------------------------------------------------------
;                        Get total used memory
; -----------------------------------------------------------------------------
; OUTPUT:	AX = total used memory (in bytes)
; -----------------------------------------------------------------------------

MemGetUsedTotal:mov	ax,[MemUsedTotal]
		ret

; -----------------------------------------------------------------------------
;                      Get size of system memory (640 KB)
; -----------------------------------------------------------------------------
; OUTPUT:	AX = size of system memory (in KB)
; -----------------------------------------------------------------------------

GetSysMemSize:	mov	ax,[MemSizeKB]
		ret

; -----------------------------------------------------------------------------
;                        Mark single block as used
; -----------------------------------------------------------------------------
; INPUT:	AL = block size (MEM_SIZE8,...)
;		BX = block address
; -----------------------------------------------------------------------------

; ------------- push registers

MemMark:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI

; ------------- set size of first block

		mov	ah,0		; AX = block size
		mov	di,bx		; DI <- block address
		mov	cl,PAGE_BITS	; CL <- 10, size of 1 KB block
		shr	di,cl		; DI <- offset of descriptor
		add	di,MemSizeTab	; DI <- descriptor address
		inc	byte [di-MemSizeTab+MemUsedTab] ; used counter
		stosb			; set first block

; ------------- mark small block

		mov	ch,1		; CH <- 1, only 1 block
		cmp	al,MEM_SIZEKB	; big block?
		jb	short MemMark6	; small block

; ------------- set block size of big block

		mov	cx,ax		; CX <- block size
		sub	cl,MEM_KB_BIAS+1 ; CL <- number of 1 KB blocks - 1
		jz	short MemMark4	; no other blocks
MemMark2:	inc	byte [di-MemSizeTab+MemUsedTab] ; used counter
		mov	byte [di],MEM_SIZE_USED ; initialize other descriptors
		inc	di		; DI <- next block
		loop	MemMark2	; next block

; ------------- mark bits of used KB blocks

MemMark4:	sub	al,MEM_KB_BIAS	; AX <- number of 1 KB blocks
		mov	ch,al		; CH <- number of 1 KB blocks
		mov	al,MEM_SIZEKB	; AL <- 1KB block

; ------------- prepare address of bitmap table -> DI

MemMark6:	mov	di,ax		; DI <- block size
		shl	di,1		; DI <- offset of table
		mov	di,[di+MemBitMapAddr-2] ; DI <- address of table

; ------------- address in bitmap table -> DI

		mov	cl,al		; AL <- block size
		add	cl,2+3		; CL <- 2 for size, 3 for bit
		mov	ax,bx		; AX <- block address
		shr	ax,cl		; AX <- offset it bitmap table
		add	di,ax		; DI <- address in bitmap table

; ------------- bit mask -> AL, block size -> DX

		mov	ax,bx		; AX <- address
		sub	cl,3		; CL <- bits per block
		xor	dx,dx		; DX <- 0
		shr	ax,cl		; AX <- number of block
		inc	dx		; DX <- 1
		and	al,7		; AL <- bit in byte
		shl	dx,cl		; DX <- size of block
		mov	cl,al		; CL <- bit in byte
		mov	al,1		; AL <- bit 0
		shl	al,cl		; AL <- bit mask

; ------------- mark blocks

MemMark8:	or	byte [di],al	; set one bit
		sub	[MemFreeTotal],dx ; total free memory
		add	[MemUsedTotal],dx ; total used memory
		rol	al,1		; AL <- shift by one bit
		adc	di,byte 0	; DI <- shift address
		dec	ch		; counter of blocks
		jnz	short MemMark8	; next block		

; ------------- pop registers

		pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
MemMark9:	ret

; -----------------------------------------------------------------------------
;            Get memory block size, check if memory block is valid
; -----------------------------------------------------------------------------
; INPUT:	AX = block address (can be NULL)
; OUTPUT:	AX = block size (NC), or 0 if invalid memory block (CY)
;		CY = invalid memory block (AX = 0)
; -----------------------------------------------------------------------------

; ------------- check block address

MemGetSize:	or	ax,ax		; is block valid?
		jz	short MemGetSize8 ; block is NULL

; ------------- push registers

		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX

; ------------- kernel address is invalid

		cmp	ax,KernelStart	; kernel address
		je	short MemGetSize6 ; kernel address

; ------------- get block size module -> AL

		mov	dx,ax		; DX <- save block address
		xchg	ax,bx		; BX <- memory block address
		mov	cl,PAGE_BITS	; CL <- 10, size of 1 KB block
		shr	bx,cl		; BX <- offset of descriptor
		cmp	byte [bx+MemUsedTab],0 ; check used table
		je	short MemGetSize6 ; invalid memory block
		mov	al,[bx+MemSizeTab] ; AL <- block size
		or	al,al		; free memory block?
		jz	short MemGetSize6 ; invalid memory block
		cmp	al,MEM_SIZE_USED ; used block of xKB string?
		je	short MemGetSize6 ; xKB string, invalid block address

; ------------- get small block size in bytes -> AX

		cmp	al,MEM_SIZEKB	; small block?
		jae	short MemGetSize2 ; big block
		mov	bl,al		; BL <- block size module
		add	al,2		; AL <- number of bits
		mov	cl,al		; CL <- number of bits
		xor	ax,ax		; AX <- 0
		inc	ax		; AX <- 1
		shl	ax,cl		; AX <- block size

; ------------- check block address align

		dec	ax		; AX <- block size - 1
		test	dx,ax		; check address align
		jnz	short MemGetSize6 ; invalid memory block address
		inc	ax		; AX <- block size
		jmp	short MemGetSize4

; ------------- block size in KB -> AX

MemGetSize2:	test	dx,PAGE_MASK	; check address align
		jnz	short MemGetSize6 ; invalid memory block address
		sub	al,MEM_KB_BIAS	; AL <- number of KB
		mov	ah,0		; AX = number of KB
		shl	ax,cl		; AX <- block size
		mov	bl,MEM_SIZEKB	; BL <- block size module

; ------------- prepare address of bitmap table -> BX

MemGetSize4:	push	ax		; push AX (block size)
		mov	cl,bl		; CL <- block size module
		mov	bh,0		; BX = block size module
		shl	bx,1		; BX <- offset of table
		mov	bx,[bx+MemBitMapAddr-2] ; BX <- address of table

; ------------- address in bitmap table -> BX

		add	cl,2+3		; CL <- 2 for size, 3 for bit
		mov	ax,dx		; DX <- block address
		shr	ax,cl		; DX <- offset it bitmap table
		add	bx,ax		; BX <- address in bitmap table

; ------------- bit mask -> AL

		sub	cl,3		; CL <- bits per block
		shr	dx,cl		; DX <- number of block
		and	dl,7		; DL <- bit in byte
		mov	cl,dl		; CL <- bit in byte
		mov	al,1		; AL <- bit 0
		shl	al,cl		; AL <- bit mask

; ------------- test block

		test	byte [bx],al	; text bit
		pop	ax		; pop AX (block size)
		jnz	short MemGetSize7 ; block is marked OK

; ------------- invalid memory block

MemGetSize6:	xor	ax,ax		; AX <- invalid memory block

; ------------- pop registers

MemGetSize7:	pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
MemGetSize8:	cmp	ax,1		; check memory block size
MemGetSize9:	ret

; -----------------------------------------------------------------------------
;                           Free memory block
; -----------------------------------------------------------------------------
; INPUT:	AX = block address (can be NULL)
; OUTPUT:	AX = block address, or 0 (NULL) if invalid memory block (CY)
;		CY = invalid memory block
; -----------------------------------------------------------------------------

; ------------- check block address

MemFree:	push	ax		; push AX
		call	MemGetSize	; check memory block
		pop	ax		; pop AX
		jnc	short MemFreeNoCheck ; memory block is OK
		xor	ax,ax		; AX <- 0, invalid memory block
		stc			; set error flag
		ret

; ------------- push registers

MemFreeNoCheck:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI

; ------------- get block size -> AL

		xchg	ax,bx		; BX <- memory block address
		mov	ah,1		; AH <- flag, don't clear block
		mov	di,bx		; DI <- block address
		mov	cl,PAGE_BITS	; CL <- 10, size of 1 KB block
		shr	di,cl		; DI <- offset of descriptor
		add	di,MemSizeTab	; DI <- descriptor address
		mov	al,[di]		; AL <- block size

; ------------- clear size of first block

		dec	byte [di-MemSizeTab+MemUsedTab] ; used counter
		jnz	short MemFree2	; not last block
		mov	ah,0		; AH <- 0, clear block size
		mov	[di],ah		; clear block size
		inc	di		; DI <- next block

; ------------- mark small block

MemFree2:	mov	ch,1		; CH <- 1, only 1 block
		cmp	al,MEM_SIZEKB	; big block?
		jb	short MemFree6	; small block

; ------------- set block size of big block

		or	ah,ah		; clear block?
		jnz	short MemFree4	; don't clear block
		mov	cx,ax		; CL <- block size
		sub	cl,MEM_KB_BIAS+1 ; CL <- number of 1 KB blocks - 1
		jz	short MemFree4	; no other blocks
MemFree3:	mov	[di-MemSizeTab+MemUsedTab],ah ; clear used counter
		mov	[di],ah		; clear block size
		inc	di		; DI <- next block
		loop	MemFree3	; next block

; ------------- mark bits of used KB blocks

MemFree4:	sub	al,MEM_KB_BIAS	; AX <- number of 1 KB blocks
		mov	ch,al		; CH <- number of 1 KB blocks
		mov	al,MEM_SIZEKB	; AL <- 1KB block

; ------------- prepare address of bitmap table -> DI

MemFree6:	mov	ah,0		; AX = block size
		mov	di,ax		; DI <- block size
		shl	di,1		; DI <- offset of table
		mov	di,[di+MemBitMapAddr-2] ; DI <- address of table

; ------------- address in bitmap table -> DI

		mov	cl,al		; AL <- block size
		add	cl,2+3		; CL <- 2 for size, 3 for bit
		mov	ax,bx		; AX <- block address
		shr	ax,cl		; AX <- offset it bitmap table
		add	di,ax		; DI <- address in bitmap table

; ------------- bit mask -> AL, block size -> DX

		mov	ax,bx		; AX <- address
		sub	cl,3		; CL <- bits per block
		xor	dx,dx		; DX <- 0
		shr	ax,cl		; AX <- number of block
		inc	dx		; DX <- 1
		and	al,7		; AL <- bit in byte
		shl	dx,cl		; DX <- size of block
		mov	cl,al		; CL <- bit in byte
		mov	al,1		; AL <- bit 0
		shl	al,cl		; AL <- bit mask

; ------------- unmark blocks

MemFree8:	xor	byte [di],al	; clear one bit
		add	[MemFreeTotal],dx ; total free memory
		sub	[MemUsedTotal],dx ; total used memory
		rol	al,1		; AL <- shift by one bit
		adc	di,byte 0	; DI <- shift address
		dec	ch		; counter of blocks
		jnz	short MemFree8	; next block		

; ------------- pop registers

		pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		clc			; clear error flag
		ret

; -----------------------------------------------------------------------------
;        Get biggest free memory space that can be allocated (only xKB)
; -----------------------------------------------------------------------------
; OUTPUT:	AX = max. size of free memory space
; -----------------------------------------------------------------------------

; ------------- push registers

MemMaxFree:	push	bx		; push BX
		push	dx		; push DX

; ------------- prepare registers

		xor	ax,ax		; AX <- 0, found biggest memory block
		xor	bx,bx		; BX <- 0, offset in table
		xor	dx,dx		; DX <- 0, size accumulator

; ------------- find free memory block

MemMaxFree2:	cmp	byte [MemSizeTab+bx],0 ; free block?
		je	short MemMaxFree4 ; free block
		cmp	dx,ax		; block is bigger ?
		jb	short MemMaxFree3 ; block is smaller
		xchg	ax,dx		; AX <- found biggest block
MemMaxFree3:	mov	dx,-PAGE_SIZE	; DX <- clear accumulator
MemMaxFree4:	add	dx,PAGE_SIZE	; DX <- increase block size
		inc	bx		; BX <- increase table offset
		cmp	bl,PAGE_NUM	; check table end
		jne	short MemMaxFree2 ; next block

; ------------- compare last free block

		cmp	dx,ax		; block is bigger ?
		jb	short MemMaxFree5 ; block is smaller
		xchg	ax,dx		; AX <- found biggest block

; ------------- pop registers

MemMaxFree5:	pop	dx		; pop DX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                        Get memory block module
; -----------------------------------------------------------------------------
; INTPUT:	AX = required size of memory block (0...)
; OUTPUT:	AX = memory block size (MEM_SIZE8,...MEM_SIZEKB...)
; -----------------------------------------------------------------------------

; ------------- push registers

MemModule:	push	bx		; push BX
		push	cx		; push CX

; ------------- small module

		cmp	ax,PAGE_SIZE	; big memory block?
		ja	short MemModule4 ; big memory block

; ------------- small memory block

		xchg	ax,bx		; BX <- required size
		mov	cx,8		; CX <- smallest module reference
		mov	ax,MEM_SIZE8	; AX <- memory block module
MemModule2:	cmp	cx,bx		; check size
		jae	short MemModule8 ; module size is OK
		shl	cx,1		; CX <- increase module size
		inc	ax		; AX <- increase module number
		jmp	short MemModule2 ; try next module

; ------------- big memory block (multiple of KB)

MemModule4:	mov	cl,PAGE_BITS	; CL <- number of bits per page
		add	ax,PAGE_MASK	; AX <- round up to page
		shr	ax,cl		; AX <- number of 1 KB memory blocks
		add	al,MEM_KB_BIAS	; AX <- KB module

; ------------- pop registers

MemModule8:	pop	cx		; pop CX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                        Allocate memory block
; -----------------------------------------------------------------------------
; INTPUT:	AX = required size of memory block (0...)
; OUTPUT:	AX = address of memory block (NC), or 0 (NULL) on error (CY)
;		CY = allocation error
; -----------------------------------------------------------------------------

; ------------- get block module -> AX

MemAlloc:	call	MemModule	; AX <- memory block module

; ------------- push registers

		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- small memory block

		cmp	al,MEM_SIZEKB	; small memory block?
		ja	short MemAlloc6	; large memory block

; ------------- number of memory blocks per one page -> AH

		mov	cl,MEM_SIZEKB	; CL <- max. value
		sub	cl,al		; CL <- number of rotations
		mov	ah,1		; AH <- bit mask
		shl	ah,cl		; AH <- max. number of blocks per 1 KB

; ------------- find page with free blocks of this size

		xor	bx,bx		; BX <- 0, address pointer
		mov	si,MemSizeTab	; SI <- size table
MemAlloc1:	cmp	[si],al		; check page size
		jne	short MemAlloc2	; invalid page size
		cmp	[si-MemSizeTab+MemUsedTab],ah ; some free blocks?
		jb	short MemAlloc4	; found free blocks
MemAlloc2:	inc	si		; SI <- increaze table pointer
		add	bx,PAGE_SIZE	; BX <- shift address pointer
		cmp	si,MemSizeTab+PAGE_NUM ; check table end
		jb	short MemAlloc1	; test next position

; ------------- create new page with this size

		xor	bx,bx		; BX <- 0, address pointer
		mov	si,MemSizeTab	; SI <- size table
MemAlloc3:	cmp	byte [si],0	; free page ?
		je	short MemAlloc4	; found free page
		inc	si		; SI <- increaze table pointer
		add	bx,PAGE_SIZE	; BX <- shift address pointer
		cmp	si,MemSizeTab+PAGE_NUM ; check table end
		jb	short MemAlloc3	; test next position
		jmp	short MemAlloc8	; allocation error

; ------------- prepare address of bitmap table -> SI
		
MemAlloc4:	mov	ah,0		; AX = block size
		mov	si,ax		; SI <- block size
		shl	si,1		; SI <- offset of table
		mov	si,[si+MemBitMapAddr-2] ; SI <- address of table

; ------------- address in bitmap table -> SI

		mov	cl,al		; AL <- block size
		add	cl,2+3		; CL <- 2 for size, 3 for bit
		mov	dx,bx		; DX <- block address
		shr	dx,cl		; DX <- offset it bitmap table
		add	si,dx		; SI <- address in bitmap table

; ------------- block size -> DX

		sub	cl,3		; CL <- bits per block
		xor	dx,dx		; DX <- 0
		inc	dx		; DX <- 1
		shl	dx,cl		; DX <- size of block

; ------------- find free block (from address BX)

		mov	ah,1		; AH <- bit mask
MemAlloc5:	test	[si],ah		; test if block is free
		jz	short MemAlloc7	; found free block
		add	bx,dx		; BX <- shift address
		rol	ah,1		; AH <- rotate mask
		adc	si,byte 0	; SI <- shift address
		jmp	short MemAlloc5	

; ------------- large memory block (2 KB and more)

MemAlloc6: 	xor	bx,bx		; BX <- 0, address pointer
		mov	si,MemSizeTab	; SI <- size table
		mov	dl,al		; DL <- required size
		sub	dl,MEM_KB_BIAS+1 ; DL <- number of KB blocks - 1
MemAlloc62:	cmp	byte [si],0	; free page?
		jne	short MemAlloc68 ; no

; ------------- check length of block string

		push	si		; push SI
		mov	dh,dl		; DH <- required size in KB blocks
MemAlloc64:	inc	si		; increase table pointer
		cmp	byte [si],0	; free page?
		jne	short MemAlloc66 ; invalid page
		dec	dh		; counter of KB blocks
		jnz	short MemAlloc64 ; try next block
MemAlloc66:	pop	si		; pop SI
		je	short MemAlloc7	; found suiting block

; ------------- try next KB block

MemAlloc68:	inc	si		; SI <- increase table pointer
		add	bx,PAGE_SIZE	; BX <- shift address pointer
		cmp	si,MemSizeTab+PAGE_NUM ; check table end
		jb	short MemAlloc62 ; test next position
		jmp	short MemAlloc8	; allocation error

; ------------- mark this block as used

MemAlloc7:	call	MemMark		; mark block as used
		xchg	ax,bx		; AX <- block address
		jmp	short MemAlloc9

; ------------- error

MemAlloc8:	xor	ax,ax		; AX <- 0, allocation error

; ------------- pop registers

MemAlloc9:	pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		cmp	ax,1		; check memory address
		ret

; -----------------------------------------------------------------------------
;                          Resize memory block
; -----------------------------------------------------------------------------
; INTPUT:	AX = required new size of memory block (0 = delete block)
;		BX = address of memory block (NULL = create new block)
; OUTPUT:	AX = new address of memory block (NC), or 0 (NULL) on error (CY)
;		CY = allocation error
; -----------------------------------------------------------------------------

; ------------- new memory block

MemResize:	or	bx,bx		; new memory block?
		jnz	short MemResize2 ; not new memory block
		jmp	MemAlloc	; allocate new memory block

; ------------- delete memory block

MemResize2:     or	ax,ax		; delete block?
		jnz	short MemResize3 ; size > 0
		mov	ax,bx		; AX <- address of memory block
		call	MemFree		; delete memory block
		jc	short MemResize22 ; error
		xor	ax,ax		; AX <- destroy old memory address
MemResize22:	ret

; ------------- push registers

MemResize3:	push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		xchg	ax,si		; SI <- required new size of block

; ------------- get size of old memory block -> AX, DI

		mov	ax,bx		; AX <- address of memory block
		call	MemGetSize	; check old memory block
		jc	short MemResize8 ; invalid memory block
		mov	di,ax		; DI <- old size of memory block

; ------------- get old module of memory block -> DL

		call	MemModule	; get memory block module
		xchg	ax,dx		; DL <- old module of memory block

; ------------- get new module of memory block -> DH

		mov	ax,si		; AX <- required new size
		call	MemModule	; get memory block module
		mov	dh,al		; DH <- new module of memory block

; ------------- check if size need to be changed

		cmp	dh,dl		; size changed?
		je	short MemResize7 ; size not changed
		ja	short MemResize5 ; size needs to expand

; ------------- decreasing by 1 degree is OK

		dec	dx		; DL <- old module - 1
		cmp	dh,dl		; size decreased by 1 degree?
		je	short MemResize7 ; size needs not be changed
		inc	dx		; DL <- old module

; ------------- decreasing small memory blocks needs to be copied

		cmp	dl,MEM_SIZEKB	; small old module?
		jb	short MemResize4 ; needs to copy
		cmp	dh,MEM_SIZEKB	; small new module?
		jae	short MemResize55 ; decrease large memory block

; ------------- decrease with copy

MemResize4:	mov	cx,si		; CX <- required new size
		inc	cx		; CX <- new size + 1
		jmp	short MemResize62 ; resize block

; ------------- expanding small memory blocks needs to be copied

MemResize5:	cmp	dl,MEM_SIZEKB	; small old module?
		jb	short MemResize6 ; needs to copy
		cmp	dh,MEM_SIZEKB	; small new module?
		jb	short MemResize6 ; needs to copy

; ------------- expand large memory block - check available space

		mov	ax,bx		; AX <- memory block address
		mov	cl,PAGE_BITS	; CL <- 10, size of 1 KB block
		shr	ax,cl		; AX <- offset of descriptor
		add	al,dl		; AL <- old module
		sub	al,MEM_KB_BIAS	; AL <- end of old block
		add	ax,MemSizeTab	; AX <- descriptor address
		xchg	ax,bx		; AX <- address, BX <- descriptor
MemResize52:	cmp	byte [bx],0	; free block?
		jne	short MemResize58 ; not free block
		inc	bx		; BX <- next size descriptor
		inc	dx		; DL <- increase old module
		cmp	dl,dh		; new size reached?
		jne	short MemResize52 ; try next block
		xchg	ax,bx		; BX <- old address

; ------------- Re-mark large memory block

MemResize55:	mov	ax,bx		; AX <- address of memory block
		call	MemFreeNoCheck	; unmark old block
		mov	al,dh		; AL <- required new size
		call	MemMark		; mark memory block BX
		jmp	short MemResize7 ; reuse old address

; ------------- expand with copy

MemResize58:	xchg	ax,bx		; BX <- old address
MemResize6:	mov	cx,di		; CX <- old size of memory block
MemResize62:	xchg	ax,si		; AX <- required new size
		call	MemAlloc	; allocate new memory block
		jc	short MemResize8 ; memory error
		mov	si,bx		; SI <- address of old block
		shr	cx,1		; CX <- old size / 2
		mov	di,ax		; DI <- address of new block
		rep	movsw		; copy old content of block
		xchg	ax,bx		; AX <- old address, BX <- new address
		call	MemFreeNoCheck	; free old memory block

; ------------- old memory block suits OK

MemResize7:	xchg	ax,bx		; AX <- old address of memory block
		clc			; clear error flag

; ------------- pop registers

MemResize8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                          Duplicate memory block
; -----------------------------------------------------------------------------
; INTPUT:	BX = address of memory block (NULL = create new block)
; OUTPUT:	AX = address of new memory block (NC) or 0 (NULL) on error (CY)
;		CY = allocation error
; -----------------------------------------------------------------------------

; ------------- push registers

MemDup:		push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- get size of memory block -> AX, CX

		mov	ax,bx		; AX <- block address
		call	MemGetSize	; get size of memory block
		jc	short MemDup9	; invalid memory block
		mov	cx,ax		; CX <- size of memory block

; ------------- create new memory block

		call	MemAlloc	; allocate new memory block
		jc	short MemDup9	; memory error

; ------------- copy block content (it clears CF, block size is even)

		mov	si,bx		; SI <- address of old block
		mov	di,ax		; DI <- address of new block
		shr	cx,1		; CX <- size of memory block / 2
		rep	movsw		; copy block content

; ------------- pop registers

MemDup9:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                            Constant data
; -----------------------------------------------------------------------------

		CONST_SECTION

; ------------- address of bit map tables

		align	2, db 0
MemBitMapAddr:	dw	MemBitMap8	; 8 byte blocks
		dw	MemBitMap16	; 16 byte blocks
		dw	MemBitMap32	; 32 byte blocks
		dw	MemBitMap64	; 64 byte blocks
		dw	MemBitMap128	; 128 byte blocks
		dw	MemBitMap256	; 256 byte blocks
		dw	MemBitMap512	; 512 byte blocks
		dw	MemBitMap1K	; 1KB blocks

; -----------------------------------------------------------------------------
;                            Uninitialised data
; -----------------------------------------------------------------------------

		DATA_SECTION

; ------------- memory statistic

		align	2, resb 1
MemFreeTotal:	resw	1		; total free memory
MemUsedTotal:	resw	1		; total used memory

; ============= tables (reinitialised to 0)

TablesStart:				; start of tables to clear

; ------------- table of block sizes of blocks in pages, 0=free

		align	2, resb 1
MemSizeTab:	resb	PAGE_NUM	; 0=free, 1..7=8..512 bytes, 8..=in KB

; ------------- table of number of allocated blocks in pages, 0=free

MemUsedTab:	resb	PAGE_NUM	; 0=free, ... max. 128 for 8B blocks

; ------------- bit map of used blocks, 1=used (uses 2 KB of memory)

		align	2, resb 1
MemBitMap:
MemBitMap8:	resb	1024		; 8 byte blocks
MemBitMap16:	resb	512		; 16 byte blocks
MemBitMap32:	resb	256		; 32 byte blocks
MemBitMap64:	resb	128		; 64 byte blocks
MemBitMap128:	resb	64		; 128 byte blocks
MemBitMap256:	resb	32		; 256 byte blocks
MemBitMap512:	resb	16		; 512 byte blocks
MemBitMap1K:	resb	8		; 1KB blocks

		align	2, resb 1
TablesEnd:				; end of tables to clear
