#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>

#define __MM__
#include "MemoryManager.h"

char* GetPageFrame(void){
	int byte, bit;
	int page = -1;

	for (byte = 0; byte < RAM_MAXPAGE / 8; byte++)
		if (mem_bitmap[byte] != 0xFF)
			for (bit = 0; bit < 8; bit++)
				if (!(mem_bitmap[byte] & (1 << bit))) {
					page = 8 * byte + bit;
					SetPageFrameUsed(page);
					return (char *) (page * PAGESIZE);
				}
	return (char *) -1;
}

void InitMM(void){
	uint32_t page_addr;
	int i, pg;

	for (pg = 0; pg < RAM_MAXPAGE / 8; pg++)
		mem_bitmap[pg] = 0;

	for (pg = PAGE(0x0); pg < PAGE(0x20000); pg++) 
		SetPageFrameUsed(pg);

	for (pg = PAGE(0xA0000); pg < PAGE(0x100000); pg++) 
		SetPageFrameUsed(pg);

	pd0 = (uint32_t*) GetPageFrame();
	pt0 = (uint32_t*) GetPageFrame();

	pd0[0] = (uint32_t) pt0;
	pd0[0] |= 3;
	for (i = 1; i < 1024; i++)
		pd0[i] = 0;

	page_addr = 0;
	for (pg = 0; pg < 1024; pg++) {
		pt0[pg] = page_addr;
		pt0[pg] |= 3;
		page_addr += 4096;
	}

	asm("	mov %0, %%eax \n \
		mov %%eax, %%cr3 \n \
		mov %%cr0, %%eax \n \
		or %1, %%eax \n \
		mov %%eax, %%cr0"::"m"(pd0), "i"(PAGING_FLAG));
}

uint32_t *PdCreate(uint32_t * code_phys_addr, unsigned int code_size)
{
	uint32_t *pd, *pt;
	uint32_t i, j;
	uint32_t pages;

	pd = (uint32_t*) GetPageFrame();
	for (i = 1; i < 1024; i++)
		pd[i] = 0;

	pd[0] = pd0[0];
	pd[0] |= 3;

	if (code_size % PAGESIZE)
		pages = code_size / PAGESIZE + 1;
	else
		pages = code_size / PAGESIZE;

	for (i = 0; pages; i++) {
		pt = (uint32_t*) GetPageFrame();

		pd[(USER_OFFSET + i * PAGESIZE * 1024) >> 22] = (uint32_t) pt;
		pd[(USER_OFFSET + i * PAGESIZE * 1024) >> 22] |= 7;

		for (j = 0; j < 1024 && pages; j++, pages--) {
			pt[j] = (uint32_t) (code_phys_addr + i * PAGESIZE * 1024 + j * PAGESIZE);
			pt[j] |= 7;
		}
	}

	return pd;
}