/*
	Systems Formulate BUBCOM80 Emulator 'eBUBCOM80'

	Author : Takeda.Toshiya
	Date   : 2018.05.11-

	[ rtc ]
*/

#include "rtc.h"

#define EVENT_1HZ	0

void RTC::initialize()
{
	get_host_time(&cur_time);
	
	register_event_by_clock(this, EVENT_1HZ, CPU_CLOCKS, true, NULL);
}

void RTC::reset()
{
	tmp_time = cur_time; // ???
	ctrl = 0;
}

#define SET_DECIMAL_HI(t, v) \
	t %= 10; \
	t += (v) * 10
#define SET_DECIMAL_LO(t, v) \
	t /= 10; \
	t *= 10; \
	t += (v)
#define GET_DECIMAL_HI(t) TO_BCD_HI(t)
#define GET_DECIMAL_LO(t) TO_BCD_LO(t)

void RTC::write_io8(uint32_t addr, uint32_t data)
{
	switch(addr) {
	case 0x3fed:
		if(ctrl == 0x80 && data != 0x80) {
			cur_time = tmp_time;
			cur_time.initialized = true;
		}
		if(data == 0xc0) {
			tmp_time = cur_time;
		}
		ctrl = data;
		break;
	case 0x3fec:
		SET_DECIMAL_HI(tmp_time.year, data & 0x0f);
		tmp_time.update_year();
		tmp_time.update_day_of_week();
		break;
	case 0x3feb:
		SET_DECIMAL_LO(tmp_time.year, data & 0x0f);
		tmp_time.update_day_of_week();
		break;
	case 0x3fea:
		SET_DECIMAL_HI(tmp_time.month, data & 0x01);
		tmp_time.update_day_of_week();
		break;
	case 0x3fe9:
		SET_DECIMAL_LO(tmp_time.month, data & 0x0f);
		tmp_time.update_day_of_week();
		break;
	case 0x3fe8:
		SET_DECIMAL_HI(tmp_time.day, data & 0x03);
		tmp_time.update_day_of_week();
		break;
	case 0x3fe7:
		SET_DECIMAL_LO(tmp_time.day, data & 0x0f);
		tmp_time.update_day_of_week();
		break;
	case 0x3fe6:
//		tmp_time.day_of_week = data & 0x07;
		break;
	case 0x3fe5:
		SET_DECIMAL_HI(tmp_time.hour, data & 0x03);
		break;
	case 0x3fe4:
		SET_DECIMAL_LO(tmp_time.hour, data & 0x0f);
		break;
	case 0x3fe3:
		SET_DECIMAL_HI(tmp_time.minute, data & 0x07);
		break;
	case 0x3fe2:
		SET_DECIMAL_LO(tmp_time.minute, data & 0x0f);
		break;
	case 0x3fe1:
		SET_DECIMAL_HI(tmp_time.second, data & 0x07);
		break;
	case 0x3fe0:
		SET_DECIMAL_LO(tmp_time.second, data & 0x0f);
		break;
	}
}

uint32_t RTC::read_io8(uint32_t addr)
{
	switch(addr) {
	case 0x3fed:
		return ctrl;
	case 0x3fec:
		return GET_DECIMAL_HI(tmp_time.year);
	case 0x3feb:
		return GET_DECIMAL_LO(tmp_time.year);
	case 0x3fea:
		return GET_DECIMAL_HI(tmp_time.month);
	case 0x3fe9:
		return GET_DECIMAL_LO(tmp_time.month);
	case 0x3fe8:
		return GET_DECIMAL_HI(tmp_time.day);
	case 0x3fe7:
		return GET_DECIMAL_LO(tmp_time.day);
	case 0x3fe6:
		return tmp_time.day_of_week;
	case 0x3fe5:
		return GET_DECIMAL_HI(tmp_time.hour);
	case 0x3fe4:
		return GET_DECIMAL_LO(tmp_time.hour);
	case 0x3fe3:
		return GET_DECIMAL_HI(tmp_time.minute);
	case 0x3fe2:
		return GET_DECIMAL_LO(tmp_time.minute);
	case 0x3fe1:
		return GET_DECIMAL_HI(tmp_time.second);
	case 0x3fe0:
		return GET_DECIMAL_LO(tmp_time.second);
	}
	return 0xff;
}

void RTC::event_callback(int event_id, int err)
{
	if(event_id == EVENT_1HZ) {
		// update calendar
		if(cur_time.initialized) {
			cur_time.increment();
		} else {
			get_host_time(&cur_time);	// resync
			cur_time.initialized = true;
		}
	}
}

#define STATE_VERSION	1

#include "../../statesub.h"

void RTC::decl_state()
{
	enter_decl_state(STATE_VERSION);
	
	DECL_STATE_ENTRY_CUR_TIME_T(cur_time);
	DECL_STATE_ENTRY_CUR_TIME_T(tmp_time);
	DECL_STATE_ENTRY_UINT8(ctrl);

	leave_decl_state();
}

void RTC::save_state(FILEIO* state_fio)
{
	//state_fio->FputUint32(STATE_VERSION);
	//state_fio->FputInt32(this_device_id);
	
	if(state_entry != NULL) {
		state_entry->save_state(state_fio);
	}
	//cur_time.save_state((void *)state_fio);
	//tmp_time.save_state((void *)state_fio);
	//state_fio->FputUint8(ctrl);
}

bool RTC::load_state(FILEIO* state_fio)
{
	bool mb = false;
	if(state_entry != NULL) {
		mb = state_entry->load_state(state_fio);
	}
	if(!mb) return false;
	//if(state_fio->FgetUint32() != STATE_VERSION) {
	//	return false;
	//}
	//if(state_fio->FgetInt32() != this_device_id) {
	//	return false;
	//}
	//if(!cur_time.load_state((void *)state_fio)) {
	//	return false;
	//}
	//if(!tmp_time.load_state((void *)state_fio)) {
	//	return false;
	//}
	//ctrl = state_fio->FgetUint8();
	return true;
}

