/* メモリ関係 */

#include "bootpack.h"

void memory_init(struct memorys *mem)
{
	int i;

	mem->frees = 0;
	mem->maxfrees = 0;
	mem->lostsize = 0;
	mem->losts = 0;
	for(i = 0; i < MEMORY_FREES; i++) {
		mem->free[i].addr = 0;
		mem->free[i].size = 0;
	}
	return;
}

unsigned int memory_test(unsigned int start, unsigned int end)
{
	char flg486 = 0;
	unsigned int eflg, cr0, i;

	/* CPUのキャッシュ機能を停止 */
	eflg = leflags();
	eflg = EFLAGS_AC_BIT;	/* AC-bit on */
	seflags(eflg);
	eflg = leflags();
	if((eflg & EFLAGS_AC_BIT) != 0) {	/* キャッシュに対応していないとoffに戻る */
		flg486 = 1;
	}
	eflg &= ~ EFLAGS_AC_BIT;	/* AC-bit off */
	seflags(eflg);

	if(flg486 != 0) {
		cr0 = lcr0();
		cr0 |= CR0_CACHE_DISABLE;	/* キャッシュ禁止 */
		scr0(cr0);
	}
	i = memory_testsub(start, end);
	if(flg486 != 0) {
		cr0 = lcr0();
		cr0 &= ~ CR0_CACHE_DISABLE;	/* キャッシュ許可 */
		scr0(cr0);
	}
	return i;
}

unsigned int memory_fsize(struct memorys *mem)
{
	unsigned int i, t = 0;

	for(i = 0; i < mem->frees; i++) {
		t += mem->free[i].size;
	}
	return t;
}

unsigned int memory_alloc(struct memorys *mem, unsigned int size)
{
	unsigned int i, a;

	for(i = 0; i < mem->frees; i++) {
		if(mem->free[i].size >= size) {
			a = mem->free[i].addr;
			mem->free[i].addr += size;
			mem->free[i].size -= size;
			if(mem->free[i].size == 0) {
				mem->frees--;
				for(; i < mem->frees; i++) {
					mem->free[i] = mem->free[i + 1];
				}
			}
			return a;
		}
	}
	return 0;
}

int memory_free(struct memorys *mem, unsigned int addr, unsigned int size)
{
	int i, j;

	/* addr順に並べてあるため、場所を決める */
	for(i = 0; i < mem->frees; i++) {
		if(mem->free[i].addr > addr) {
			break;
		}
	}

	/* 前と後ろをまとめてみる */
	if(i > 0) {
		/* 前とまとめられる */
		if(mem->free[i - 1].addr + mem->free[i - 1].size == addr) {
			mem->free[i - 1].size += size;
			if(i < mem->frees) {
				/* 後ろにもまとめられる */
				if(addr + size == mem->free[i].addr) {
					mem->free[i - 1].size += mem->free[i].size;
					/* 前へつめる */
					mem->frees--;
					for(; i < mem->frees; i++) {
						mem->free[i] = mem->free[i + 1];
					}
				}
			}
			return 0;
		}
	}

	/* 後ろとまとめてみる */
	if(i < mem->frees) {
		/* 後ろとまとめられる */
		if(addr + size == mem->free[i].addr) {
			mem->free[i].addr = addr;
			mem->free[i].size += size;
			return 0;
		}
	}

	/* 新たに項目を作っていれてみる */
	if(mem->frees < MEMORY_FREES) {
		/* free[i]より後ろにずらして、格納する */
		for(j = mem->frees; j > i; j--) {
			mem->free[j] = mem->free[j - 1];
		}
		mem->frees++;
		/* freesの最大値の更新 */
		if(mem->maxfrees < mem->frees) {
			mem->maxfrees = mem->frees;
		}
		mem->free[i].addr = addr;
		mem->free[i].size = size;
		return 0;
	}

	/* どこにも格納できずに、失敗 */
	mem->losts++;
	mem->lostsize += size;
	return -1;
}
