-/*\r
- FUJITSU FMR-30 Emulator 'eFMR-30'\r
-\r
- Author : Takeda.Toshiya\r
- Date : 2008.12.30 -\r
-\r
- [ rtc ]\r
-*/\r
-\r
-#include "rtc.h"\r
-#include "../i8259.h"\r
-#include "../../fileio.h"\r
-\r
-#define EVENT_1HZ 0\r
-#define EVENT_32HZ 1\r
-#define EVENT_DONE 2\r
-\r
-#define POWON 8\r
-#define TCNT 34\r
-#define CKHM 35\r
-#define CKL 36\r
-#define POWOF 36\r
-#define POFMI 37\r
-#define POFH 38\r
-#define POFD 39\r
-\r
-void RTC::initialize()\r
-{\r
- // load rtc regs image\r
- memset(regs, 0, sizeof(regs));\r
- regs[POWON] = 0x10; // cleared\r
- \r
- FILEIO* fio = new FILEIO();\r
- if(fio->Fopen(emu->bios_path(_T("RTC.BIN")), FILEIO_READ_BINARY)) {\r
- fio->Fread(regs + 8, 32, 1);\r
- fio->Fclose();\r
- }\r
- delete fio;\r
- \r
- // init registers\r
-// regs[POWON] &= 0x1f; // local power on\r
-// regs[POWOF] = 0x80; // program power off\r
- regs[POWON] = 0x10; // cleared\r
- regs[POWOF] = 0x20; // illegal power off\r
- regs[TCNT] = 0;\r
- update_checksum();\r
- \r
- rtcmr = rtdsr = 0;\r
- \r
- // update calendar\r
- emu->get_host_time(&cur_time);\r
- read_from_cur_time();\r
- \r
- // register event\r
- register_event_by_clock(this, EVENT_1HZ, CPU_CLOCKS, true, ®ister_id);\r
- register_event_by_clock(this, EVENT_32HZ, CPU_CLOCKS >> 5, true, NULL);\r
-}\r
-\r
-void RTC::release()\r
-{\r
- // set power off time\r
- regs[POFMI] = TO_BCD(cur_time.minute);\r
- regs[POFH] = TO_BCD(cur_time.hour);\r
- regs[POFD] = TO_BCD(cur_time.day);\r
- \r
- // save rtc regs image\r
- FILEIO* fio = new FILEIO();\r
- if(fio->Fopen(emu->bios_path(_T("RTC.BIN")), FILEIO_WRITE_BINARY)) {\r
- fio->Fwrite(regs + 8, 32, 1);\r
- fio->Fclose();\r
- }\r
- delete fio;\r
-}\r
-\r
-void RTC::write_io16(uint32 addr, uint32 data)\r
-{\r
- switch(addr) {\r
- case 0:\r
- rtcmr = data;\r
- break;\r
- case 2:\r
- // echo reset\r
- rtdsr &= ~(data & 0xe);\r
- update_intr();\r
- break;\r
- case 4:\r
- if(!(rtdsr & 1)) {\r
- rtadr = data;\r
- rtdsr |= 1;\r
- // register event\r
- register_event(this, EVENT_DONE, 100, false, NULL);\r
- }\r
- break;\r
- case 6:\r
- rtobr = data;\r
- break;\r
- }\r
-}\r
-\r
-uint32 RTC::read_io16(uint32 addr)\r
-{\r
- switch(addr) {\r
- case 2:\r
- return rtdsr;\r
- case 6:\r
- return rtibr;\r
- }\r
- return 0xffff;\r
-}\r
-\r
-void RTC::event_callback(int event_id, int err)\r
-{\r
- if(event_id == EVENT_1HZ) {\r
- // update calendar\r
- if(cur_time.initialized) {\r
- cur_time.increment();\r
- } else {\r
- emu->get_host_time(&cur_time); // resync\r
- cur_time.initialized = true;\r
- }\r
- read_from_cur_time();\r
- \r
- // 1sec interrupt\r
- rtdsr |= 4;\r
- update_intr();\r
- } else if(event_id == EVENT_32HZ) {\r
- // update tcnt\r
- regs[TCNT]++;\r
- } else if(event_id == EVENT_DONE) {\r
- int ch = (rtadr >> 1) & 0x3f;\r
- if(rtadr & 1) {\r
- // invalid address\r
- } else if(rtadr & 0x80) {\r
- // write\r
- if(ch <= 6) {\r
- regs[ch] = (uint8)rtobr;\r
- write_to_cur_time();\r
- } else if(ch == POWON) {\r
- regs[ch] = (regs[ch] & 0xe0) | (rtobr & 0x1f);\r
- if((rtobr & 0xe0) == 0xc0) {\r
- // reipl\r
- regs[ch] = (regs[ch] & 0x1f) | 0xc0;\r
- vm->reset();\r
- } else if((rtobr & 0xe0) == 0xe0) {\r
- // power off\r
- emu->power_off();\r
- }\r
- update_checksum();\r
- } else if(7 <= ch && ch < 32) {\r
- regs[ch] = (uint8)rtobr;\r
- update_checksum();\r
- }\r
- } else {\r
- // read\r
- if(ch < 40) {\r
- rtibr = regs[ch];\r
- }\r
- }\r
- // update flags\r
- rtdsr &= ~1;\r
- rtdsr |= 2;\r
- update_intr();\r
- }\r
-}\r
-\r
-void RTC::read_from_cur_time()\r
-{\r
- regs[0] = TO_BCD(cur_time.second);\r
- regs[1] = TO_BCD(cur_time.minute);\r
- regs[2] = TO_BCD(cur_time.hour);\r
- regs[3] = cur_time.day_of_week;\r
- regs[4] = TO_BCD(cur_time.day);\r
- regs[5] = TO_BCD(cur_time.month);\r
- regs[6] = TO_BCD(cur_time.year);\r
-}\r
-\r
-void RTC::write_to_cur_time()\r
-{\r
- cur_time.second = FROM_BCD(regs[0]);\r
- cur_time.minute = FROM_BCD(regs[1]);\r
- cur_time.hour = FROM_BCD(regs[2]);\r
-// cur_time.day_of_week = regs[3];\r
- cur_time.day = FROM_BCD(regs[4]);\r
- cur_time.month = FROM_BCD(regs[5]);\r
- cur_time.year = FROM_BCD(regs[6]);\r
- cur_time.update_year();\r
- cur_time.update_day_of_week();\r
- \r
- // restart event\r
- cancel_event(this, register_id);\r
- register_event_by_clock(this, EVENT_1HZ, CPU_CLOCKS, true, ®ister_id);\r
-}\r
-\r
-void RTC::update_checksum()\r
-{\r
- int sum = 0;\r
- for(int i = 8; i < 32; i++) {\r
- sum += regs[i] & 0xf;\r
- sum += (regs[i] >> 4) & 0xf;\r
- }\r
- uint8 ckh = (sum >> 6) & 0xf;\r
- uint8 ckm = (sum >> 2) & 0xf;\r
- uint8 ckl = (sum >> 0) & 3;\r
- \r
- regs[CKHM] = ckh | (ckm << 4);\r
- regs[CKL] = (regs[CKL] & 0xf0) | ckl | 0xc;\r
-}\r
-\r
-void RTC::update_intr()\r
-{\r
- d_pic->write_signal(SIG_I8259_CHIP0 | SIG_I8259_IR1, (rtcmr & rtdsr & 0xe) ? 1 : 0, 1);\r
-}\r
+/*
+ FUJITSU FMR-30 Emulator 'eFMR-30'
+
+ Author : Takeda.Toshiya
+ Date : 2008.12.30 -
+
+ [ rtc ]
+*/
+
+#include "rtc.h"
+#include "../i8259.h"
+#include "../../fileio.h"
+
+#define EVENT_1HZ 0
+#define EVENT_32HZ 1
+#define EVENT_DONE 2
+
+#define POWON 8
+#define TCNT 34
+#define CKHM 35
+#define CKL 36
+#define POWOF 36
+#define POFMI 37
+#define POFH 38
+#define POFD 39
+
+void RTC::initialize()
+{
+ // load rtc regs image
+ memset(regs, 0, sizeof(regs));
+ regs[POWON] = 0x10; // cleared
+
+ FILEIO* fio = new FILEIO();
+ if(fio->Fopen(emu->bios_path(_T("RTC.BIN")), FILEIO_READ_BINARY)) {
+ fio->Fread(regs + 8, 32, 1);
+ fio->Fclose();
+ }
+ delete fio;
+
+ // init registers
+// regs[POWON] &= 0x1f; // local power on
+// regs[POWOF] = 0x80; // program power off
+ regs[POWON] = 0x10; // cleared
+ regs[POWOF] = 0x20; // illegal power off
+ regs[TCNT] = 0;
+ update_checksum();
+
+ rtcmr = rtdsr = 0;
+
+ // update calendar
+ emu->get_host_time(&cur_time);
+ read_from_cur_time();
+
+ // register event
+ register_event_by_clock(this, EVENT_1HZ, CPU_CLOCKS, true, ®ister_id);
+ register_event_by_clock(this, EVENT_32HZ, CPU_CLOCKS >> 5, true, NULL);
+}
+
+void RTC::release()
+{
+ // set power off time
+ regs[POFMI] = TO_BCD(cur_time.minute);
+ regs[POFH] = TO_BCD(cur_time.hour);
+ regs[POFD] = TO_BCD(cur_time.day);
+
+ // save rtc regs image
+ FILEIO* fio = new FILEIO();
+ if(fio->Fopen(emu->bios_path(_T("RTC.BIN")), FILEIO_WRITE_BINARY)) {
+ fio->Fwrite(regs + 8, 32, 1);
+ fio->Fclose();
+ }
+ delete fio;
+}
+
+void RTC::write_io16(uint32 addr, uint32 data)
+{
+ switch(addr) {
+ case 0:
+ rtcmr = data;
+ break;
+ case 2:
+ // echo reset
+ rtdsr &= ~(data & 0xe);
+ update_intr();
+ break;
+ case 4:
+ if(!(rtdsr & 1)) {
+ rtadr = data;
+ rtdsr |= 1;
+ // register event
+ register_event(this, EVENT_DONE, 100, false, NULL);
+ }
+ break;
+ case 6:
+ rtobr = data;
+ break;
+ }
+}
+
+uint32 RTC::read_io16(uint32 addr)
+{
+ switch(addr) {
+ case 2:
+ return rtdsr;
+ case 6:
+ return rtibr;
+ }
+ return 0xffff;
+}
+
+void RTC::event_callback(int event_id, int err)
+{
+ if(event_id == EVENT_1HZ) {
+ // update calendar
+ if(cur_time.initialized) {
+ cur_time.increment();
+ } else {
+ emu->get_host_time(&cur_time); // resync
+ cur_time.initialized = true;
+ }
+ read_from_cur_time();
+
+ // 1sec interrupt
+ rtdsr |= 4;
+ update_intr();
+ } else if(event_id == EVENT_32HZ) {
+ // update tcnt
+ regs[TCNT]++;
+ } else if(event_id == EVENT_DONE) {
+ int ch = (rtadr >> 1) & 0x3f;
+ if(rtadr & 1) {
+ // invalid address
+ } else if(rtadr & 0x80) {
+ // write
+ if(ch <= 6) {
+ regs[ch] = (uint8)rtobr;
+ write_to_cur_time();
+ } else if(ch == POWON) {
+ regs[ch] = (regs[ch] & 0xe0) | (rtobr & 0x1f);
+ if((rtobr & 0xe0) == 0xc0) {
+ // reipl
+ regs[ch] = (regs[ch] & 0x1f) | 0xc0;
+ vm->reset();
+ } else if((rtobr & 0xe0) == 0xe0) {
+ // power off
+ emu->power_off();
+ }
+ update_checksum();
+ } else if(7 <= ch && ch < 32) {
+ regs[ch] = (uint8)rtobr;
+ update_checksum();
+ }
+ } else {
+ // read
+ if(ch < 40) {
+ rtibr = regs[ch];
+ }
+ }
+ // update flags
+ rtdsr &= ~1;
+ rtdsr |= 2;
+ update_intr();
+ }
+}
+
+void RTC::read_from_cur_time()
+{
+ regs[0] = TO_BCD(cur_time.second);
+ regs[1] = TO_BCD(cur_time.minute);
+ regs[2] = TO_BCD(cur_time.hour);
+ regs[3] = cur_time.day_of_week;
+ regs[4] = TO_BCD(cur_time.day);
+ regs[5] = TO_BCD(cur_time.month);
+ regs[6] = TO_BCD(cur_time.year);
+}
+
+void RTC::write_to_cur_time()
+{
+ cur_time.second = FROM_BCD(regs[0]);
+ cur_time.minute = FROM_BCD(regs[1]);
+ cur_time.hour = FROM_BCD(regs[2]);
+// cur_time.day_of_week = regs[3];
+ cur_time.day = FROM_BCD(regs[4]);
+ cur_time.month = FROM_BCD(regs[5]);
+ cur_time.year = FROM_BCD(regs[6]);
+ cur_time.update_year();
+ cur_time.update_day_of_week();
+
+ // restart event
+ cancel_event(this, register_id);
+ register_event_by_clock(this, EVENT_1HZ, CPU_CLOCKS, true, ®ister_id);
+}
+
+void RTC::update_checksum()
+{
+ int sum = 0;
+ for(int i = 8; i < 32; i++) {
+ sum += regs[i] & 0xf;
+ sum += (regs[i] >> 4) & 0xf;
+ }
+ uint8 ckh = (sum >> 6) & 0xf;
+ uint8 ckm = (sum >> 2) & 0xf;
+ uint8 ckl = (sum >> 0) & 3;
+
+ regs[CKHM] = ckh | (ckm << 4);
+ regs[CKL] = (regs[CKL] & 0xf0) | ckl | 0xc;
+}
+
+void RTC::update_intr()
+{
+ d_pic->write_signal(SIG_I8259_CHIP0 | SIG_I8259_IR1, (rtcmr & rtdsr & 0xe) ? 1 : 0, 1);
+}