#include "clib.h"
#include "phmem.h"

#define MEMORY	32768

/*
 * qui c'e` il gestore delle pagine di memoria.
 * ad ogni pagina e` associato un contatore che indica da quanti task
 * e` utilizzata. quando il contatore e` 0 la pagina e` libera.
 * la massima quantita` di memoria e` determinata dalla costante
 * MAX_PAGES. nota che indica il numero massimo di pagine allocabili
 * a partire da FIRST_PAGE, che e` la prima pagina utilizzabile, mentre
 * LAST_PAGE e` l'ultima pagina utilizzabile.
 *
 * esempio di come viene caricato il tutto :
 *
 *
 *
 * ____________ indirizzo limite per l'heap, massimo IBMH + #MEMORY
 * | HEAP   |
 * |        |
 * |________|__   Indirizzo Base della Memoria Heap (IBMH)
 * | kernel |
 * |________|___ indirizzo base del kernel
 *
 *   .....  ______________ eventuali programmi che usano memoria > 1Mb,
 * | ?????  |              come ad esempio smartdrv
 * |___________ 110000h
 * | HMA    |
 * |________|__ 100000h
 * |        |
 * |bios/video/UMB
 * |________|__ 0A0000h
 * |  dos   |
 * |________|__ 000000h
 *
 *
 *
 * NOTA: la prima pagina di memoria non puo` avere indirizzo < di 110000h
 *
 * NOTA: queste funzioni non devono essere interrotte dallo scheduler
 *
 *
 * LREV 23/sept/97
 */



#define MAX_PAGES (MEMORY/4)
#define LAST_PAGE  last_page
#define FIRST_PAGE first_page
#define MEM_START (FIRST_PAGE * 4096)

// array, tiene taccia delle pagine libere/occupate
// allineato a cache-line
static s8 	mem_pages[MAX_PAGES];// _align(32);

// numero di pagine UTILIZZABILI (che e` sempre <= MAX_PAGES)
static int 	num_pages;
// PRIMA pagina utilizzabile
static int      last_page;
// indirizzo ULTIMA pagina UTILIZZABILE
static int      first_page;


phaddr_t alloc_free_pages(int num)
{
	int count = num, i;

	for (i = 0; i < num_pages; i++) {
        	if (mem_pages[i] == 0) {      // se e` una pagina libera
                	if (--count == 0) {
                        	int j;

                                for (j = 0; j < num; j++) {
                                	mem_pages[i-j] = 1; // marca come usate
                                }
                                i = (i - num + 1) * 4096+MEM_START;
                                ASSERT((i shr 12) >= FIRST_PAGE);
                                ASSERT((i shr 12) <= LAST_PAGE);

                		return (phaddr_t)i;
                        }
                } else count = num;
        }
        WARNING("return NULL");
        return 0;
}

phaddr_t alloc_free_page(void)
{
	int i;
	for (i = 0; i < num_pages; i++)
        	if (mem_pages[i] == 0) {
                	mem_pages[i] = 1;
                        i = i*4096+MEM_START;
        		return (phaddr_t)i;
                }
        WARNING("return NULL");
        return 0;
}

phaddr_t alloc_pages_at(phaddr_t base, int num)
{
	int page = (u32)base >> 12, i;

        for (i = 0; i < num; i++, page++) {
        	if (page >= FIRST_PAGE && page <= LAST_PAGE) {
                        mem_pages[page - FIRST_PAGE]++;
                        ASSERT(mem_pages[page - FIRST_PAGE] > 0);
                } else {
                        WARNING("return NULL");
                	return 0;
                }
        }
        return base;
}

int free_pages(phaddr_t a, int num)
{
	int page = (u32)a >> 12, i;

	for (i = 0; i < num; i++, page++) {
        	if ((page >= FIRST_PAGE) && (page <= LAST_PAGE)) {
                        if (mem_pages[page - FIRST_PAGE] > 0)
                        	mem_pages[page - FIRST_PAGE]--;
                        return 0;
                }  else {
                	WARNING("EFAUL in page %xh", page);
                	return EFAULT;
                }
        }
        return 0; /* se num == 0 */
}

int free_page(phaddr_t a)
{
	int page = (u32)a >> 12;
       	if (page >= FIRST_PAGE && page <= LAST_PAGE) {
        	if (mem_pages[page - FIRST_PAGE] > 0)
                         --(mem_pages[page - FIRST_PAGE]);
                return 0;
        }
    	WARNING("EFAUL in page %xh", page);
	return EFAULT;
}


int ph_memory_setup(phaddr_t heap_base, int npages)
{
        FIRST_PAGE = (u32)(heap_base + 0xFFF) >> 12; // arrotonda per eccesso
        num_pages = npages;
        if (num_pages > MAX_PAGES) num_pages = MAX_PAGES;
        LAST_PAGE = (FIRST_PAGE + num_pages) - 1;
        ASSERT(FIRST_PAGE >= 272);

        DEBUG_VERBOSE("first page %xh ", FIRST_PAGE);
        DEBUG_VERBOSE("last page %xh ", LAST_PAGE);
        DEBUG_VERBOSE("num_pages %xh\n", num_pages);

        return num_pages;
}

int get_free_pages(void)
{
	int f = 0, i;
	for (i = 0; i < num_pages; i++)
        	if (mem_pages[i] == 0) f++;

        return f;
}

int get_total_pages(void)
{
	return num_pages;
}

phaddr_t get_fault_address(void)
{
	return (phaddr_t)(LAST_PAGE shl 12) + 4096;
}