#include "sys/alt_stdio.h"
#include <stdio.h>
#include "system.h"
#include "hex_out.h"
#include "lcd_out.h"
#include "sys_register.h"
#include "sys_memory.h"
#include "input_int.h"
#include "sys_debug.h"
#include "sys_prog.h"

#define ledrs (volatile int *) LEDRS_BASE

static void init() {
	// lcd
	lcd_init();
	lcd_print("Starting now...");

	registers_init();
	memory_init();
	// hex
	clear_block(HEX0_3); clear_block(HEX4_5); clear_block(HEX6_7);
	print_block("ve", 2, HEX6_7);
	print_block("r-", 2, HEX4_5);
	print_block("0003", 4, HEX0_3);
	delay10ms(200);
	clear_block(HEX0_3); clear_block(HEX4_5); clear_block(HEX6_7);

	lcd_caret_reset2();
	lcd_print("Ok!");
}

char stack[5];

void store_value(){
	unsigned int memi = global_registers[Ssw_memi];
	memory_store(memi, Ssw_data);

	{	// デバック表示
		char buf[5];
		sprintf(buf, "%02x", (unsigned char)memi);
		print_block(buf, 2, HEX6_7);
		print_block("--", 2, HEX4_5);
		sprintf(buf, "%04d", global_registers[Ssw_data]);
		print_block(buf, 4, HEX0_3);

		display_mem((unsigned char)memi, global_registers[Ssw_data]);
	}
}
void store_inst(){
	char inst;
	char mem_index;
	char reg_index;
	unsigned int stored_pc;
	struct InstRec inst_rec;

	// 必要な情報の取得
	inst = global_registers[Ssw_inst];
	mem_index = global_registers[Ssw_memi];
	reg_index = global_registers[Ssw_regi];
	inst_rec.inst = (unsigned int)inst;
	inst_rec.memi = (unsigned int)mem_index;
	inst_rec.regi = (unsigned int)reg_index;

	// ストア処理
	stored_pc = get_pc();
	inst_memory_store(get_pc(), inst_rec);
	{	// デバック表示
		char buf[5];
		sprintf(buf, "0x%02x", inst_rec.inst);
		print_block(buf, 4, HEX0_3);
		sprintf(buf, "--");
		print_block(buf, 2, HEX4_5);
		sprintf(buf, "%02x", get_pc());
		print_block(buf, 2, HEX6_7);

		display_inst(inst_rec, stored_pc);
	}
	inc_pc();
}

void print_change_memory(unsigned int current_memory) {
	char buf[17];
	sprintf(buf, "Current page:%2d", current_memory);
	lcd_caret_reset();
	lcd_print("Change program");
	lcd_caret_reset2();
	lcd_print(buf);
	clear_block(HEX0_3); clear_block(HEX4_5); clear_block(HEX6_7);
}

static void reset_mem_cancel() {
	lcd_caret_reset();
	lcd_print("rewrite all 0?");
	lcd_caret_reset2();
	lcd_print("push again -> NG");
}
static char reset_mem(char confirmed) {
	if (confirmed == 0) {
			lcd_caret_reset();
			lcd_print("rewrite all 0?");
			lcd_caret_reset2();
			lcd_print("push again");
			// 確認要請
			return 1;
		} else {
			int i;
			for (i = 0; i < MEM_SIZE; i++){
				memory_store(i,Szero);
			}
			lcd_caret_reset();
			lcd_print("rewrite all 0?");
			lcd_caret_reset2();
			lcd_print("push again -> OK");

			// PCリセット完了
			return 0;
	}
}
static void reset_pc_cancel() {
	lcd_caret_reset();
	lcd_print("reset pc?");
	lcd_caret_reset2();
	lcd_print("push again -> NG");
}
static char reset_pc(char confirmed) {
	if (confirmed == 0) {
		lcd_caret_reset();
		lcd_print("reset pc?");
		lcd_caret_reset2();
		lcd_print("push again");
		// 確認要請
		return 1;
	} else {
		set_pc(0);

		lcd_caret_reset();
		lcd_print("reset pc?");
		lcd_caret_reset2();
		lcd_print("push again -> OK");

		// PCリセット完了
		return 0;
	}
}


int main()
{
	init();

	enum RunMode fRun = RUN_STOP;
	char reset_pc_confirmed = 0;
	char reset_mem_confirmed = 0;

	while(1) {
		// interrupt
		in_int();

		// event
		// CANCEL
		if (global_registers[Ssw_rw] == 0
		|| ((PUSH_EVENT & PUSH_ANY) && !(PUSH_EVENT & PUSH_VALSTR))) {
			if (reset_mem_confirmed == 1) {
				reset_mem_confirmed = 0; //確認キャンセル
				reset_mem_cancel();
			}
		}
		if (global_registers[Ssw_rw] == 0
		|| ((PUSH_EVENT & PUSH_ANY) && !(PUSH_EVENT & PUSH_INSSTR))) {
			if (reset_pc_confirmed == 1) {
				reset_pc_confirmed = 0; //確認キャンセル
				reset_pc_cancel();
			}
		}
		// CONFIRM
		if (PUSH_EVENT & PUSH_VALSTR) {
			// 値のストア
			if (global_registers[Ssw_rw] == 1) {
				//削除
				char res = reset_mem(reset_mem_confirmed);//仮確認の承認
				if (1 == res) reset_mem_confirmed = 1; //仮確認
				if (0 == res) reset_mem_confirmed = 0; //リセットの確認
			} else {
				store_value();
			}
		}
		if (PUSH_EVENT & PUSH_INSSTR) {
			// 命令のストア
			if (global_registers[Ssw_rw] == 1) {
				//PCのリセット
				char res = reset_pc(reset_pc_confirmed);//仮確認の承認
				if (1 == res) reset_pc_confirmed = 1; //仮確認
				if (0 == res) reset_pc_confirmed = 0; //リセットの確認
			} else {
				store_inst();
			}
		}
		// RUN
		if (PUSH_EVENT & PUSH_RUN) {
			if (global_current_memory != (unsigned int)global_registers[Ssw_psel]) {
				global_current_memory = (unsigned int)global_registers[Ssw_psel];
				print_change_memory(global_current_memory);
			}
			else {
				// プログラム初期化設定
				fRun = RUN_INIT;
			}
		}
		if (fRun != RUN_STOP) {
			// プログラム実行割り込み & 実行モード更新
			fRun = run_proc(fRun);
		}
	}
	return 0;
}
