/*
================================ COPYRIGHT ================================
The contents of this file are subject to the VTech Informations Ltd. License
of VT-OS Ver. 1.1 operating system (the "License"); you may not use this 
file except in compliance with the License.  

Software distributed under the License is distributed on an "AS IS" basis, 
WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License 
for the specific language governing rights and limitations under the License.
  
The Original Code is VT-OS Ver. 1.1 Operating System, released 
on October 1st, 1999
	
The Initial Developer of the Original Code is VTech Informations Ltd.  All 
codes are Copyright (C) VTech Informations Ltd. 1999.  All Rights Reserved.
===========================================================================
*/

/*
===========================================================================
File        :   mmu.c
Author(s)   :   Kenny Ng
Company     :   VTech Informations Ltd.
Project     :   Helio 
Date:	    :   October 1st, 1999
Purpose:	:   Memory Manager
Revision    :   1.1
Note        :   01/07/98    : Totally rewrite all the routines
25/10/98    : Bug fix many routines,
Split mmu.c to mmu.c and datamgr.c for faster complie
03/11/98    : MemoryInstallProg() bug fix                  
10/11/98    : Add function MemoryTotalFree()
03/12/98    : Add function MemoryRegFlashProg()
Add function MemoryClearAppDM()
05/01/99    : Add Intertupt VM support	
===========================================================================
*/              

#include "stdafx.h"
#include "mmu.h"

#undef DEBUG
#define NEW_BOOT_LOADER                         /* define this for bootloader version 4.1 and later */
#undef MMU_ACTUAL_COUNT                         /* define this for MUCH slower but more accurate free block count */

#ifdef UpdateMATCRC
#undef UpdateMATCRC
#endif

#define UpdateMATCRC(x)			/* skip CRC checking */

#ifdef PC_SIM
BYTE sim_mem[4][PC_SIM_MEM_SIZE];		/* pointer of PC simulated memory */
BYTE DM_MEM[16384];
BYTE OS_MEM[16384];
UWORD mem_alloc_start[10];
unsigned char  run_mode;
unsigned long *  int_page;
long  dm_os_init;
#define RUN_MODE_NORMAL	1
#define RUN_MODE_MODE1	2
signed char  user_mem_init;   
#else
extern mem_alloc_start;
#endif
UBYTE sys_crashed;

#define SetFragment(attribute)  (attribute |= 0x0800) /* upate mat after the operation */
#define ClrFragment(attribute)  (attribute &= 0xF7FF) /* upate mat after the operation */

MemBankMap mem_bank_map[TOTAL_SLOT][MAX_BANK_PER_SLOT];

MatInfoList  mat_info[TOTAL_SLOT];
USHORT dmos_first_block;
USHORT dmuser_first_block;
UBYTE dmuser_slot;
UBYTE dmos_slot;
UWORD last_found_free_block;
extern UBYTE run_mode;
extern UWORD *app_page, *sys_page, *int_page;

MAT *fmat = NULL;

UWORD *user_dynamic_mem_address;        /* start address of dynamic memory pool */
WORD user_dynamic_mem_size;
UWORD *os_dynamic_mem_address;        /* start address of dynamic memory pool */
WORD os_dynamic_mem_size;
UWORD *sys_page_table;
UWORD *app_page_table;
UWORD *int_page_table;
UWORD MemDataCRC;
UWORD mmu_total_free_block;
UWORD total_sys_mem;

extern _DBCache dbcache;

UWORD SBlockAddr(UWORD sblock)
{
	UWORD i;
	USHORT block_num = sblock & 0xFFFF;
	USHORT mat_index = sblock >> 16;
	MemBankMap *matb = mem_bank_map[mat_index];
	
	if(block_num > mat_info[mat_index].total_entry) return 0;
	
	i = MAX_BANK_PER_SLOT-1;
	do
	{  // find block start bank
		if((*matb).mat_start_index > block_num)	break;
		matb++;
	} while (--i);
	
	matb--;
	return ((*matb).base_addr + (block_num - (*matb).mat_start_index) * BLOCK_SIZE);
}


UWORD BlockAddr(MatInfoList* mat, USHORT block_num)
{
	UWORD i;
	MemBankMap *matb = mem_bank_map[0];
	
	if(block_num > mat->total_entry) return 0;
	
	i = MAX_BANK_PER_SLOT-1;
	do
	{  // find block start bank
		if((*matb).mat_start_index > block_num)	break;
		matb++;
	} while (--i);
	
	matb--;
	return ((*matb).base_addr + (block_num - (*matb).mat_start_index) * BLOCK_SIZE);
}

UWORD BlockAddr1(MatInfoList mat, USHORT block_num)
{
	UWORD i;
	MemBankMap *matb = mem_bank_map[0];
	
	if(block_num > mat.total_entry)	return 0;
	
	i = MAX_BANK_PER_SLOT-1;
	do
	{  // find block start bank
		if((*matb).mat_start_index > block_num)	break;
		matb++;
	} while (--i);
	
	matb--;
	return ((*matb).base_addr + (block_num - (*matb).mat_start_index) * BLOCK_SIZE);
}


void _valid_ptr(UWORD *cur_sblock, BYTE **ptr, UWORD inc, RecordHeader *rec_ptr)
{
	USHORT next;
	UWORD offset = BLOCK_SIZE - ((UWORD)*ptr % BLOCK_SIZE);
	
	if(inc >= offset) {
		next = SBlockBlock(*cur_sblock);
        while (inc >= offset) {
			MemoryNextBlock(&mat_info[SBlockMat(*cur_sblock)], next, &next);
			inc -= offset;
			offset = BLOCK_SIZE - RECHEADER_SIZE;
			if(rec_ptr)	{
				*ptr = (BYTE*) SBlockAddr(MakeSBlock(SBlockMat(*cur_sblock), next));
				*(RecordHeader*)(*ptr) = *rec_ptr;
				((RecordHeader*)(*ptr))->size = (inc >= offset)? BLOCK_SIZE: inc+RECHEADER_SIZE;
			}
		}
		*cur_sblock = MakeSBlock(SBlockMat(*cur_sblock), next);
		*ptr = (BYTE*) (SBlockAddr(*cur_sblock) + RECHEADER_SIZE);
	}
	*ptr += inc;
}

/*
Function         : MemoryBuildMemBankMap
Purpose          : Build the Memory bank map
Scope            : Internal
Input Parameters : None
Output Parameters: None
Return	    : None
ERR_MEM_INV_PARAM - invalid slot number
ERR_NO_MEM	- no mem for that slot available to
Comment          : This function will return DEV_RAM on slot 1, DEV_NONE on others
*/
void MemoryBuildMemBankMap()
{
#ifdef PC_SIM
	UWORD i;
	for(i=0;i<4;i++)
	{
		mem_bank_map[0][i].bank_size = PC_SIM_MEM_SIZE;
		mem_bank_map[0][i].base_addr = (UWORD) &sim_mem[i][0];
		mem_bank_map[0][i].base_addr +=  BLOCK_SIZE - mem_bank_map[0][i].base_addr % BLOCK_SIZE;  /* align to Block boundary */
		mem_bank_map[0][i].bank_size -= BLOCK_SIZE - mem_bank_map[0][i].base_addr % BLOCK_SIZE;  
		mem_bank_map[0][i].bank_size = mem_bank_map[0][i].bank_size - mem_bank_map[0][i].bank_size % BLOCK_SIZE; /* make to 4K multiple */
		mem_bank_map[0][i].mat_start_index = 0xFFFF;
	}
	mem_bank_map[0][i].bank_size = 0; // end of bank map
	mem_bank_map[0][i].mat_start_index = 0xffff;
#endif
#ifdef PR31700
#ifdef OLD_BOOT_LOADER
	mem_bank_map[0][0].bank_size = 0x1f0000 - (UWORD) (((UWORD)&mem_alloc_start) & 0x00ffffff) - 16 * BLOCK_SIZE;
	mem_bank_map[0][0].base_addr = (UWORD) &mem_alloc_start + 10 *BLOCK_SIZE;
	mem_bank_map[0][0].base_addr +=  BLOCK_SIZE - mem_bank_map[0][0].base_addr % BLOCK_SIZE;  /* align to Block boundary */
	mem_bank_map[0][0].bank_size -= BLOCK_SIZE - mem_bank_map[0][0].base_addr % BLOCK_SIZE;  
	mem_bank_map[0][0].bank_size = mem_bank_map[0][0].bank_size - mem_bank_map[0][0].bank_size % BLOCK_SIZE; /* make to 4K multiple */
	mem_bank_map[0][0].mat_start_index = 0xFFFF;
	
	mem_bank_map[0][1].bank_size = 0x821fffff - 0x82000000;
	mem_bank_map[0][1].base_addr = 0x82000000;
	mem_bank_map[0][1].base_addr +=  BLOCK_SIZE - mem_bank_map[0][1].base_addr % BLOCK_SIZE;  /* align to Block boundary */
	mem_bank_map[0][1].bank_size -= BLOCK_SIZE - mem_bank_map[0][1].base_addr % BLOCK_SIZE;  
	mem_bank_map[0][1].bank_size = mem_bank_map[0][1].bank_size - mem_bank_map[0][1].bank_size % BLOCK_SIZE; /* make to 4K multiple */
	mem_bank_map[0][1].mat_start_index = 0xFFFF;
	
	mem_bank_map[0][2].bank_size = 0; // end of bank map
	mem_bank_map[0][2].mat_start_index = 0xffff;
	
#endif
#ifdef NEW_BOOT_LOADER
	UWORD bank_count = 0;
	UWORD *meminfo = (UWORD*) MEM_INFO_ADDR + 1; /* bank 0 size */
	
												 /*  0x80000014     Bank 0 addr
												 0x80000018     Bank 0 size
												 0x8000001c     Bank 1 addr
												 0x80000020     Bank 1 size
												 .		.
												 .		.
												 0x800000??     Bank ? addr  <--if content = 0, memory map end
	*/
	
	/* addr of bank 0 is fixed, since the info can obtain from linker
	   and some of the memory in bank 0 is used by system */
	
	total_sys_mem = *meminfo;
	mem_bank_map[0][0].bank_size = *meminfo++ - (UWORD) (((UWORD)&mem_alloc_start) & 0x00ffffff) - 15 * BLOCK_SIZE;
	mem_bank_map[0][0].base_addr = (UWORD) &mem_alloc_start + 10 *BLOCK_SIZE;
	mem_bank_map[0][0].base_addr +=  BLOCK_SIZE - mem_bank_map[0][0].base_addr % BLOCK_SIZE;  /* align to Block boundary */
	mem_bank_map[0][0].bank_size -= BLOCK_SIZE - mem_bank_map[0][0].base_addr % BLOCK_SIZE;  
	mem_bank_map[0][0].bank_size = mem_bank_map[0][0].bank_size - mem_bank_map[0][0].bank_size % BLOCK_SIZE; /* make to 4K multiple */
	mem_bank_map[0][0].mat_start_index = 0xFFFF;
	bank_count = 1;
	
	while(*meminfo)	/* until start addr = 0 */
	{
		mem_bank_map[0][bank_count].base_addr = *meminfo++;
		mem_bank_map[0][bank_count].bank_size = *meminfo++;
		total_sys_mem += mem_bank_map[0][bank_count].bank_size;
		mem_bank_map[0][bank_count].base_addr +=  BLOCK_SIZE - mem_bank_map[0][bank_count].base_addr % BLOCK_SIZE;  /* align to Block boundary */
		mem_bank_map[0][bank_count].bank_size -= BLOCK_SIZE - mem_bank_map[0][bank_count].base_addr % BLOCK_SIZE;  
		mem_bank_map[0][bank_count].bank_size = mem_bank_map[0][bank_count].bank_size - mem_bank_map[0][bank_count].bank_size % BLOCK_SIZE; /* make to 4K multiple */
		mem_bank_map[0][bank_count++].mat_start_index = 0xFFFF;
	}
	
	
	
	mem_bank_map[0][bank_count].bank_size = 0; // end of bank map
	mem_bank_map[0][bank_count].mat_start_index = 0xffff;
#endif	
#endif
}

Err MemoryMemInfo(USHORT slot_num, UWORD *base_addr, UWORD *size)
{
	return FALSE;     
};

void *MemoryGetMatPtr(USHORT blk)
{
	return (void*) &mat_info[blk];
}


/*
Function         : MemoryBuildMAT
Purpose          : Build the MAT
Scope            : Internal
Input Parameters : None
Output Parameters: None
Return	    : TRUE - success
ERR_MEM_INV_PARAM - invalid slot number
ERR_NO_MEM	- no mem for that slot available to
Comment          : This function will return DEV_RAM on slot 1, DEV_NONE on others
*/
Err MemoryBuildMAT(void)
{
	UWORD i,j,k;
	UWORD total_mat_entry = 0;
	UWORD total_mat_memory = 0;
	USHORT mat_occupied_block;
	
	/*
    Physical Address	       Map                Size
			 -----------------
			 x : multi of block size  |     MAT        |  > multiple of BLOCK_SIZE
			 ------------------
			 y	 |     Block 0    |  \
			 ------------------   |
			 y + BLOCK_SIZE	 |     Block 1    |   |
			 ------------------    >  BLOCK_SIZE
			 |                |   |
			 ------------------   |
			 |   Last Block   |   /
			 ------------------
			 
			   y : x + (no. of block occupied by MAT) * BLOCK_SIZE
	*/
	mmu_total_free_block = 0;
	for(i=0;i<TOTAL_SLOT;i++)
	{
		mat_info[i].total_entry = 0;
		for(j=0;j<MAX_BANK_PER_SLOT;j++)
		{  // cal. total memory for this mat in all banks
			if(!mem_bank_map[i][j].bank_size)
				break;  // if bank size = 0 means end of this SLOT
			mem_bank_map[i][j].mat_start_index = mat_info[i].total_entry;  // mat start of the band
			mat_info[i].total_entry += mem_bank_map[i][j].bank_size / BLOCK_SIZE;
		}
		
		mat_occupied_block = mat_info[i].total_entry / MatEntryPerBlock + 1; /* number block occupied by MAT */
		mat_info[i].total_entry -= mat_occupied_block;  /* usable block exclude MAT */
		if (mat_info[i].total_entry < 0)
			mat_info[i].total_entry = 0;
		
		for(j=1;j<MAX_BANK_PER_SLOT;j++) // 0 always index 0
		{  // cal. total memory for this mat in all banks
			if(!mem_bank_map[i][j].bank_size)
				break;  // if bank size = 0 means end of this SLOT
			mem_bank_map[i][j].mat_start_index -= mat_occupied_block;  // mat start of the band
		}
		
		mat_info[i].base_addr = mem_bank_map[i][0].base_addr;  // Usable block start in bank 0;
		mat_info[i].mat = (MAT*) (mat_info[i].base_addr);// + BLOCK_SIZE * mat_occupied_block);
		
		
		mem_bank_map[i][0].base_addr += mat_occupied_block * BLOCK_SIZE;  // first mat_occupied_block block is used by MAT entry table
		mem_bank_map[i][0].bank_size -= mat_occupied_block * BLOCK_SIZE;
		
#ifdef DEBUG
		printf("\nTotal MAT entry: %d ", mat_info[i].total_entry);
		//		printf("\nAlloc start: %08x ", &mem_alloc_start);
		printf("\nMat[0] address: %08x ", mat_info[i].mat);
		
		for(j=0;j<MAX_BANK_PER_SLOT;j++)
		{
			if(!mem_bank_map[i][j].bank_size)
				break;  // if bank size = 0 means end of this SLOT
			printf("\nBank %d ", j);
			printf(" address = %08x, size = %ld, MatStartIndex = %d ",mem_bank_map[i][j].base_addr , mem_bank_map[i][j].bank_size , mem_bank_map[i][j].mat_start_index );
		}
#endif                
		
		for(j=0;j<mat_info[i].total_entry;j++)   /* set the block free */
		{
			mmu_total_free_block++;
			SetBlockType(mat_info[i].mat[j].attribute, MAT_BLOCK_FREE);
		}
		
		SetBlockType(mat_info[i].mat[j].attribute, MAT_BLOCK_MAT_END);
		UpdateMATCRC((&mat_info[i]));
		
	}//i
	
	fmat = mat_info[0].mat;
	
	return TRUE;
}



/*
Function         : MemoryBlockType
Purpose          : Get the block type
Scope            : Internal
Input Parameters : mat
block
Output Parameters: None
Return	    : Type of the block, or 0xFFFF or invalid block
Comment          :
*/
BlockType MemoryBlockType(MatInfoList *mat, USHORT block)
{
	if(block >= mat->total_entry)	/* out of range */
		return (BlockType) 0xFFFF;
	else
		return (BlockType) BlockType(fmat[block].attribute);
}

/*
Function         : MemorySetMATFragment
Purpose          : Set/clear the fragment flag of a block
Scope            : Internal
Input Parameters : mat
block
flag  - TRUE / FALSE
Output Parameters: None
Return	    : TRUE
ERR_MEM_INV_BLOCK
Comment          :
*/
Err MemorySetMATFragment(MatInfoList *mat, USHORT block, BOOLEAN flag)
{
	if(block >= mat->total_entry)	/* out of range */
		return ERR_MEM_INV_BLOCK;
	
	if (flag)
		ClrFragment(fmat[block].attribute);	/* set flag */
	else
		SetFragment(fmat[block].attribute);
	
	UpdateMATCRC(mat);
	return TRUE;
}


/*
Function         : MemoryCheckMAT
Purpose          : Check the integrity of MAT
Scope            : OS
Input Parameters : mat
correct - TRUE will correct error
Output Parameters: None
Return	    : TRUE
ERR_MEM_INV_CHKSUM
ERR_MEM_CROSS_LINK
Comment          : Invalid check sum is not correctable
If correct = TRUE, invalid link will mark free
This func. is obs, always return TRUE, to increase speed
*/
Err MemoryCheckMAT(MatInfoList *mat)
{
	/* check sum after the last entry of MAT */
	if(MemoryMATCheckSum(mat) != fmat[mat->total_entry].id)
		return ERR_MEM_INV_CHKSUM;    /* not equal means invalid check sum */
	return TRUE;
}


/*
Function         : MemoryReadData
Purpose          : Read data directly from a block
Scope            : Internal
Input Parameters : mat
block - block to read
offset - start position
num_byte - number of byte to read
Output Parameters: byte_read - number of byte read
Return	    : TRUE
ERR_MEM_INV_BLOCK - block is invalid
ERR_MEM_INV_PARAM - num_byte = 0
Comment          : - buffer should allocated before this function
*/
Err MemoryReadData(MatInfoList *mat, USHORT block, UWORD offset, UWORD num_byte,
				   BYTE *buffer)
{
	BYTE *p;
	
	if(block >= mat->total_entry)	/* out of range */
		return ERR_MEM_INV_BLOCK;
	
	if(!num_byte)
		return ERR_MEM_INV_PARAM;
	
	p = (BYTE*) ((BlockAddr(mat,block)) + offset);
	
	while(num_byte--)
		*(buffer++) = *(p++);
	
	return TRUE;
}

/*
Function         : MemoryShiftData
Purpose          : Shift data within a block up or down to fill/free memory
Scope            : Internal
Input Parameters : mat
block
src_pos - position(offset of the block) to start move
dest_pos - destination position
num_byte  - number of byte to shift
Output Parameters: None
Return	    : TRUE
ERR_MEM_INV_BLOCK - block is invalid
ERR_MEM_INV_PARAM (if src_pos = dest_pos)
Comment          :
*/
Err MemoryShiftData(MatInfoList *mat, USHORT block, UWORD src_pos,
					UWORD dest_pos, UWORD num_byte)
{
	BYTE *s;
	BYTE *d;
	
	if(block >= mat->total_entry)	/* out of range */
		return ERR_MEM_INV_BLOCK;
	
	if((src_pos == dest_pos) || (!num_byte))
		return ERR_MEM_INV_PARAM;
	
	s = (BYTE*) (BlockAddr(mat, block) + src_pos);
	d = (BYTE*) (BlockAddr(mat, block) + dest_pos);
	
	if( ((UWORD) d + num_byte) >= (BlockAddr(mat, block)  + BLOCK_SIZE))
		num_byte = BLOCK_SIZE - dest_pos;
	
	if(s>d)
	{
		while(num_byte--)
			*(d++) = *(s++);
	}
	else
	{
		while(num_byte--)
		{
			d[num_byte] = s[num_byte];
		}
	}
	
	return TRUE;
}


/*
Function         : MemoryPrevBlock
Purpose          : Find the previous linked block
Scope            : Internal
Input Parameters : mat
block
Output Parameters: prev - previous block entry
Return	    : TRUE
ERR_MEM_INV_BLOCK
ERR_MEM_BLOCK_FREE  - this is a free block, no previous
ERR_MEM_BOB         - begin of block, no previous block
Comment          :
*/
Err MemoryPrevBlock(MatInfoList *mat, USHORT block, USHORT *prev)
{
	UWORD i;
	
	if(block >= mat->total_entry)	/* out of range */
		return ERR_MEM_INV_BLOCK;
	
		/*      not check, since will call by CheckMAT
		if(!IsBlockFree(fmat[block].attribute))
		return ERR_MEM_BLOCK_FREE;
	*/
	if(block)
	{
		i = block - 1;
		if(fmat[i].next == block)
			if (BlockType(fmat[i].attribute) != MAT_BLOCK_FREE)
			{
				*prev = i;
				return TRUE;
			}
	}
	
	for(i=0;i<mat->total_entry;i++)  /* examine all block, find the block's next is the block number and type is not FREE */
		if(fmat[i].next == block)
			if (BlockType(fmat[i].attribute) != MAT_BLOCK_FREE)
			{
				*prev = i;
				return TRUE;
			}
			
			return ERR_MEM_BOB;
}

/*
Function         : MemoryNextBlock
Purpose          : Find the next linked block
Scope            : Internal
Input Parameters : mat
block
Output Parameters: prev - previous block entry
Return	    : TRUE
ERR_MEM_INV_BLOCK
ERR_MEM_BLOCK_FREE  - this is a free block, no next
ERR_MEM_EOB         - end of block, no previous block
Comment          : if no next, do not modify variable 'block', see MemoryGetSpace()
*/
Err MemoryNextBlock(MatInfoList *mat, USHORT block, USHORT *next)
{
	
	if(block >= mat->total_entry)	/* out of range */
		return ERR_MEM_INV_BLOCK;
	
#ifdef DEBUG
	if(IsBlockFree(fmat[block].attribute))
	{
		printf("\n[MMU] Err: query next linked block for a free block # %d ", block);
		return ERR_MEM_BLOCK_FREE;
	}
#endif
	
	if(fmat[block].next != (USHORT) 0xFFFF)
	{
		*next = fmat[block].next;
		return TRUE;
	}
	else
		return ERR_MEM_BLOCK_FREE;
}


/*
Function         : MemoryNextBlock2
Purpose          : Find the next linked block
Scope            : Internal
Input Parameters : block
Output Parameters: prev - previous block entry
Return	    : TRUE
ERR_MEM_INV_BLOCK
ERR_MEM_BLOCK_FREE  - this is a free block, no next
ERR_MEM_EOB         - end of block, no previous block
Comment          : 
*/
Err MemoryNextBlock2(USHORT block, USHORT *next)
{
	return MemoryNextBlock(&mat_info[0], block, next);
}


/*
Function         : MemoryLastBlock
Purpose          : Find the last linked block
Scope            : Internal
Input Parameters : mat
block
Output Parameters: prev - previous block entry
Return	    : TRUE
ERR_MEM_INV_BLOCK
ERR_MEM_BLOCK_FREE  - this is a free block, no next
Comment          : if no next, do not modify variable 'block', see MemoryGetSpace()
*/
Err MemoryLastBlock(MatInfoList *mat, USHORT block, USHORT *last)
{
	
	if(block >= mat->total_entry)	/* out of range */
		return ERR_MEM_INV_BLOCK;
	
#ifdef DEBUG
	if(IsBlockFree(fmat[block].attribute))
	{
		printf("\n[MMU] Err: query last linked for a free block # %d ", block);
		return ERR_MEM_BLOCK_FREE;
	}
#endif
	
	if(fmat[block].next != (USHORT) 0xFFFF)
	{
		*last = fmat[block].next;
		while(fmat[*last].next != 0xFFFF)
			*last = fmat[*last].next;
		if(fmat[*last].next == 0xFFFF)
			*last = fmat[*last].next;
		return TRUE;
	}
	else
		return ERR_MEM_BLOCK_FREE;
}


/*
Function         : MemoryNewBlock
Purpose          : Allocate new block
Scope            : Internal
Input Parameters : mat
num_block	 - number of block
id	 - id of new block
type	 - block type
prev_block - previous block
Output Parameters: new_block  - new block number
Return	    : TRUE
ERR_MEM_INV_BTYPE  - invalid block type
ERR_MEM_INV_BLOCK  - invalid previous block
ERR_MEM_NO_MEM	 - no. of free block < required
Comment          : allocatefree entry in MAT, update MAT's link, block may not continuous, if need
continuous block, call MemoryGetConBlock()
*/
Err MemoryNewBlock(MatInfoList *mat, USHORT num_block, UWORD id, BlockType type, USHORT prev_block, USHORT *new_block)
{
    UWORD i, found;
	
    if(prev_block >= mat->total_entry)	/* out of range */
		if(prev_block != (USHORT) 0xFFFF)
			return ERR_MEM_INV_BLOCK;
#ifdef DEBUG
		printf("\n[MMU] Total free block  = %d" , MemoryTotalFreeBlock(mat));
#endif
		
		if(num_block > 5)	/* only check free block if req > 5, most likely there are more than 5 free blocks in system, to increase the speed */
			if(MemoryTotalFreeBlock(mat) < num_block)
				return ERR_MEM_NO_MEM;
			
			found = 0;
			for(i=last_found_free_block;i<mat->total_entry;i++)
			{
				if (IsBlockFree(fmat[i].attribute))		/* free block found */
				{
					if (prev_block != (USHORT) 0xFFFF)	/* update previous link */
						fmat[prev_block].next = i;
					prev_block = i;
					fmat[i].id = id;
					fmat[i].next = (USHORT) 0xFFFF;
					SetBlockType(fmat[i].attribute, type);
					ClrFragment(fmat[i].attribute);
					mmu_total_free_block--;
					if(i < mat->total_entry - 20)
						last_found_free_block = i;
					else
						last_found_free_block = 0;
					if(++found == 1)                /* first block */
						*new_block = i;
					if(found >= num_block)		/* completed, exit for loop */
						break;
				}
			}
			
			//_KAM		if (found == 0)
			if (found < num_block)				//_KAM
			{
				//_KAM			for(i=0;i<last_found_free_block;i++)
				for(i=0;i<mat->total_entry;i++)	//_KAM
				{
					if (IsBlockFree(fmat[i].attribute))		/* free block found */
					{
						if (prev_block != (USHORT) 0xFFFF)	/* update previous link */
							fmat[prev_block].next = i;
						prev_block = i;
						fmat[i].id = id;
						fmat[i].next = (USHORT) 0xFFFF;
						SetBlockType(fmat[i].attribute, type);
						ClrFragment(fmat[i].attribute);
						mmu_total_free_block--;
						last_found_free_block = 0;
						if(++found == 1)                /* first block */
							*new_block = i;
						if(found >= num_block)		/* completed, exit for loop */
							break;
					}
				}     	
			}
			if(found == 0)
				return ERR_MEM_NO_MEM;
			
			
			UpdateMATCRC(mat);
			if (found < num_block)	/* cannot allocate enough block */
			{
#ifdef DEBUG
				printf("\n[MMU] No more memory ");
#endif
				MemoryReleaseBlock(mat, *new_block, TRUE, TRUE);    /* release all allocated block */
				return ERR_MEM_NO_MEM;
			}
			else
				return TRUE;
}


/*
Function         : MemoryInsertBlock
Purpose          : Allocate a block AFTER the block in MAT
Scope            : Internal
Input Parameters : mat
block        - current block
num_block    - number of block to insert
Output Parameters: new_block  - first inserted block
Return	    : TRUE
ERR_MEM_INV_BLOCK  - invalid block number
ERR_MEM_NO_MEM     - not enough memory
Comment          : Check num_block <> 0 before call
*/
Err MemoryInsertBlock(MatInfoList *mat, USHORT block, USHORT num_block, USHORT *new_block)
{
    USHORT old_link;
    Err result;
    USHORT t;
	
	
    if(block >= mat->total_entry)	/* out of range */
		return ERR_MEM_INV_BLOCK;
	
	
    if(BlockType(fmat[block].attribute) == MAT_BLOCK_FREE)
		return TRUE;			/* already a free block */
	
    old_link = fmat[block].next;
	
    if(BlockType(fmat[block].attribute) == MAT_BLOCK_DB_REC)
		t = MAT_BLOCK_REC;
    else
		t = BlockType(fmat[block].attribute);
	
    if((result=MemoryNewBlock(mat, num_block, fmat[block].id, t, block, new_block))!=TRUE)
		return result;
	
    while(num_block--)
		MemoryNextBlock(mat, block, &block);
	
    fmat[block].next = old_link;
    
    UpdateMATCRC(mat);
	
    return TRUE;
	
}

void MemoryClrBlockData(MatInfoList *mat, USHORT block)
{
	UWORD *pdata;
	UWORD i;
	
    if(block >= mat->total_entry)	/* out of range */
		return;
	
	pdata = (UWORD*) (BlockAddr(mat, block));
	
	for(i=0;i < BLOCK_SIZE / sizeof(UWORD); i++)
		*(pdata++) = 0;        
}

/*
Function         : MemoryReleaseBlock
Purpose          : Free a block to release memory
Scope            : OS
Input Parameters : mat
block
check_link   -  if TRUE, all forward links will mark free
else will set prev->next = current->next
check_back_link - if TRUE, will check backward link	
Output Parameters: None
Return	    : TRUE
ERR_MEM_INV_BLOCK  - invalid block number
Comment          :
*/
Err MemoryReleaseBlock(MatInfoList *mat, USHORT block, BOOLEAN check_link, BOOLEAN check_back_link)
{
    USHORT prev_link;
    USHORT last_link;
	
    if(block >= mat->total_entry)	/* out of range */
		return ERR_MEM_INV_BLOCK;
	
    if(BlockType(fmat[block].attribute) == MAT_BLOCK_FREE)
		return TRUE;			/* already a free block */
	
    SetBlockType(fmat[block].attribute, MAT_BLOCK_FREE);  /* mark the block free */
    ClrFragment(fmat[block].attribute);
    mmu_total_free_block++;
	
    if(check_link)  /* free all forward linked blocks */
    {
		if (MemoryPrevBlock(mat, block, &prev_link) == TRUE)	/* this block has previous block */
			fmat[prev_link].next = (USHORT) 0xFFFF;
		last_link = block + 1;   /* set last_link != block */
		while(last_link != block)
		{
			last_link = block;
			while (fmat[last_link].next != (USHORT) 0xFFFF)  /* locate to last link */
				if(BlockType(fmat[fmat[last_link].next].attribute) != MAT_BLOCK_FREE)
					last_link = fmat[last_link].next;
				else
					break;
				
			SetBlockType(fmat[last_link].attribute, MAT_BLOCK_FREE);  /* mark the block free */
			ClrFragment(fmat[last_link].attribute);
			mmu_total_free_block++;
		}
    }
    else if(check_back_link)
		if (MemoryPrevBlock(mat, block, &prev_link) == TRUE)	/* this block has previous block */
			fmat[prev_link].next = fmat[block].next;
		
		UpdateMATCRC(mat);
		return TRUE;
}

/*
Function         : MemoryReleaseBlock2
Purpose          : Free a block to release memory
Scope            : OS
Input Parameters : block
check_link   -  if TRUE, all forward links will mark free
else will set prev->next = current->next
check_back_link - if TRUE, will check backward link	
Output Parameters: None
Return	    : TRUE
ERR_MEM_INV_BLOCK  - invalid block number
Comment          :
*/
Err MemoryReleaseBlock2(USHORT block, BOOLEAN check_link, BOOLEAN check_back_link)
{
	return MemoryReleaseBlock(&mat_info[0], block, check_link, check_back_link);
}


/*
Function         : MemoryCopyData
Purpose          : Copy data from one block to another/same block
Scope            : Internal
Input Parameters : mat
src_block
src_offset - Copy start position(offset of the block)
dest_block - destination block
dest_offset - destination start position
num_byte - Number of byte to copy
Output Parameters: None
Return	    : TRUE
ERR_MEM_INV_SRC
ERR_MEM_INV_DEST
ERR_MEM_INV_PARAM
Comment          :
*/
Err MemoryCopyData(MatInfoList *mat, USHORT src_block, USHORT src_offset,
				   USHORT dest_block, USHORT dest_offset, UWORD num_byte)
{
	BYTE  *s;
	BYTE  *d;
	
	if(src_block >= mat->total_entry)
		return ERR_MEM_INV_SRC;
	if(dest_block >= mat->total_entry)
		return ERR_MEM_INV_DEST;
	if(num_byte + dest_offset > BLOCK_SIZE)
		return ERR_MEM_INV_PARAM;
	
	s = (BYTE*) (BlockAddr(mat, src_block) + src_offset);
	d = (BYTE*) (BlockAddr(mat, dest_block) + dest_offset);
	
	if ( ((UWORD)d + num_byte) > ((UWORD)d + BLOCK_SIZE))
		num_byte = BLOCK_SIZE - dest_offset;
	
	while(num_byte--)
		*(d++) = *(s++);
	
	return TRUE;
}


/*
Function         : MemoryMoveBlock
Purpose          : Move the content and MAT info. of a block to another block,
and mark the origin block free
Scope            : Internal
Input Parameters : mat
src_block
dest_block
Output Parameters: None
Return	    : TRUE
ERR_MEM_INV_BLOCK  - invalid src/dest block
Comment          :
*/
Err MemoryMoveBlock(MatInfoList *mat, USHORT src_block, USHORT dest_block)
{
	Err result;
	USHORT prev_link;
	
	/* copy the content */
	if( MemoryCopyData(mat, src_block, 0, dest_block, 0, BLOCK_SIZE) != TRUE)
		return ERR_MEM_INV_BLOCK;
	
	result = MemoryPrevBlock(mat, src_block, &prev_link);  /* find prev first, to increase the MAT content inconsistent time */
	fmat[dest_block] = fmat[src_block];  /* copy the MAT content */
	if(result==TRUE)   /* update prev. link */
		fmat[prev_link].next = dest_block;
	/* mark the block free */
	SetBlockType(fmat[src_block].attribute, MAT_BLOCK_FREE);
	/* dest block is free, so free block not increase */
	UpdateMATCRC(mat);
	return TRUE;
}


/*
Function         : MemoryFindStartBlock
Purpose          : Find the start block of a database
Scope            : Internal
Input Parameters : mat
dbid
Output Parameters: block
Return	    : TRUE
ERR_MEM_ID_MISS - invalid block
Comment          :
*/
Err MemoryFindStartBlock(MatInfoList *mat, UWORD dbid, USHORT *block)
{
	UWORD i;
	
	/* first block of DB, type = MAT_BLOCK_DBREC */
	
	for(i=0;i<mat->total_entry;i++)
		if(BlockType(fmat[i].attribute) == MAT_BLOCK_DB_REC)
			if(fmat[i].id == dbid)
			{
				*block = i;
				return TRUE;
			}
			
			return ERR_MEM_ID_MISS;
}


/*
Function         : MemoryTotalFree
Purpose          : Find total free blocks in System
Scope            : All
Input Parameters : None
Output Parameters: None
Return	    : Number of free blocks
Comment          :
*/
USHORT MemoryTotalFree()
{
	return MemoryTotalFreeBlock(&mat_info[0]);
}

/*
Function         : MemoryTotalFreeBlock
Purpose          : Find total free blocks in MAT
Scope            : Internal
Input Parameters : mat
Output Parameters: None
Return	    : Number of free blocks
Comment          :
*/
USHORT MemoryTotalFreeBlock(MatInfoList *mat)
{
#ifdef MMU_ACTUAL_COUNT
	UWORD i;
	USHORT total_free = 0;
	
	for(i=0;i<mat->total_entry;i++)
		if ((BlockType(fmat[i].attribute)) == MAT_BLOCK_FREE)
			total_free++;
		
		return total_free;
#else
		return  mmu_total_free_block;
#endif
}


/*
Function         : MemoryGetConBlock
Purpose          : Find total block occupied by a database / program
Scope            : Internal
Input Parameters : mat
num_block - number of block requested
id - id of new blocks
type - type of new blocks
prev_block - previous linked block for new blocks
Output Parameters: new_block - first block of the new blocks
Return	    : TRUE
ERR_MEM_NO_MEM - not enough memory
ERR_MEM_NO_CON_MEM - not enough continuous memory
Comment          : ERR_MEM_NO_CON_MEM means enough free memory but not
enough free continuous memory block
*/
Err MemoryGetConBlock(MatInfoList *mat, USHORT num_block, UWORD id, BlockType type, USHORT prev_block, USHORT *new_block)
{
	UWORD i;
	USHORT free_first = 0;
	USHORT free_count = 0;
	BOOLEAN is_free = FALSE;
	BOOLEAN free_found = FALSE;
	USHORT mov_first = 0;
	USHORT mov_count = 0;
	BOOLEAN is_mov = FALSE;
	BOOLEAN mov_found = FALSE;
	USHORT mov_ptr;
	
	/*
	(1) Find total free blocks, if < num_blocks, fail, end
	(2) If largest continuous free blocks > num_block, end
	(3) if largest continuous movable blocks ( REC + FREE )
	> num_block, move out the blocks, end
	(4) fail, end
	*/
	
	/* --- (1) total free ---------------------------*/
	if ( MemoryTotalFreeBlock(mat) < num_block )
		return ERR_MEM_NO_MEM;
	
	for(i=0;i<mat->total_entry-1;i++)
	{
		/* --- (2) largest continuous block ------------- */
		if ((BlockType(fmat[i].attribute)) == MAT_BLOCK_FREE)
		{
			if (is_free)	/* previous block also a free block */
				free_count++;
			else            /* the first free block in this series */
			{
				free_count = 1;
				free_first = i;
				is_free = TRUE;
			}
			if (free_count >= num_block)  /* stop if free block found > requested */
			{
				free_found = TRUE;
				break;
			}
		}
		else
			is_free = FALSE;  /* not a free block, disconnect the link */
		
		/* --- (3) largest movable block ------------- */
		if (((BlockType(fmat[i].attribute)) == MAT_BLOCK_FREE) ||
			((BlockType(fmat[i].attribute)) == MAT_BLOCK_REC))
		{
			if (is_mov)	/* previous block also a free block */
				mov_count++;
			else            /* the first free block in this series */
			{
				mov_count = 1;
				mov_first = i;
				is_mov = TRUE;
			}
			if (mov_count >= num_block)  /* stop if free block found > requested */
			{
				mov_found = TRUE;
				break;
			}
		}
		else
			is_mov = FALSE;  /* not a free block, disconnect the link */
	}
	
	if((free_found == FALSE) && (mov_found == FALSE))
		return ERR_MEM_NO_CON_MEM;
	
	if(free_found == FALSE)
	{
		/* continuous free block not found, need to move out movable blocks */
		free_first = mov_first;  /* see the code after this if code */
		mov_ptr = mov_first;	 /* locate first block need to move */
		while( ((BlockType(mov_ptr)) == MAT_BLOCK_FREE) &&
			(mov_ptr < (mov_first+num_block)) )
			mov_ptr++;
		
		for(i=0;i<mat->total_entry;i++)
		{
			/* free block should not fall into [mov_first, mov_first + num_block] */
			if( ((BlockType(fmat[i].attribute))==MAT_BLOCK_FREE)  &&
				( (i<mov_first) && ( i>=(mov_first + num_block)) ))
			{
				MemoryMoveBlock(mat, mov_ptr++, i);  /* move out the block */
				
				/* found next block to move */
				while( ((BlockType(mov_ptr)) == MAT_BLOCK_FREE) &&
					(mov_ptr < (mov_first+num_block)) )
					mov_ptr++;
				
				if(mov_ptr >= (mov_first+num_block))
					break;   /* move completed */
			}
		}
	}
	
	for(i=free_first;i<free_first + num_block;i++)       /* mark all blocks occupied */
	{
		fmat[i].id = id;
		SetBlockType(fmat[i].attribute, type);
		ClrFragment(fmat[i].attribute);
		mmu_total_free_block--;
		if(i==free_first)  /* first block, need to check if has prev */
		{
			if(prev_block != (USHORT) 0xFFFF)
				fmat[prev_block].next = i;
		}
		else		   /* rest block must update previous */
			fmat[prev_block].next = i;
		prev_block = i;
	}
	
	*new_block = free_first;
	UpdateMATCRC(mat);
	return TRUE;
}

/*
Function         : MemoryGetSpace
Purpose          : Find block/blocks will free space > request
Scope            : Internal
Input Parameters : mat
block  -  start block of the database
size   -  request size
con    -  if TRUE, if size > 4 KB, allocate block in continuous physical address
Output Parameters: new_block - first block of the new block(s)
offset - offset from the block
Return	    : Number of block used by the database / program
Comment          : block should check in valid range
*/
BOOLEAN MemoryGetSpace(MatInfoList *mat, USHORT block, UWORD size, BOOLEAN con,
					   USHORT *new_block, UWORD *offset)
{
/* (1) examine block's free space, if enough , exit with found
(2) get next block, repeat (1) until no next block
(3) allocate a new block, exit with found
(4) if cannot allocate a new block, exit with error
	*/
	UWORD free_space;
	BlockType type;
	
	*new_block = block;
	if(size < BLOCK_SIZE)	/* if size > BLOCK_SIZE, should alloc. a new block */
	{
		while (1)
		{
			if((free_space = MemoryBlockFreeSpace(mat, *new_block)) > size)
			{
				*offset = BLOCK_SIZE - free_space;
				return TRUE;
			}
			if (MemoryNextBlock(mat, *new_block, new_block) != TRUE)
				break;
		}
	}
	else
	{
		while (MemoryNextBlock(mat, *new_block, new_block) == TRUE);
	}
	
	
	type = ((BlockType(fmat[block].attribute) == MAT_BLOCK_DB_REC) ? MAT_BLOCK_REC : BlockType(fmat[block].attribute));
	
	*offset = 0;
	/* cannot found free space in existing allocated block */
	
	if(con == FALSE)
		return (MemoryNewBlock(mat, (size/BLOCK_SIZE) + 1, fmat[block].id, type, *new_block, new_block) == TRUE);
	else
		return (MemoryGetConBlock(mat, (size/BLOCK_SIZE) + 1, fmat[block].id, type, *new_block, new_block) == TRUE);
}

/*
Function         : MemoryBlockFreeSpace
Purpose          : Find free space in a block
Scope            : Internal
Input Parameters : mat
block
Output Parameters: None
Return	    : block free
Comment          :
*/
UWORD MemoryBlockFreeSpace(MatInfoList *mat, USHORT block)
{
	RecordHeader *rec;
	DBHeader *db;
	UWORD block_addr;
	
	block_addr = BlockAddr(mat, block);
	switch BlockType(fmat[block].attribute)
	{
	case MAT_BLOCK_FREE:
		return BLOCK_SIZE;
	case MAT_BLOCK_PRG_RES:
		return 0;
	case MAT_BLOCK_DB_REC:
		db = (DBHeader *) block_addr;
		rec = (RecordHeader*)( block_addr + db->length);  /* first record address */
		while (rec->size != DATA_CHUNK_END)
		{
			if (((UWORD)rec - block_addr) >= BLOCK_SIZE)
				return 0;
			rec = (RecordHeader*) ((UWORD) rec + rec->size);
			if (((UWORD)rec - block_addr) >= BLOCK_SIZE)
				return 0;
		}
		return BLOCK_SIZE - ((UWORD)rec - block_addr);
	case MAT_BLOCK_REC:
		rec = (RecordHeader *) block_addr;
		while (rec->size != DATA_CHUNK_END)
		{
			if (((UWORD)rec - block_addr) >= BLOCK_SIZE)
				return 0;
			if(rec->size > BLOCK_SIZE)
				rec->size = 0;
			rec = (RecordHeader*) ((UWORD) rec + rec->size);
			if (((UWORD)rec - block_addr) >= BLOCK_SIZE)
				return 0;
		}
		return BLOCK_SIZE - ((UWORD)rec - block_addr);
	default:
		return 0;
	}
	
}


/*
Function         : MemoryResolveBlock
Purpose          : Resolve physical address to [mat,block] format
Scope            : Internal
Input Parameters : addr: physical address to resolve 
Output Parameters: block, mat_num
Return           : TRUE
FALSE 
Comment          : 
*/
BOOLEAN MemoryResolveBlock(UWORD addr, USHORT *block, USHORT *mat_num)
{
	UWORD i,j,k;
	*mat_num = 0xffff;
	
	for(i=0;i<TOTAL_SLOT;i++)
		for(j=0;j<MAX_BANK_PER_SLOT;j++)
		{
			if( (addr >= mem_bank_map[i][j].base_addr) && 
				(addr < mem_bank_map[i][j].base_addr +mem_bank_map[i][j].bank_size) )
			{
				*mat_num = i;
				*block = (addr - mem_bank_map[i][j].base_addr) / BLOCK_SIZE + mem_bank_map[i][j].mat_start_index;
				break;
			}
		}
		
		return (*mat_num != (USHORT) 0xFFFF);
		
}

/*
Function         : MemoryGroupUsage
Purpose          : Find total block occupied by a database / program
Scope            : Internal
Input Parameters : mat
Output Parameters: None
Return	    : Number of block used by the database / program
Comment          : If the database/program not exist, return 0
*/
USHORT MemoryGroupUsage(MatInfoList *mat, UWORD id)
{
	UWORD i;
	USHORT total_block = 0;
	
	for(i=0;i<mat->total_entry;i++)
		if(fmat[i].id == id)
			if((BlockType(fmat[i].attribute)) != MAT_BLOCK_FREE)
				total_block++;
			
			
			return total_block;
}

/*
Function         : MemoryMATCheckSum
Purpose          : Calculate the check sum of MAT
Scope            : Internal
Input Parameters : MAT pointer
Output Parameters: None
Return	    : The check sum
Comment          : This function is obs.
*/
UWORD MemoryMATCheckSum(MatInfoList *mat)
{
	UWORD i;
	UWORD check_sum = 0;
	
	for(i=0;i<mat->total_entry;i++)
		check_sum ^= fmat[i].id ^ *((UWORD*) (&(fmat[i].next)));
	return check_sum;
}


void MemoryUpdateCheckSum()
{
	MemDataCRC = MemoryDataCheckSum();
}

/*
Function         : MemoryDataCheckSum
Purpose          : Calculate the check sum of Data
Scope            : Internal
Input Parameters : None
Output Parameters: None
Return	    : The check sum
Comment          :
*/
UWORD MemoryDataCheckSum()
{
	UWORD i, j;
	UWORD check_sum = 0;
	UWORD addr;
	
	for(i=0;i<TOTAL_SLOT;i++)
	{
		for(j=0;j<mat_info[i].total_entry;j++)
			if((BlockType(mat_info[i].mat[j].attribute) == MAT_BLOCK_DB_REC))
			{
				addr = (BlockAddr(&mat_info[i], j));
				check_sum ^= *(UWORD*) addr;// ^ *(UWORD*)(addr + HALF_BLOCK_SIZE) ^ *(UWORD*)(addr + BLOCK_SIZE_MINUS_1);
			}
	}
	
	return check_sum;
}

/*
Function         : MemoryCheck()
Purpose          : Check the memory & MAT CRC
Scope            : All
Input Parameters : None
Output Parameters: None
Return           : TRUE      memory check sum ok
FALSE     memory check sum error
Comment          :
*/
BOOLEAN MemoryCheck()
{
	UWORD i;
	UWORD size, addr;
	UWORD *meminfo = (UWORD*) MEM_INFO_ADDR + 1; /* bank 0 size */
	
#ifdef MMU_CHECK_MAT
	
	/* 1. Check all MAT */
#ifdef NEW_BOOT_LOADER
#ifdef PR31700
    size = *meminfo++ - (UWORD) (((UWORD)&mem_alloc_start) & 0x00ffffff) - 15 * BLOCK_SIZE;
	addr = (UWORD) &mem_alloc_start + 10 *BLOCK_SIZE;
    addr +=  BLOCK_SIZE - addr % BLOCK_SIZE;  /* align to Block boundary */
    size -= BLOCK_SIZE - addr % BLOCK_SIZE;  
    size = size - size % BLOCK_SIZE; /* make to 4K multiple */
	
	if( (size - mem_bank_map[0][0].bank_size) > 26 * BLOCK_SIZE)
	{
#ifdef DEBUG
		printf("\n[MMU] Fatal error: MAT index 0 error ");
#endif
		return FALSE;
	}
	
	i = 1;
	while(*meminfo)
	{
		addr = *meminfo++;
		size = *meminfo++;
       	addr +=  BLOCK_SIZE - addr % BLOCK_SIZE;  /* align to Block boundary */
        size -= BLOCK_SIZE - addr % BLOCK_SIZE;  
        size = size - size % BLOCK_SIZE; /* make to 4K multiple */
		
		if( (mem_bank_map[0][i].base_addr != addr) ||
			(mem_bank_map[0][i].bank_size != size) )
		{
#ifdef DEBUG
			printf("\n[MMU] Fatal error: Memory map error");
#endif
			return FALSE;
		}
		i++;
	}
#endif
#endif
	
	for(i=0;i<TOTAL_SLOT;i++)
	{
		if(MemoryCheckMAT(&mat_info[i]) != TRUE)
		{
#ifdef DEBUG
			printf("\n[MMU] Fatal error: MAT CRC error\n");
#endif   
			return FALSE;
		}
	}
	
#ifdef DATA_CHECK_SUM
	
	if(MemDataCRC != MemoryDataCheckSum())
	{
#ifdef DEBUG
		printf("\n[MMU] Fatal error: Memory content CRC error\n");
#endif
		return FALSE;
	}
#endif
	
#ifdef DEBUG
	printf("\nMat Check OK\n");
#endif
	
	
	// end if with  MMU_CHECK_MAT
#endif 
	return TRUE;
}


/*
Function         : MemoryDMANew
Purpose          : Allocate new space to dynamic memory
Scope            : Internal
Input Parameters : type      DM_OS or DM_USER
app       Application ID
Output Parameters: None
Return           : New block's virtual address (if success)
NULL      (if fail)
Comment          :
*/
void *MemoryDMANew(UBYTE type, USHORT num_block, USHORT *blk_num)
{
	USHORT i;
	DatabaseID dbid;
	USHORT blk_type, next_block, total_block, prev_block;
	WORD *size;
	UWORD **start;
	USHORT *first_block;
	void *ptr;
	
	switch(type)
	{
	case DM_OS:
		dbid = 0xFFFFFFF0;
		blk_type = MAT_BLOCK_OSDMA;
		size = &os_dynamic_mem_size;
		start = &os_dynamic_mem_address;
		first_block = &dmos_first_block;
		break;
	case DM_USER:
		dbid = 0xFFFFFFF1;
		if(run_mode == RUN_MODE_NORMAL)
			blk_type = MAT_BLOCK_APPDMA;
		else
		{
			blk_type = MAT_BLOCK_APP_DATA_MODE1;   /* mode 1 */
		}
		size = &user_dynamic_mem_size;
		start = &user_dynamic_mem_address;
		first_block = &dmuser_first_block;
		break;
defalut:
#ifdef DEBUG
		printf("\n[MMU] Invalid dynamic memory type ");
#endif
		return NULL;
	}
	
	MemoryDMAInfo(&mat_info[0], type, 0, &total_block, &prev_block);
	
	for(i=0;i<num_block;i++)
	{
		if (MemoryNewBlock(&mat_info[0], 1, dbid, blk_type, prev_block, &next_block) != TRUE)
			return NULL;
		prev_block = next_block;
		if(*start == (UWORD*)0xFFFFFFFF)
		{
			ptr = *start = (UWORD*)MemoryDMAStart(0, type);  /* find DMA logical start address */
			*size = BLOCK_SIZE;
			MemoryPageTableAppend(type, 0, BlockAddr(&mat_info[0], next_block));  /* add this block to page table */
#ifdef MMU_OLD_DMA_INFO
			*blk_num = *first_block = next_block;
#else
			*blk_num = next_block;
#endif
		}
		else
		{
			MemoryPageTableAppend(type, 0, BlockAddr(&mat_info[0], next_block));  /* add this block to page table */
			(*size) += BLOCK_SIZE;
			if(i==0)
			{
				ptr = (void *)((UWORD) ((total_block + i) * BLOCK_SIZE) + (UWORD) *start);
				*blk_num = next_block;
			}
		}         
	}
	
#ifdef DEBUG
	printf("\n[MMU] New Page for DM virtual address = %08x ", ptr);
#endif
	return ptr;
}

/*
Function         : MemoryDMAStart
Purpose          : Return the virtual address of next available dynamic memory
Scope            : OS
Input Parameters : type      DM_OS or DM_USER
app       Application ID  (ignored)
Output Parameters: None
Return           : virtual address (if success)
NULL      (if fail)
Comment          :
*/
UWORD MemoryDMAStart(AppID app, UBYTE type)
{
	switch(type)
	{
	case DM_USER:
		
		if(app_page_table == NULL)
#ifdef PR31700
		{
#ifdef DEBUG				
			printf("\nvirtual start %08x", USER_MEM_BASE);
#endif				
			return USER_MEM_BASE;
		}
#endif
#ifdef PC_SIM
		return (UWORD) &DM_MEM;
#endif
		else
#ifdef PR31700
			return USER_MEM_BASE + (*app_page_table) * BLOCK_SIZE;
#endif
#ifdef PC_SIM
		return (UWORD) &DM_MEM + (*app_page_table) * BLOCK_SIZE;
#endif
	case DM_OS:
		if(sys_page_table == NULL)
#ifdef PR31700
			return SYS_MEM_BASE;
#endif
#ifdef PC_SIM
		return (UWORD) &OS_MEM;
#endif
		else
#ifdef PR31700
			return (UWORD) SYS_MEM_BASE + (*sys_page_table) * BLOCK_SIZE;
#endif
#ifdef PC_SIM
		return (UWORD) &OS_MEM + (*sys_page_table) * BLOCK_SIZE;
#endif
	}
#ifdef DEBUG
	printf("\nInvalid type in MemoryDMAStart");
#endif
	return USER_MEM_BASE;
}


/*
Function         : MemoryClearAppDM
Purpose          : Clear all dm block (qmalloc) allocated by the application
Scope            : Internal
Input Parameters : app       Application ID  - currently ignored
Output Parameters: None
Return           : TRUE      Success
FALSE
Comment          :
*/
Err MemoryClearAppDM(AppID app)
{
	UWORD i, j;
	
	for(i=0;i<TOTAL_SLOT;i++)
	{
		if(mat_info[i].total_entry)
		{
			for(j=0;j<mat_info[i].total_entry;j++)
			{
				if(BlockType(mat_info[i].mat[j].attribute) == MAT_BLOCK_APPDMA) /* && mat_info[i].id = (UWORD) appid */
				{
					SetBlockType(mat_info[i].mat[j].attribute,MAT_BLOCK_FREE);
					mmu_total_free_block++;
				}
			}
		}
	}
	
	return TRUE;
}


/*
Function         : MemoryPageTableAppend
Purpose          : Add a page to page table for application
Scope            : Internal
Input Parameters : app       Application ID
phy_addr  The physical address of the new page (4KB)
Output Parameters: None
Return           : TRUE      Success
FALSE     Fail (app not found)
Comment          :
*/
Err MemoryPageTableAppend(UBYTE type, AppID app, UWORD phy_addr)
{
	UWORD i;
	UWORD *page_table;
	UWORD *new_page, *old_table;
	
	switch (type)
	{
	case DM_OS:
		page_table = sys_page_table;
		break;
	case DM_USER:
		page_table = app_page_table;
		break;
	case DM_DRV:
		page_table = int_page_table;
		break;
#ifdef DEBUG
	default:		
		printf("\n[MMU] Invalid page table type");
		return FALSE;
#endif
	}
	
	if((run_mode == RUN_MODE_NORMAL) || (type != DM_USER))
	{       /* the page table is static alloc, and can hold 512 entries */
		(*page_table)++;
		page_table[*page_table] = phy_addr;
	}
	else
	{  /* the page table is dynamic allocated */
#ifdef DEBUG        
		printf(" Mode1 app detected, org total page = %d ", *page_table);
#endif              
		new_page = (UWORD*) pmalloc( ((*page_table) + 3) * sizeof(UWORD*));
		if(!new_page)
		{
#ifdef DEBUG
			printf("\n[MMU] Cannot allocated space for new PT ");
#endif
			return FALSE;
		}
		for(i=1;i<=page_table[0];i++)
			new_page[i] = page_table[i];
		
		new_page[0] = page_table[0] + 1;
		new_page[new_page[0]] = phy_addr;
		switch (type)
		{
		case DM_OS:
			sys_page_table = new_page;
			sys_page = new_page + 1;
			break;
		case DM_USER:
			app_page_table = new_page;
			app_page = new_page + 1;
			break;
		case DM_DRV:
			int_page_table = new_page;
			int_page = new_page + 1;
			break;
		}
		old_table = page_table;
		page_table = new_page;
		pfree(old_table);
	}
#ifdef DEBUG
	printf("\nTotal page = %d ",*page_table);
	printf("\nLast Page table addr: %08x ", &page_table[*page_table]);
	printf("\nPage %d phy_addr: %08x , %08x",  *page_table,  phy_addr, page_table[*page_table]);
#endif
	return TRUE;
}


#ifndef MMU_OLD_DMA_INFO
/*
Function         : MemoryDMAInfo
Purpose          : Get an application's DMA information
Scope            : Internal
Input Parameters : mat       The MAT
type      DM type: DM_OS or DM_USER
app       Reserved
Output Parameters: total_block       #of block used for DMM
last_block        always equal 0xffff
Return           : TRUE
ERR_MEM_INV_TYPE
ERR_MEM_NOT_SET
Comment          :
*/
Err MemoryDMAInfo(MatInfoList *mat, UBYTE type, AppID app, USHORT *total_block, USHORT *last_block)
{
	switch(type)
	{
	case DM_OS:
		if(dmos_first_block==0xffff)
		{
			*total_block=0;
			*last_block = 0xffff;
			dmos_first_block = *(sys_page - 1);
		}
		else
		{
			*total_block = *(sys_page -  1) - dmos_first_block;
			*last_block =  0xFFFF;
		}
		break;
	case DM_USER:
		if(dmuser_first_block ==0xffff)
		{
			*total_block=0;
			*last_block = 0xffff;
			dmuser_first_block = *(app_page - 1);
		}
		else
		{
			*total_block = *(app_page -  1) - dmuser_first_block;
			*last_block = 0xFFFF;
		}
		break;
	default:
		return ERR_MEM_INV_TYPE;
	}
	return TRUE;
}
#endif


#ifdef MMU_OLD_DMA_INFO

/*
Function         : MemoryDMAInfo
Purpose          : Get an application's DMA information
Scope            : Internal
Input Parameters : mat       The MAT
type      DM type: DM_OS or DM_USER
app       Reserved
Output Parameters: total_block   #of blocks used by dmm
last_block    last pyshical block# used by dmm                     
Return           : TRUE
ERR_MEM_INV_TYPE
ERR_MEM_NOT_SET
Comment          :
*/
Err MemoryDMAInfo(MatInfoList *mat, UBYTE type, AppID app, USHORT *total_block, USHORT *last_block)
{
	USHORT *dm_first_block, next_block;
	USHORT stotal_block = 0;
	USHORT slast_block = 0;
	
	
	switch(type)
	{
	case DM_OS:
		dm_first_block = &dmos_first_block;
		break;
	case DM_USER:
		dm_first_block = &dmuser_first_block;
		break;
	default:
		return ERR_MEM_INV_TYPE;
	}
	
	if(*dm_first_block == 0xFFFF)  /* not init. */
	{
		*total_block = 0;
		*last_block = 0xFFFF;
		MemoryDMAInfo1(mat, type, app, &stotal_block, &slast_block);
		return ERR_MEM_NOT_SET;
	}
	
	next_block = *last_block = *dm_first_block;
	*total_block = 0;
	
	while(next_block != 0xFFFF)
	{
		*last_block = next_block;
		(*total_block)++;
		if (MemoryNextBlock(mat, *last_block, &next_block)!=TRUE)
			break;
	}
	return TRUE;
}

#endif

/*
Function         : MemoryPageTableInit
Purpose          : Init. sys and app. page table
Scope            : OS
Input Parameters : mat       The MAT
type      DM type: DM_OS or DM_USER
app
Output Parameters: sys_table Pointer to system page table physical address
app_table Pointer to application page table physical address
Return	    : TRUE	Success
FALSE	Fail
Comment          : MUST call when system reset, should not call other then systen reset
*/
Err MemoryPageTableInit(UWORD **sys_table, UWORD **app_table, UWORD **int_table)
{
	UWORD i;
	for(i=0;i<TOTAL_SLOT;i++)
	{
		if (mat_info[i].total_entry)
		{
			SetBlockType(mat_info[i].mat[mat_info[i].total_entry-1].attribute, MAT_BLOCK_PG_TABLE);
			mmu_total_free_block--;
			sys_page_table = (UWORD*) BlockAddr1(mat_info[i], mat_info[i].total_entry-1);
			*sys_page_table = 0;
			int_page_table = (UWORD*)(sys_page_table + BLOCK_SIZE / 2 / sizeof(UWORD));
			*int_page_table = 0;
			SetBlockType(mat_info[i].mat[mat_info[i].total_entry-2].attribute, MAT_BLOCK_PG_TABLE);
			mmu_total_free_block--;
			app_page_table = (UWORD*) BlockAddr1(mat_info[i], mat_info[i].total_entry-2);
			*app_page_table = 0;
			MemoryGetPageTable(sys_table, app_table, int_table);
			return TRUE;
		}
	}
	return FALSE;
}

/*
Function         : MemoryGetPageTable
Purpose          : Return the page table pointer
Scope            : OS
Input Parameters : None
Output Parameters: sys_table Pointer to system page table physical address
app_table Pointer to application page table physical address
Return	    : None
Comment          :
*/
void MemoryGetPageTable(UWORD **sys_table, UWORD **app_table, UWORD **int_table)
{
	if(sys_table)
		*sys_table = (UWORD*) sys_page_table + 1;
	if(app_table)
		*app_table = (UWORD*) app_page_table + 1;
	if(int_table)
		*int_table = (UWORD*) int_page_table + 1;
}


/*
Function         : MemoryInstallProg
Purpose          : Install a new program
Scope            : All
Input Parameters : name	application name, terminate with '\0', max. 19 character
size      application size
type      application type, APP_TYPE_APP or APP_TYPE_DRV
Output Parameters: appid     application ID
data	data pointer for application code
ptable    PageTable
Return	    : TRUE
ERR_MEM_NO_SPACE
ERR_SYS_APP_EXIST
ERR_SYS_REG_FULL
Comment          :
*/
Err MemoryInstallProg(BYTE *name, UWORD size, UBYTE attr, UBYTE run_spec, UWORD data_size, UWORD res_offset, AppID *appid, UBYTE **data, UWORD **ptable)
{
	UWORD i, j;
	USHORT block_need, pg_block_need;
	USHORT first_block, next_block;
	UWORD *page_table;
	UWORD *res_addr;
	Err result;
	
	if(size > 8 * 1024*1024)
		return  ERR_MEM_NO_SPACE;
	
	if(SysGetAppID(name, appid) == TRUE)
	{
#ifdef DEBUG
		printf("\n[MMU] AppID exist, quit ");
#endif
		return ERR_SYS_APP_EXIST;
	}
	
	
	block_need = (size / BLOCK_SIZE) + 1 + (data_size / BLOCK_SIZE + 1);
	pg_block_need = block_need ;
	
#ifdef DEBUG
	printf("\nblock need = %d ", block_need);
#endif        
	
	for(i=0;i<TOTAL_SLOT;i++)  /* find memory for the prog */
	{
		if(MemoryNewBlock(&mat_info[i], pg_block_need, 0xFFFFFFFF, MAT_BLOCK_PRG_RES, 0xFFFF, &first_block) == TRUE)
			break;
	}
	if(i>TOTAL_SLOT)
		return ERR_MEM_NO_SPACE;
	
	/* build the page table & set the page table to global */
	if((page_table = (UWORD*) pmalloc(sizeof(UWORD*) * (pg_block_need + 4)))==NULL)
		return ERR_MEM_NO_SPACE;
	
#ifdef PC_SIM
	*data = (UBYTE*) BlockAddr1(mat_info[i], first_block);
#else
	*data = (UBYTE*) ((*app_page_table) * BLOCK_SIZE + USER_MEM_BASE);  /* pointer for application */
#endif
	
	page_table[0] = block_need;  /* number of entries */
	
	*ptable = page_table + 1;
	
	if (strlen((const char*)name) > 19)         /* check name length */
		name[19] = 0x00;
#ifdef PC_SIM
	res_addr = (UWORD*) (BlockAddr1(mat_info[i], first_block) + res_offset);
#else
	res_addr = (UWORD*) (USER_MEM_BASE + res_offset);
#endif
	
	if(data_size == 0)
		data_size = 100;
	
	result = SysNewApp(name, attr, first_block, run_spec, res_addr, data_size, page_table + 1, appid);  /* skip the number of entry (1st word) */
	
	if(result != TRUE)
	{
		pfree(page_table);
		MemoryReleaseBlock(&mat_info[i], first_block, TRUE, TRUE);
		return result;
	}
	
	/* fill the page table */
	next_block = first_block;
	
	for(j=0;j<pg_block_need;j++)
	{       /* MAT id = appid */
		MemoryPageTableAppend(DM_USER, SysGetActiveAppID(), BlockAddr1(mat_info[i],next_block));  /* for the installer */
#ifdef DEUBG            
        printf("\nPhysical address: %08x ", BlockAddr1(mat_info[i],next_block));
#endif        
		mat_info[i].mat[next_block].id = (DatabaseID) *appid;
		/* fill the physical address */
		page_table[j+1] = BlockAddr1(mat_info[i],next_block);
		MemoryNextBlock(&mat_info[i], next_block, &next_block);
	}
	mat_info[i].mat[first_block].id = (UWORD)*appid  | 0x80000000;
	
	return TRUE;
}


/*
Function         : MemoryRegFlashProg
Purpose          : Install a flash program
Scope            : All
Input Parameters : name	application name, terminate with '\0', max. 19 character (exclude '\0')
size      application code + res size
type      application type, APP_TYPE_APP or APP_TYPE_DRV
Output Parameters: appid     application ID
ptable    PageTable
Return	    : TRUE
ERR_MEM_NO_SPACE
ERR_SYS_APP_EXIST
ERR_SYS_REG_FULL
Comment          : data_bss_size, prog_address sholud be 4-K align
res_offset should be word align
*/

Err MemoryRegFlashProg(BYTE *name, UWORD prog_address, UWORD prog_size,
					   UWORD res_offset, UWORD data_bss_size, UBYTE attr,  UBYTE run_spec, AppID *appid,
					   UWORD **ptable)
{
	UWORD j;
	USHORT block_need;
	UWORD *page_table;
	UWORD *res_addr;
	Err result;
	
	attr |= BIT_FLASH_PRG;
	
	if(SysGetAppID(name, appid) == TRUE)
		return ERR_SYS_APP_EXIST;
	
	block_need = (prog_size / BLOCK_SIZE) + 1 + (data_bss_size / BLOCK_SIZE + 1);
	
	/* build the page table & set the page table to global */
	/* The first UWORD is the number of entry in the PT */
	if((page_table = (UWORD*) pmalloc(sizeof(UWORD*) * (block_need + 1)))==NULL)
		return ERR_MEM_NO_SPACE;
	
	if(ptable)
		*ptable = page_table + 1;
	
	page_table[0] = block_need;  /* number of entries */
	
	if (strlen((const char*)name) > 19)         /* check name length */
		name[19] = 0x00;
#ifdef PC_SIM
	res_addr = (UWORD*)(prog_address + res_offset);
#else
	res_addr = (UWORD*) (USER_MEM_BASE + res_offset);
#endif
	
	result = SysNewApp(name, attr, prog_address, run_spec, res_addr, data_bss_size, page_table + 1, appid);  /* skip the number of entry (1st word) */
	if(result != TRUE)
	{
		pfree(page_table);
		return result;
	}
	
	/* fill the page table */
	
	for(j=0;j<((prog_size + data_bss_size) / BLOCK_SIZE + 1);j++)
	{
		/* fill the physical address for prog*/
		page_table[j+1] = prog_address + BLOCK_SIZE * j;
		
	}
	
	return TRUE;
}


/*
Function         : MemoryAppStartBlock
Purpose          : Find the start block of an application
Scope            : OS
Input Parameters : app_id	   application ID
Output Parameters: mat          The MAT where the application resident
start_block  The block number
Return	    : TRUE
ERR_APP_MISS app not found
Comment          :
*/
Err MemoryAppStartBlock(AppID app_id, MatInfoList **mat, USHORT *start_block)
{
	UWORD i;
	WORD j;
	
	for(i=0;i<TOTAL_SLOT;i++)
		for(j=0;j<mat_info[i].total_entry;j++)
			if((mat_info[i].mat[j].id == (DatabaseID) ((UWORD)app_id | 0x80000000))
				&&(BlockType(mat_info[i].mat[j].attribute) == MAT_BLOCK_PRG_RES))
			{
				*mat = &mat_info[i];
				*start_block = j;
				return TRUE;
			}
			return ERR_APP_MISS;
}

/*
Function         : MemoryRemoveProg
Purpose          : UnInstall a program
Scope            : All
Input Parameters : app_id	application ID
Output Parameters: None
Return	    : TRUE
ERR_APP_MISS	not found
ERR_APP_ACTIVE    cannot remove it-self
Comment          :
*/
Err MemoryRemoveProg(AppID app_id)
{
	USHORT start_block;
	MatInfoList *mat;
	
	if(SysGetAppEntryIndex(app_id, NULL) != TRUE)
		return ERR_APP_MISS;
	
	if (SysGetActiveAppID() == app_id)
		return ERR_APP_ACTIVE;
	
	SysRemoveApp(app_id);              /* remove from registry */
	
	if((MemoryAppStartBlock(app_id, (MatInfoList**) &mat, &start_block)) != TRUE)
		return ERR_APP_MISS;
	
	MemoryReleaseBlock(mat, start_block, TRUE, TRUE);
	
	return TRUE;
	
}

UWORD MemoryTotalSysMem(UWORD *sys_used)
{
	if(sys_used)
		*sys_used =  (UWORD) (((UWORD)&mem_alloc_start) & 0x00ffffff);
	
	return total_sys_mem;
}

UWORD SysGetCrashStatus()
{
	return sys_crashed;
}

void SysSetCrashStatus(UBYTE status)
{
	sys_crashed = status;
}

/*
Function         : MemoryCheckMemLow
Purpose          : Check memory low
Scope            : All
Input Parameters : None
Output Parameters: None
Return           : TRUE       Memory low
FALSE      Memory not low
Comment          :
*/
BOOLEAN MemoryCheckMemLow()
{
	return (MemoryTotalFree() <= FREE_MEM_BLOCK_LIMIT);
}

extern BYTE user_mem_init;
extern WORD dm_os_init;
extern BYTE writable;

void MMUInit()
{
	UWORD i;
#ifdef DEBUG
	printf("\n[MMU] Init memory ");
#endif
	
#ifdef PC_SIM
	for(UWORD j=0;j<4;j++)
		for(i=0;i<PC_SIM_MEM_SIZE;i++)
			sim_mem[j][i] = 0x8f;
#endif
		opendb_table = NULL;
		dmos_first_block = 0xFFFF;
		dmuser_first_block = 0xFFFF;
        run_mode = RUN_MODE_NORMAL;
		os_dynamic_mem_address = (UWORD*) 0xFFFFFFFF;
		os_dynamic_mem_size  = 0;
		user_dynamic_mem_address = (UWORD*) 0xFFFFFFFF;
		user_dynamic_mem_size  = 0;
		last_found_free_block = 0;
		user_mem_init = 0;
		dm_os_init = 0;
		dmuser_slot = 0;
		dmos_slot = 0;
		mmu_total_free_block = 0;
        total_sys_mem = 0;
		dbcache.dbid = 0xffffffff;
		SysSetCrashStatus(1);
		sys_page_table = NULL;
		app_page_table = NULL;
		SysInit();
		MemoryBuildMemBankMap();
		MemoryBuildMAT();
		MemoryPageTableInit(NULL,NULL,NULL);
		
        os_mm_init();
        user_mm_init();
		
		dmos_first_block = 0xFFFF;
		dmuser_first_block = 0xFFFF;
		
        writable = 1;
		
		mat_info[0].mat[0].next = 1;
		mat_info[0].mat[1].next = 2;
		mat_info[0].mat[2].next = 3;
		for(i=0;i<TOTAL_SLOT;i++)
			UpdateMATCRC((&mat_info[i]));
		MemDataCRC = MemoryDataCheckSum();
		
#ifdef DEBUG
		printf("\n-------------------------------------------------------\n");
#endif
		
}

