/* タスク関係 */

#include "bootpack.h"

struct tasks *tasks;	/* 管理用 */
struct timer *ttimer;	/* タスクスイッチ */

struct task *task_init(void)
{
	struct segdesc *gdt = (struct segdesc *) GDT_ADDR;
	struct task *task, *idle;
	int i;

	tasks = (struct tasks *) memory_alloc(mem, sizeof(struct tasks));
	for(i = 0; i < TASK_MAX; i++) {
		tasks->task[i].flag = TASK_NONE;
		tasks->task[i].sel = (TASK_GDT + i) * 8;
		tasks->task[i].lbyte = 0;
		tasks->task[i].tss.ldtr = (TASK_GDT + TASK_MAX + i) * 8;
		seg_set(gdt + TASK_GDT + i, (int) &tasks->task[i].tss, 103, AR_TSS);
		seg_set(gdt + TASK_GDT + TASK_MAX + i, (int) tasks->task[i].ldt, 15, AR_LDT);
	}
	for(i = 0; i < TASK_LEVELS; i++) {
		tasks->level[i].running = 0;
		tasks->level[i].now = 0;
	}

	/* システム用 */
	task = task_alloc();
	task->flag = TASK_RUNNING;
	task->pri = 1;	/* 0.01s */
	task->lv = 1;	/* レベル1 */
	task_add(task);
	task_switchsub();
	ltr(task->sel);
	task_run(task, 1, 0);
	ttimer = timer_alloc();
	timer_set(ttimer, task->pri);

	/* アイドル用 */
	idle = task_alloc();
	idle->tss.esp = memory_alloc(mem, 65536) + 65536;
	idle->tss.eip = (int) &task_idle;
	idle->tss.es = 1 * 8;
	idle->tss.cs = 2 * 8;
	idle->tss.ss = 1 * 8;
	idle->tss.ds = 1 * 8;
	idle->tss.fs = 1 * 8;
	idle->tss.gs = 1 * 8;
	task_run(idle, TASK_LEVELS - 1, 1);

	/* FPUレジスタ */
	tasks->tfpu = 0;
	return task;
}

struct task *task_alloc(void)
{
	struct task *task;
	int i;

	for(i = 0; i < TASK_MAX; i++) {
		if(tasks->task[i].flag == TASK_NONE) {
			task = &tasks->task[i];
			task->flag = TASK_ALLOC;
			task->tss.eflags = 0x00000202;	/* IF = 1 */
			task->tss.eax = 0;
			task->tss.ecx = 0;
			task->tss.edx = 0;
			task->tss.ebx = 0;
			task->tss.ebp = 0;
			task->tss.esi = 0;
			task->tss.edi = 0;
			task->tss.es = 0;
			task->tss.ds = 0;
			task->tss.fs = 0;
			task->tss.gs = 0;
			task->tss.iomap = 0x40000000;
			task->tss.ss0 = 0;

			task->fpu[0] = 0x037f;	/* control word */
			task->fpu[1] = 0x0000;	/* status word */
			task->fpu[2] = 0xffff;	/* tag word */
			for(i = 3; i < 108 / 4; i++) {
				task->fpu[i] = 0;
			}
			task->lbyte = 0;
			return task;
		}
	}
	return 0;
}

void task_run(struct task *task, int level, int pri)
{
	if(level < 0) {	/* レベルを変更しない */
		level = task->lv;
	}
	if(pri > 0) {	/* 優先度を変更する */
		task->pri = pri;
	}
	if(task->flag == TASK_RUNNING && task->lv != level) {	/* レベルを変更するなら
								 * レベルからタスクを取り除く */
		task_remove(task);
	}
	if(task->flag != TASK_RUNNING) {	/* 実行中でないなら、現在のレベルにタスクを追加 */
		task->lv = level;
		task_add(task);
	}
	tasks->clv = 1;
	return;
}

void task_sleep(struct task *task)
{
	struct task *nt;

	if(task->flag == TASK_RUNNING) {
		nt = task_now();
		task_remove(task);
		if(task == nt) {	/* 自分を除くためタスクスイッチする */
			task_switchsub();
			nt = task_now();
			farjmp(0, nt->sel);
		}
	}
	return;
}

void task_switch(void)
{
	struct tlevel *tl = &tasks->level[tasks->now];
	struct task *nt = tl->t[tl->now], *newt;

	tl->now++;
	if(tl->now == tl->running) {
		tl->now = 0;
	}
	if(tasks->clv != 0) {
		task_switchsub();
		tl = &tasks->level[tasks->now];
	}
	newt = tl->t[tl->now];
	timer_set(ttimer, newt->pri);
	if(newt != nt) {
		farjmp(0, newt->sel);
	}
	return;
}

void task_switchsub(void)
{
	int i;

	/* 最上レベルから検索 */
	for(i = 0; i < TASK_LEVELS; i++) {
		if(tasks->level[i].running > 0) {
			 break;
		}
	}
	tasks->now = i;
	tasks->clv = 0;
	return;
}

struct task *task_now(void)
{
	struct tlevel *tl = &tasks->level[tasks->now];

	return tl->t[tl->now];
}

void task_add(struct task *task)
{
	struct tlevel *tl = &tasks->level[task->lv];

	tl->t[tl->running] = task;
	tl->running++;
	task->flag = TASK_RUNNING;
	return;
}

void task_remove(struct task *task)
{
	struct tlevel *tl = &tasks->level[task->lv];
	int i;

	/* taskの検索 */
	for(i = 0; i < tl->running; i++) {
		if(tl->t[i] == task) {
			break;
		}
	}

	tl->running--;
	if(i < tl->now) {
		tl->now--;
	}
	if(tl->now == tl->running) {
		tl->now = 0;
	}
	task->flag = TASK_SLEEP;

	/* ずらし */
	for(; i < tl->running; i++) {
		tl->t[i] = tl->t[i + 1];
	}
	return;
}

void task_idle(void)
{
	for(;;) {
		hlt();
	}
}
