;; File: pmm.inc
	;;
	;; Our PMM is just a linked list of page-aligned size blocks. 
	;;
	;; > ┌─────┐      ┌─────┐       
	;; > │size │  ┌──►│size │       
	;; > │next ├──┘   │next ├────► 0
	;; > └─────┘      └─────┘       
	;;

;;; MARK: macros and structs

	;; Constant: PMM_VIRT_TEMP
PMM_VIRT_TEMP = 0xD0000000
PMM_BITMAP_USED = 0x0
PMM_BITMAP_FREE = 0x1

	;; Macro: PAGE_ALIGN_UP reg
macro PAGE_ALIGN_UP reg {
	add reg, PAGE_SIZE - 1
	and reg, -PAGE_SIZE
}

	;; Macro: PAGE_ALIGN_DOWN reg
macro PAGE_ALIGN_DOWN reg {
	and reg, -PAGE_SIZE
}
	
	;; Struc: PMMFreeRange
struc PMMFreeRange {
	.size dd ?
	.next dd ?
}
DEFN PMMFreeRange

;;; MARK: private functions

	;; Function: _pmm_map_temp_page
	;;
	;; In:
	;;   EAX - physical address to map at <PMM_VIRT_TEMP>
_pmm_map_temp_page:
	mov ecx, MM_KERNEL_PTE_BASE + (PMM_VIRT_TEMP shr 12) * 4
	or eax, 0x3
	mov [ecx], eax

if CONFIG_COMPAT_I386
	mov eax, 0x40000 + (1023 * PAGE_SIZE)
	mov cr3, eax
else
	mov eax, PMM_VIRT_TEMP
	invlpg [eax]
end if
	ret

_pmm_unmap_temp_page:
	mov ecx, MM_KERNEL_PTE_BASE + (PMM_VIRT_TEMP shr 12) * 4
	mov dword [ecx], 0

if CONFIG_COMPAT_I386
	mov eax, 0x40000 + (1023 * PAGE_SIZE)
	mov cr3, eax
else
	mov eax, PMM_VIRT_TEMP
	invlpg [eax]
end if
	ret

	;; Function: _pmm_init_region
	;;
	;; Add new memory region to the linked list.
	;;
	;; In:
	;;    EAX - Start
	;;    EDX - End
	;;
_pmm_init_region:
	push ebp
	mov ebp, esp

	push edi
	push esi

	mov edi, eax
	mov esi, edx

	push edx
	push eax
	mov eax, szMsgPmmFreeRange
	call klog
	pop eax
	pop edx

	call _pmm_map_temp_page

	xor ecx, ecx
	mov [PMM_VIRT_TEMP], esi
	mov [PMM_VIRT_TEMP + PMMFreeRange.next], ecx
	mov ecx, [pPmmFreeListHead]
	or ecx, ecx
	jz @f
	mov [PMM_VIRT_TEMP + PMMFreeRange.next], ecx
@@:
	mov [pPmmFreeListHead], edi

	call _pmm_unmap_temp_page

	pop edi
	pop esi

	leave
	ret

_pmm_init_low_memory:
	push ebp
	mov ebp, esp

	mov eax, [stBootInfo.low_mem]
	push eax
	xor eax, eax
	push eax
	mov eax, szMsgPmmFreeRange
	call klog
	add esp, 8

	; clear bitmap
	push edi
	xor eax, eax
	mov ecx, 8
	mov edi, au32PmmLowMemBitmap
	rep stosd
	
	mov edi, au32PmmLowMemBitmap
	xor eax, eax
	xor ecx, ecx
@@:
	add eax, PAGE_SIZE
	cmp eax, [stBootInfo.low_mem]
	jg @f
	mov ebx, 1
	shl ebx, cl
	or [edi], ebx
	inc ecx
	cmp ecx, 32
	jb @b
	xor ecx, ecx
	add edi, 4
	jmp @b

@@:
	pop edi

	leave
	ret

;;; MARK: public functions

	;; Function: pmm_alloc
	;; 
	;; In:
	;;    EAX - size in page
	;;
	;; Out:
	;;    EAX - first page physical address, 0 on error
	;;
pmm_alloc:
	mov eax, [pPmmFreeListHead]
	or eax, eax
	jnz @f
	mov edx, ENOMEM
	ret	; no memory left :'(
@@:
	call _pmm_map_temp_page

	call _pmm_unmap_temp_page

	ret

	;; Function: pmm_alloc_page
	;;
	;; Out:
	;;    EAX - page physical address, 0 on error
pmm_alloc_page:
	mov eax, 1
	call pmm_alloc
	ret

	;; Function: pmm_free
	;; 
	;; In:
	;;    EAX - Start
	;;    EDX - End
pmm_free:
	ret

	;; Function: pmm_init
	;; 
	;; Out:
	;;    EAX - return -1 on error
pmm_init:
	mov eax, szMsgPmmInit
	call klog

	call _pmm_init_low_memory

if CONFIG_TRACE_PMM
	mov byte [cga_color], CGA_COLOR_FG_BLUE
	push dword [au32PmmLowMemBitmap + 28]
	push dword [au32PmmLowMemBitmap + 24]
	push dword [au32PmmLowMemBitmap + 20]
	push dword [au32PmmLowMemBitmap + 16]
	push dword [au32PmmLowMemBitmap + 12]
	push dword [au32PmmLowMemBitmap + 8]
	push dword [au32PmmLowMemBitmap + 4]
	push dword [au32PmmLowMemBitmap]

	mov eax, szTracePmmBitmap

	TRACE

	add esp, 32

end if

	mov eax, kend - KERNEL_VIRT_BASE
	PAGE_ALIGN_UP eax

	mov edx, 0x400000
	sub edx, eax

	jle @f
	cmp edx, PAGE_SIZE
	jle @f

	mov edx, 0x400000

	call _pmm_init_region

@@:
	mov eax, 0x800000
	mov edx, [stBootInfo.high_mem]
	PAGE_ALIGN_DOWN edx
	call _pmm_init_region

	xor eax, eax ; TODO: check if enough memory and so on
	ret

;;; MARK: variables

	;; Variable: pPMMFreeListHead
	;; Hold first free list entry physical address
pPmmFreeListHead dd 0

au32PmmLowMemBitmap rd 8

szMsgPmmInit      db "PMM: initialize", 0
szMsgPmmFreeRange db "PMM: add free memory region %x - %x", 0
szErrorNoMemLeft  db "Error(PMM): no free memory left", 0
if CONFIG_TRACE_PMM
szTracePmmBitmap db "Trace(PMM): low memory bitmap", CR, LF, \
	"    %x", CR, LF, "    %x", CR, LF, "    %x", CR, LF, "    %x", CR, LF, \
	"    %x", CR, LF, "    %x", CR, LF, "    %x", CR, LF, "    %x", 0
end if