From 074de516390eefeab7e1e0dc13ede4321d078dc3 Mon Sep 17 00:00:00 2001 From: SarahW Date: Tue, 19 Jul 2016 20:25:27 +0100 Subject: [PATCH] RTC changes : - Replace linux-time.c and win-time.c with universal rtc.c code - Add RTC binary and 12-hour modes, century register, and alarm support - Cleaned up nvr.c a bit - Fix handling of update ended flag - Add ability to disable host clock synchronisation Changes from Battler and Mahod. --- src/Makefile.am | 4 +- src/Makefile.mingw | 4 +- src/Makefile.mingw64 | 4 +- src/linux-time.c | 48 --------- src/nvr.c | 146 ++++++++++++++++++++------ src/nvr.h | 2 + src/pc.c | 5 + src/pc.rc | 11 +- src/resources.h | 1 + src/rtc.c | 244 +++++++++++++++++++++++++++++++++++++++++++ src/rtc.h | 172 ++++++++++++++++++++++++++++++ src/win-config.c | 8 ++ src/win-time.c | 43 -------- src/win.c | 4 + 14 files changed, 561 insertions(+), 135 deletions(-) delete mode 100644 src/linux-time.c create mode 100644 src/rtc.c create mode 100644 src/rtc.h delete mode 100644 src/win-time.c diff --git a/src/Makefile.am b/src/Makefile.am index cb5293c..ace5bf2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,8 +21,8 @@ codegen.c codegen_ops.c codegen_timing_486.c codegen_timing_686.c codegen_timing dac.c device.c disc.c disc_fdi.c disc_img.c disc_sector.c dma.c fdc.c fdc37c665.c fdd.c fdi2raw.c gameport.c \ headland.c i430lx.c i430fx.c i430vx.c ide.c intel.c intel_flash.c io.c jim.c joystick_standard.c joystick_sw_pad.c \ keyboard.c keyboard_amstrad.c keyboard_at.c keyboard_olim24.c keyboard_pcjr.c keyboard_xt.c \ -linux-time.c lpt.c mcr.c mem.c model.c mouse.c mouse_ps2.c mouse_serial.c neat.c nmi.c nvr.c \ -olivetti_m24.c opti.c pc.c pci.c pic.c piix.c pit.c ppi.c ps1.c rom.c scat.c serial.c sis496.c sound.c \ +lpt.c mcr.c mem.c model.c mouse.c mouse_ps2.c mouse_serial.c neat.c nmi.c nvr.c olivetti_m24.c \ +opti.c pc.c pci.c pic.c piix.c pit.c ppi.c ps1.c rom.c rtc.c scat.c serial.c sis496.c sound.c \ sound_ad1848.c sound_adlib.c sound_adlibgold.c sound_cms.c sound_emu8k.c sound_gus.c \ sound_mpu401_uart.c sound_opl.c sound_pas16.c sound_ps1.c sound_pssj.c sound_sb.c sound_sb_dsp.c sound_sn76489.c \ sound_speaker.c sound_ssi2001.c sound_wss.c sound_ym7128.c soundopenal.c tandy_eeprom.c tandy_rom.c thread-pthread.c \ diff --git a/src/Makefile.mingw b/src/Makefile.mingw index 36bc323..2343b7a 100644 --- a/src/Makefile.mingw +++ b/src/Makefile.mingw @@ -8,7 +8,7 @@ OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acer386sx.o ali1429.o amstrad device.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o fdc.o fdc37c665.o fdd.o fdi2raw.o gameport.o headland.o i430lx.o i430fx.o \ i430vx.o ide.o intel.o intel_flash.o io.o jim.o joystick_standard.o joystick_sw_pad.o keyboard.o keyboard_amstrad.o keyboard_at.o \ keyboard_olim24.o keyboard_pcjr.o keyboard_xt.o lpt.o mcr.o mem.o model.o mouse.o mouse_ps2.o \ - mouse_serial.o neat.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o \ + mouse_serial.o neat.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o rtc.o \ scat.o serial.o sis496.o sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_cms.o sound_dbopl.o \ sound_emu8k.o sound_gus.o sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_resid.o \ sound_sb.o sound_sb_dsp.o sound_sn76489.o sound_speaker.o sound_ssi2001.o sound_wss.o \ @@ -20,7 +20,7 @@ OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acer386sx.o ali1429.o amstrad vid_svga_render.o vid_tandy.o vid_tandysl.o vid_tgui9440.o vid_tkd8001_ramdac.o vid_tvga.o vid_unk_ramdac.o \ vid_vga.o vid_voodoo.o video.o wd76c10.o win.o win-config.o win-d3d.o win-d3d-fs.o win-ddraw.o \ win-ddraw-fs.o win-deviceconfig.o win-hdconf.o win-joystick.o win-joystickconfig.o win-keyboard.o win-midi.o win-mouse.o \ - win-status.o win-time.o win-video.o x86seg.o x87.o xtide.o pc.res + win-status.o win-video.o x86seg.o x87.o xtide.o pc.res DBOBJ = dbopl.o vid_cga_comp.o SIDOBJ = convolve.o convolve-sse.o envelope.o extfilt.o filter.o pot.o sid.o voice.o wave6581__ST.o wave6581_P_T.o wave6581_PS_.o wave6581_PST.o wave8580__ST.o wave8580_P_T.o wave8580_PS_.o wave8580_PST.o wave.o diff --git a/src/Makefile.mingw64 b/src/Makefile.mingw64 index c3ce59b..34d039b 100644 --- a/src/Makefile.mingw64 +++ b/src/Makefile.mingw64 @@ -8,7 +8,7 @@ OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acer386sx.o ali1429.o amstrad device.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o fdc.o fdc37c665.o fdd.o fdi2raw.o gameport.o headland.o i430lx.o i430fx.o \ i430vx.o ide.o intel.o intel_flash.o io.o jim.o joystick_standard.o joystick_sw_pad.o keyboard.o keyboard_amstrad.o keyboard_at.o \ keyboard_olim24.o keyboard_pcjr.o keyboard_xt.o lpt.o mcr.o mem.o model.o mouse.o mouse_ps2.o \ - mouse_serial.o neat.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o \ + mouse_serial.o neat.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o rtc.o \ scat.o serial.o sis496.o sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_cms.o sound_dbopl.o \ sound_emu8k.o sound_gus.o sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_resid.o \ sound_sb.o sound_sb_dsp.o sound_sn76489.o sound_speaker.o sound_ssi2001.o sound_wss.o sound_ym7128.o \ @@ -20,7 +20,7 @@ OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acer386sx.o ali1429.o amstrad vid_svga_render.o vid_tandy.o vid_tandysl.o vid_tgui9440.o vid_tkd8001_ramdac.o vid_tvga.o vid_unk_ramdac.o \ vid_vga.o vid_voodoo.o video.o wd76c10.o win.o win-config.o win-d3d.o win-d3d-fs.o win-ddraw.o \ win-ddraw-fs.o win-deviceconfig.o win-hdconf.o win-joystick.o win-joystickconfig.o win-keyboard.o win-midi.o win-mouse.o \ - win-status.o win-time.o win-video.o x86seg.o x87.o xtide.o pc.res + win-status.o win-video.o x86seg.o x87.o xtide.o pc.res DBOBJ = dbopl.o vid_cga_comp.o SIDOBJ = convolve.o convolve-sse.o envelope.o extfilt.o filter.o pot.o sid.o voice.o wave6581__ST.o wave6581_P_T.o wave6581_PS_.o wave6581_PST.o wave8580__ST.o wave8580_P_T.o wave8580_PS_.o wave8580_PST.o wave.o diff --git a/src/linux-time.c b/src/linux-time.c deleted file mode 100644 index 1a111b7..0000000 --- a/src/linux-time.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include "ibm.h" -#include "nvr.h" - -void time_get(char *nvrram) -{ - int c,d; - uint8_t baknvr[10]; - time_t cur_time; - struct tm cur_time_tm; - - memcpy(baknvr,nvrram,10); - - cur_time = time(NULL); - localtime_r(&cur_time, &cur_time_tm); - - d = cur_time_tm.tm_sec % 10; - c = cur_time_tm.tm_sec / 10; - nvrram[0] = d | (c << 4); - d = cur_time_tm.tm_min % 10; - c = cur_time_tm.tm_min / 10; - nvrram[2] = d | (c << 4); - d = cur_time_tm.tm_hour % 10; - c = cur_time_tm.tm_hour / 10; - nvrram[4] = d | (c << 4); - d = cur_time_tm.tm_wday % 10; - c = cur_time_tm.tm_wday / 10; - nvrram[6] = d | (c << 4); - d = cur_time_tm.tm_mday % 10; - c = cur_time_tm.tm_mday / 10; - nvrram[7] = d | (c << 4); - d = cur_time_tm.tm_mon % 10; - c = cur_time_tm.tm_mon / 10; - nvrram[8] = d | (c << 4); - d = cur_time_tm.tm_year % 10; - c = (cur_time_tm.tm_year / 10) % 10; - nvrram[9] = d | (c << 4); - if (baknvr[0] != nvrram[0] || - baknvr[2] != nvrram[2] || - baknvr[4] != nvrram[4] || - baknvr[6] != nvrram[6] || - baknvr[7] != nvrram[7] || - baknvr[8] != nvrram[8] || - baknvr[9] != nvrram[9]) - nvrram[0xA] |= 0x80; -} - diff --git a/src/nvr.c b/src/nvr.c index bfb2985..d429960 100644 --- a/src/nvr.c +++ b/src/nvr.c @@ -4,6 +4,7 @@ #include "nvr.h" #include "pic.h" #include "timer.h" +#include "rtc.h" int oldromset; int nvrmask=63; @@ -23,7 +24,7 @@ void nvr_recalc() { int c; int newrtctime; - c=1<<((nvrram[0xA]&0xF)-1); + c = 1 << ((nvrram[RTC_REGA] & RTC_RS) - 1); newrtctime=(int)(RTCCONST * c * (1 << TIMER_SHIFT)); if (rtctime>newrtctime) rtctime=newrtctime; } @@ -31,64 +32,136 @@ void nvr_recalc() void nvr_rtc(void *p) { int c; - if (!(nvrram[0xA]&0xF)) + if (!(nvrram[RTC_REGA] & RTC_RS)) { rtctime=0x7fffffff; return; } - c=1<<((nvrram[0xA]&0xF)-1); + c = 1 << ((nvrram[RTC_REGA] & RTC_RS) - 1); rtctime += (int)(RTCCONST * c * (1 << TIMER_SHIFT)); // pclog("RTCtime now %f\n",rtctime); - nvrram[0xC] |= 0x40; - if (nvrram[0xB]&0x40) + nvrram[RTC_REGC] |= RTC_PF; + if (nvrram[RTC_REGB] & RTC_PIE) { - nvrram[0xC]|=0x80; + nvrram[RTC_REGC] |= RTC_IRQF; if (AMSTRAD) picint(2); else picint(0x100); // pclog("RTC int\n"); } } +int nvr_update_status = 0; + +#define ALARM_DONTCARE 0xc0 + +int nvr_check_alarm(int nvraddr) +{ + return (nvrram[nvraddr + 1] == nvrram[nvraddr] || (nvrram[nvraddr + 1] & ALARM_DONTCARE) == ALARM_DONTCARE); +} + +int nvr_update_end_count = 0; + +void nvr_update_end(void *p) +{ + if (!(nvrram[RTC_REGB] & RTC_SET)) + { + getnvrtime(); + /* Clear update status. */ + nvr_update_status = 0; + + if (nvr_check_alarm(RTC_SECONDS) && nvr_check_alarm(RTC_MINUTES) && nvr_check_alarm(RTC_HOURS)) + { + nvrram[RTC_REGC] |= RTC_AF; + if (nvrram[RTC_REGB] & RTC_AIE) + { + nvrram[RTC_REGC] |= RTC_IRQF; + if (AMSTRAD) picint(2); + else picint(0x100); + } + } + + /* The flag and interrupt should be issued on update ended, not started. */ + nvrram[RTC_REGC] |= RTC_UF; + if (nvrram[RTC_REGB] & RTC_UIE) + { + nvrram[RTC_REGC] |= RTC_IRQF; + if (AMSTRAD) picint(2); + else picint(0x100); + } + } + +// pclog("RTC onesec\n"); + + nvr_update_end_count = 0; +} + void nvr_onesec(void *p) { nvr_onesec_cnt++; if (nvr_onesec_cnt >= 100) { - nvr_onesec_cnt = 0; - nvrram[0xC] |= 0x10; - if (nvrram[0xB] & 0x10) + if (!(nvrram[RTC_REGB] & RTC_SET)) { - nvrram[0xC] |= 0x80; - if (AMSTRAD) picint(2); - else picint(0x100); + nvr_update_status = RTC_UIP; + /* If sync is disabled, move internal clock ahead by 1 second. */ + if (!enable_sync) + rtc_tick(); + + nvr_update_end_count = (int)((244.0 + 1984.0) * TIMER_USEC); } -// pclog("RTC onesec\n"); } nvr_onesec_time += (int)(10000 * TIMER_USEC); } void writenvr(uint16_t addr, uint8_t val, void *priv) { - int c; + int c, old; // printf("Write NVR %03X %02X %02X %04X:%04X %i\n",addr,nvraddr,val,cs>>4,pc,ins); if (addr&1) { + if (nvraddr==RTC_REGC || nvraddr==RTC_REGD) + return; /* Registers C and D are read-only. There's no reason to continue. */ // if (nvraddr == 0x33) pclog("NVRWRITE33 %02X %04X:%04X %i\n",val,CS,pc,ins); - if (nvraddr >= 0xe && nvrram[nvraddr] != val) + if (nvraddr > RTC_REGD && nvrram[nvraddr] != val) nvr_dosave = 1; - if (nvraddr!=0xC && nvraddr!=0xD) nvrram[nvraddr]=val; - if (nvraddr==0xA) + old = nvrram[nvraddr]; + nvrram[nvraddr]=val; + + if (nvraddr == RTC_REGA) { // pclog("NVR rate %i\n",val&0xF); - if (val&0xF) + if (val & RTC_RS) { - c=1<<((val&0xF)-1); + c = 1 << ((val & RTC_RS) - 1); rtctime += (int)(RTCCONST * c * (1 << TIMER_SHIFT)); } else rtctime = 0x7fffffff; } + else + { + if (nvraddr == RTC_REGB) + { + if (((old ^ val) & RTC_SET) && (val & RTC_SET)) + { + nvrram[RTC_REGA] &= ~RTC_UIP; /* This has to be done according to the datasheet. */ + nvrram[RTC_REGB] &= ~RTC_UIE; /* This also has to happen per the specification. */ + } + } + + if ((nvraddr < RTC_REGA) || (nvraddr == RTC_CENTURY)) + { + if ((nvraddr != 1) && (nvraddr != 3) && (nvraddr != 5)) + { + if ((old != val) && !enable_sync) + { + time_update(nvrram, nvraddr); + nvr_dosave = 1; + } + } + } + } } else nvraddr=val&nvrmask; } @@ -99,20 +172,16 @@ uint8_t readnvr(uint16_t addr, void *priv) // printf("Read NVR %03X %02X %02X %04X:%04X\n",addr,nvraddr,nvrram[nvraddr],cs>>4,pc); if (addr&1) { - if (nvraddr<=0xA) getnvrtime(); - if (nvraddr==0xD) nvrram[0xD]|=0x80; - if (nvraddr==0xA) - { - temp=nvrram[0xA]; - nvrram[0xA]&=~0x80; - return temp; - } - if (nvraddr==0xC) + if (nvraddr == RTC_REGA) + return ((nvrram[RTC_REGA] & 0x7F) | nvr_update_status); + if (nvraddr == RTC_REGD) + nvrram[RTC_REGD] |= RTC_VRT; + if (nvraddr == RTC_REGC) { if (AMSTRAD) picintc(2); else picintc(0x100); - temp=nvrram[0xC]; - nvrram[0xC]=0; + temp = nvrram[RTC_REGC]; + nvrram[RTC_REGC] = 0; return temp; } // if (AMIBIOS && nvraddr==0x36) return 0; @@ -161,13 +230,22 @@ void loadnvr() if (!f) { memset(nvrram,0xFF,128); + if (!enable_sync) + { + nvrram[RTC_SECONDS] = nvrram[RTC_MINUTES] = nvrram[RTC_HOURS] = 0; + nvrram[RTC_DOM] = nvrram[RTC_MONTH] = 1; + nvrram[RTC_YEAR] = BCD(80); + nvrram[RTC_CENTURY] = BCD(19); + nvrram[RTC_REGB] = RTC_2412; + } return; } fread(nvrram,128,1,f); + if (!enable_sync) time_update(nvrram, 0xFF); /* Update the internal clock state based on the NVR registers. */ fclose(f); - nvrram[0xA]=6; - nvrram[0xB]=0; - c=1<<((6&0xF)-1); + nvrram[RTC_REGA] = 6; + nvrram[RTC_REGB] = RTC_2412; + c = 1 << ((nvrram[RTC_REGA] & RTC_RS) - 1); rtctime += (int)(RTCCONST * c * (1 << TIMER_SHIFT)); } void savenvr() @@ -212,4 +290,6 @@ void nvr_init() io_sethandler(0x0070, 0x0002, readnvr, NULL, NULL, writenvr, NULL, NULL, NULL); timer_add(nvr_rtc, &rtctime, TIMER_ALWAYS_ENABLED, NULL); timer_add(nvr_onesec, &nvr_onesec_time, TIMER_ALWAYS_ENABLED, NULL); + timer_add(nvr_update_end, &nvr_update_end_count, &nvr_update_end_count, NULL); + } diff --git a/src/nvr.h b/src/nvr.h index 5539e57..30b7b4b 100644 --- a/src/nvr.h +++ b/src/nvr.h @@ -1,5 +1,7 @@ void nvr_init(); +extern int enable_sync; + extern int nvr_dosave; void time_get(char *nvrram); diff --git a/src/pc.c b/src/pc.c index a6c5d6c..5829c1b 100644 --- a/src/pc.c +++ b/src/pc.c @@ -97,6 +97,7 @@ void fatal(const char *format, ...) va_end(ap); fputs(buf,pclogf); fflush(pclogf); + savenvr(); dumppic(); dumpregs(); exit(-1); @@ -679,6 +680,8 @@ void loadconfig(char *fn) } } } + + enable_sync = config_get_int(NULL, "enable_sync", 1); } void saveconfig() @@ -764,6 +767,8 @@ void saveconfig() } } } + + config_set_int(NULL, "enable_sync", enable_sync); config_save(config_file_default); } diff --git a/src/pc.rc b/src/pc.rc index e3b3c99..65bb8c7 100644 --- a/src/pc.rc +++ b/src/pc.rc @@ -57,13 +57,13 @@ BEGIN END END -ConfigureDlg DIALOGEX 0, 0, 232+40, 292+40+40 +ConfigureDlg DIALOGEX 0, 0, 232+40, 308+40+40 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Configure PCem" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,64,308+40,50,14, WS_TABSTOP - PUSHBUTTON "Cancel",IDCANCEL,128,308+40,50,14, WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,64,324+40,50,14, WS_TABSTOP + PUSHBUTTON "Cancel",IDCANCEL,128,324+40,50,14, WS_TABSTOP COMBOBOX IDC_COMBO1,62,16,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_COMBOVID,62,36,157,120,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Configure", IDC_CONFIGUREVID, 224, 36, 40, 14, WS_TABSTOP @@ -81,8 +81,9 @@ BEGIN CONTROL "Gravis Ultrasound",IDC_CHECKGUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,288,118,10 CONTROL "Innovation SSI-2001",IDC_CHECKSSI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,304,118,10 CONTROL "Composite CGA",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,320,118,10 - CONTROL "Voodoo Graphics",IDC_CHECKVOODOO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,336,118,10 - PUSHBUTTON "Configure", IDC_CONFIGUREVOODOO, 224, 336, 40, 14, WS_TABSTOP + CONTROL "Synchronise time to host clock",IDC_CHECKSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,336,118,10 + CONTROL "Voodoo Graphics",IDC_CHECKVOODOO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,352,118,10 + PUSHBUTTON "Configure", IDC_CONFIGUREVOODOO, 224, 352, 40, 14, WS_TABSTOP LTEXT "Machine :",IDC_STATIC,15,16,40,10 LTEXT "Video :",IDC_STATIC,15,36,34,10 LTEXT "CPU type :",IDC_STATIC,15,56,34,10 diff --git a/src/resources.h b/src/resources.h index 20d314b..f303d67 100644 --- a/src/resources.h +++ b/src/resources.h @@ -47,6 +47,7 @@ #define IDC_CHECKSSI 1015 #define IDC_CHECKVOODOO 1016 #define IDC_CHECKDYNAREC 1017 +#define IDC_CHECKSYNC 1018 #define IDC_STATIC 1020 #define IDC_EDIT1 1030 #define IDC_EDIT2 1031 diff --git a/src/rtc.c b/src/rtc.c new file mode 100644 index 0000000..a3fe581 --- /dev/null +++ b/src/rtc.c @@ -0,0 +1,244 @@ +/* Emulation of: + Dallas Semiconductor DS12C887 Real Time Clock + + http://datasheets.maximintegrated.com/en/ds/DS12885-DS12C887A.pdf + + http://dev-docs.atariforge.org/files/MC146818A_RTC_1984.pdf + */ + +#include +#include +#include +#include +#include +#include "nvr.h" +#include "rtc.h" + +int enable_sync; + +struct +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; +} internal_clock; + +/* When the RTC was last updated */ +static time_t rtc_set_time = 0; + +/* Table for days in each month */ +static int rtc_days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* Called to determine whether the year is leap or not */ +static int rtc_is_leap(int org_year) +{ + if (org_year % 400 == 0) return 1; + if (org_year % 100 == 0) return 0; + if (org_year % 4 == 0) return 1; + return 0; +} + +/* Called to determine the days in the current month */ +static int rtc_get_days(int org_month, int org_year) +{ + if (org_month != 2) + return rtc_days_in_month[org_month]; + else + return rtc_is_leap(org_year) ? 29 : 28; +} + +/* Called when the internal clock gets updated */ +static void rtc_recalc() +{ + if (internal_clock.sec == 60) + { + internal_clock.sec = 0; + internal_clock.min++; + } + if (internal_clock.min == 60) + { + internal_clock.min = 0; + internal_clock.hour++; + } + if (internal_clock.hour == 24) + { + internal_clock.hour = 0; + internal_clock.mday++; + } + if (internal_clock.mday == (rtc_get_days(internal_clock.mon, internal_clock.year) + 1)) + { + internal_clock.mday = 1; + internal_clock.mon++; + } + if (internal_clock.mon == 13) + { + internal_clock.mon = 1; + internal_clock.year++; + } +} + +/* Called when ticking the second */ +void rtc_tick() +{ + internal_clock.sec++; + rtc_recalc(); +} + +/* Called when modifying the NVR registers */ +void time_update(char *nvrram, int reg) +{ + int temp; + + switch(reg) + { + case RTC_SECONDS: + internal_clock.sec = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_SECONDS] : DCB(nvrram[RTC_SECONDS]); + break; + case RTC_MINUTES: + internal_clock.min = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_MINUTES] : DCB(nvrram[RTC_MINUTES]); + break; + case RTC_HOURS: + temp = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_HOURS] : DCB(nvrram[RTC_HOURS]); + + if (nvrram[RTC_REGB] & RTC_2412) + internal_clock.hour = temp; + else + internal_clock.hour = ((temp & ~RTC_AMPM) % 12) + ((temp & RTC_AMPM) ? 12 : 0); + break; + case RTC_DOM: + internal_clock.mday = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_DOM] : DCB(nvrram[RTC_DOM]); + break; + case RTC_MONTH: + internal_clock.mon = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_MONTH] : DCB(nvrram[RTC_MONTH]); + break; + case RTC_YEAR: + internal_clock.year = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_YEAR] : DCB(nvrram[RTC_YEAR]); + internal_clock.year += (nvrram[RTC_REGB] & RTC_DM) ? 1900 : (DCB(nvrram[RTC_CENTURY]) * 100); + break; + case RTC_CENTURY: + if (nvrram[RTC_REGB] & RTC_DM) + return; + internal_clock.year %= 100; + internal_clock.year += (DCB(nvrram[RTC_CENTURY]) * 100); + break; + case 0xFF: /* Load the entire internal clock state from the NVR. */ + internal_clock.sec = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_SECONDS] : DCB(nvrram[RTC_SECONDS]); + internal_clock.min = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_MINUTES] : DCB(nvrram[RTC_MINUTES]); + + temp = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_HOURS] : DCB(nvrram[RTC_HOURS]); + + if (nvrram[RTC_REGB] & RTC_2412) + internal_clock.hour = temp; + else + internal_clock.hour = ((temp & ~RTC_AMPM) % 12) + ((temp & RTC_AMPM) ? 12 : 0); + + internal_clock.mday = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_DOM] : DCB(nvrram[RTC_DOM]); + internal_clock.mon = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_MONTH] : DCB(nvrram[RTC_MONTH]); + internal_clock.year = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_YEAR] : DCB(nvrram[RTC_YEAR]); + internal_clock.year += (nvrram[RTC_REGB] & RTC_DM) ? 1900 : (DCB(nvrram[RTC_CENTURY]) * 100); + break; + } +} + +/* Called to obtain the current day of the week based on the internal clock */ +static int time_week_day() +{ + int day_of_month = internal_clock.mday; + int month2 = internal_clock.mon; + int year2 = internal_clock.year % 100; + int century = ((internal_clock.year - year2) / 100) % 4; + int sum = day_of_month + month2 + year2 + century; + /* (Sum mod 7) gives 0 for Saturday, we need it for Sunday, so +6 for Saturday to get 6 and Sunday 0 */ + int raw_wd = ((sum + 6) % 7); + return raw_wd; +} + +/* Called to get time into the internal clock */ +static void time_internal(struct tm **time_var) +{ + if (*time_var == NULL) + *time_var = (struct tm *)malloc(sizeof(struct tm)); + + (*time_var)->tm_sec = internal_clock.sec; + (*time_var)->tm_min = internal_clock.min; + (*time_var)->tm_hour = internal_clock.hour; + (*time_var)->tm_wday = time_week_day(); + (*time_var)->tm_mday = internal_clock.mday; + (*time_var)->tm_mon = internal_clock.mon - 1; + (*time_var)->tm_year = internal_clock.year - 1900; +} + +static time_t cur_time; +static struct tm *cur_time_tm; + +/* Periodic RTC update function + See also: nvr_onesec() in nvr.c + */ +void time_get(char *nvrram) +{ + int dow, mon, year; + + if (enable_sync) + { + time(&cur_time); + + /* Mingw doesn't support localtime_r */ + #if __MINGW32__ + cur_time_tm = localtime(&cur_time); + #else + #if __MINGW64__ + cur_time_tm = localtime(&cur_time); + #else + localtime_r(&cur_time, &cur_time_tm); + #endif + #endif + } + else + { + time_internal(&cur_time_tm); + } + + if (nvrram[RTC_REGB] & RTC_DM) + { + nvrram[RTC_SECONDS] = cur_time_tm->tm_sec; + nvrram[RTC_MINUTES] = cur_time_tm->tm_min; + nvrram[RTC_DOW] = cur_time_tm->tm_wday + 1; + nvrram[RTC_DOM] = cur_time_tm->tm_mday; + nvrram[RTC_MONTH] = cur_time_tm->tm_mon + 1; + nvrram[RTC_YEAR] = cur_time_tm->tm_year % 100; + + if (nvrram[RTC_REGB] & RTC_2412) + { + nvrram[RTC_HOURS] = cur_time_tm->tm_hour; + } + else + { + nvrram[RTC_HOURS] = (cur_time_tm->tm_hour % 12) ? (cur_time_tm->tm_hour % 12) : 12; + if (cur_time_tm->tm_hour > 11) + nvrram[RTC_HOURS] |= RTC_AMPM; + } + } + else + { + nvrram[RTC_SECONDS] = BCD(cur_time_tm->tm_sec); + nvrram[RTC_MINUTES] = BCD(cur_time_tm->tm_min); + nvrram[RTC_DOW] = BCD(cur_time_tm->tm_wday + 1); + nvrram[RTC_DOM] = BCD(cur_time_tm->tm_mday); + nvrram[RTC_MONTH] = BCD(cur_time_tm->tm_mon + 1); + nvrram[RTC_YEAR] = BCD(cur_time_tm->tm_year % 100); + + if (nvrram[RTC_REGB] & RTC_2412) + { + nvrram[RTC_HOURS] = BCD(cur_time_tm->tm_hour); + } + else + { + nvrram[RTC_HOURS] = (cur_time_tm->tm_hour % 12) ? BCD(cur_time_tm->tm_hour % 12) : BCD(12); + if (cur_time_tm->tm_hour > 11) + nvrram[RTC_HOURS] |= RTC_AMPM; + } + } +} diff --git a/src/rtc.h b/src/rtc.h new file mode 100644 index 0000000..91c9304 --- /dev/null +++ b/src/rtc.h @@ -0,0 +1,172 @@ +#define BCD(X) (((X) % 10) | (((X) / 10) << 4)) +#define DCB(X) ((((X) & 0xF0) >> 4) * 10 + ((X) & 0x0F)) + +enum RTC_ADDR +{ + RTC_SECONDS, + RTC_ALARMSECONDS, + RTC_MINUTES, + RTC_ALARMMINUTES, + RTC_HOURS, + RTC_ALARMHOURS, + RTC_DOW, + RTC_DOM, + RTC_MONTH, + RTC_YEAR, + RTC_REGA, + RTC_REGB, + RTC_REGC, + RTC_REGD +}; + +/* The century register at location 32h is a BCD register designed to automatically load the BCD value 20 as the year register changes from 99 to 00. + The MSB of this register is not affected when the load of 20 occurs, and remains at the value written by the user. */ +#define RTC_CENTURY 0x32 + +/* When the 12-hour format is selected, the higher-order bit of the hours byte represents PM when it is logic 1. */ +#define RTC_AMPM 0b10000000 + +/* Register A bitflags */ +enum RTC_RA_BITS +{ +/* Rate Selector (RS0) + + These four rate-selection bits select one of the 13 taps on the 15-stage divider or disable the divider output. + The tap selected can be used to generate an output square wave (SQW pin) and/or a periodic interrupt. + The user can do one of the following: + - Enable the interrupt with the PIE bit; + - Enable the SQW output pin with the SQWE bit; + - Enable both at the same time and the same rate; or + - Enable neither. + + Table 3 lists the periodic interrupt rates and the square wave frequencies that can be chosen with the RS bits. + These four read/write bits are not affected by !RESET. */ + RTC_RS = 0b1111, +/* DV0 + + These three bits are used to turn the oscillator on or off and to reset the countdown chain. + A pattern of 010 is the only combination of bits that turn the oscillator on and allow the RTC to keep time. + A pattern of 11x enables the oscillator but holds the countdown chain in reset. + The next update occurs at 500ms after a pattern of 010 is written to DV0, DV1, and DV2. */ + RTC_DV0 = 0b1110000, +/* Update-In-Progress (UIP) + + This bit is a status flag that can be monitored. When the UIP bit is a 1, the update transfer occurs soon. + When UIP is a 0, the update transfer does not occur for at least 244us. + The time, calendar, and alarm information in RAM is fully available for access when the UIP bit is 0. + The UIP bit is read-only and is not affected by !RESET. + Writing the SET bit in Register B to a 1 inhibits any update transfer and clears the UIP status bit. */ + RTC_UIP = 0b10000000 +}; + +/* Register B bitflags */ +enum RTC_RB_BITS +{ +/* Daylight Saving Enable (DSE) + + This bit is a read/write bit that enables two daylight saving adjustments when DSE is set to 1. + On the first Sunday in April (or the last Sunday in April in the MC146818A), the time increments from 1:59:59 AM to 3:00:00 AM. + On the last Sunday in October when the time first reaches 1:59:59 AM, it changes to 1:00:00 AM. + When DSE is enabled, the internal logic test for the first/last Sunday condition at midnight. + If the DSE bit is not set when the test occurs, the daylight saving function does not operate correctly. + These adjustments do not occur when the DSE bit is 0. This bit is not affected by internal functions or !RESET. */ + RTC_DSE = 0b1, +/* 24/12 + + The 24/12 control bit establishes the format of the hours byte. A 1 indicates the 24-hour mode and a 0 indicates the 12-hour mode. + This bit is read/write and is not affected by internal functions or !RESET. */ + RTC_2412 = 0b10, +/* Data Mode (DM) + + This bit indicates whether time and calendar information is in binary or BCD format. + The DM bit is set by the program to the appropriate format and can be read as required. + This bit is not modified by internal functions or !RESET. A 1 in DM signifies binary data, while a 0 in DM specifies BCD data. */ + RTC_DM = 0b100, +/* Square-Wave Enable (SQWE) + + When this bit is set to 1, a square-wave signal at the frequency set by the rate-selection bits RS3-RS0 is driven out on the SQW pin. + When the SQWE bit is set to 0, the SQW pin is held low. SQWE is a read/write bit and is cleared by !RESET. + SQWE is low if disabled, and is high impedance when VCC is below VPF. SQWE is cleared to 0 on !RESET. */ + RTC_SQWE = 0b1000, +/* Update-Ended Interrupt Enable (UIE) + + This bit is a read/write bit that enables the update-end flag (UF) bit in Register C to assert !IRQ. + The !RESET pin going low or the SET bit going high clears the UIE bit. + The internal functions of the device do not affect the UIE bit, but is cleared to 0 on !RESET. */ + RTC_UIE = 0b10000, +/* Alarm Interrupt Enable (AIE) + + This bit is a read/write bit that, when set to 1, permits the alarm flag (AF) bit in Register C to assert !IRQ. + An alarm interrupt occurs for each second that the three time bytes equal the three alarm bytes, including a don't-care alarm code of binary 11XXXXXX. + The AF bit does not initiate the !IRQ signal when the AIE bit is set to 0. + The internal functions of the device do not affect the AIE bit, but is cleared to 0 on !RESET. */ + RTC_AIE = 0b100000, +/* Periodic Interrupt Enable (PIE) + + The PIE bit is a read/write bit that allows the periodic interrupt flag (PF) bit in Register C to drive the !IRQ pin low. + When the PIE bit is set to 1, periodic interrupts are generated by driving the !IRQ pin low at a rate specified by the RS3-RS0 bits of Register A. + A 0 in the PIE bit blocks the !IRQ output from being driven by a periodic interrupt, but the PF bit is still set at the periodic rate. + PIE is not modified by any internal device functions, but is cleared to 0 on !RESET. */ + RTC_PIE = 0b1000000, +/* SET + + When the SET bit is 0, the update transfer functions normally by advancing the counts once per second. + When the SET bit is written to 1, any update transfer is inhibited, and the program can initialize the time and calendar bytes without an update + occurring in the midst of initializing. Read cycles can be executed in a similar manner. SET is a read/write bit and is not affected by !RESET or + internal functions of the device. */ + RTC_SET = 0b10000000 +}; + +/* Register C bitflags */ +enum RTC_RC_BITS +{ +/* Unused + + These bits are unused in Register C. These bits always read 0 and cannot be written. */ + RTC_RC = 0b1111, +/* Update-Ended Interrupt Flag (UF) + + This bit is set after each update cycle. When the UIE bit is set to 1, the 1 in UF causes the IRQF bit to be a 1, which asserts the !IRQ pin. + This bit can be cleared by reading Register C or with a !RESET. */ + RTC_UF = 0b10000, +/* Alarm Interrupt Flag (AF) + + A 1 in the AF bit indicates that the current time has matched the alarm time. + If the AIE bit is also 1, the !IRQ pin goes low and a 1 appears in the IRQF bit. This bit can be cleared by reading Register C or with a !RESET. */ + RTC_AF = 0b100000, +/* Periodic Interrupt Flag (PF) + + This bit is read-only and is set to 1 when an edge is detected on the selected tap of the divider chain. + The RS3 through RS0 bits establish the periodic rate. PF is set to 1 independent of the state of the PIE bit. + When both PF and PIE are 1s, the !IRQ signal is active and sets the IRQF bit. This bit can be cleared by reading Register C or with a !RESET. */ + RTC_PF = 0b1000000, +/* Interrupt Request Flag (IRQF) + + The interrupt request flag (IRQF) is set to a 1 when one or more of the following are true: + - PF == PIE == 1 + - AF == AIE == 1 + - UF == UIE == 1 + + Any time the IRQF bit is a 1, the !IRQ pin is driven low. + All flag bits are cleared after Register C is read by the program or when the !RESET pin is low. */ + RTC_IRQF = 0b10000000 +}; + +/* Register D bitflags */ +enum RTC_RD_BITS +{ +/* Unused + + The remaining bits of Register D are not usable. They cannot be written and they always read 0. */ + RTC_RD = 0b1111111, +/* Valid RAM and Time (VRT) + + This bit indicates the condition of the battery connected to the VBAT pin. This bit is not writeable and should always be 1 when read. + If a 0 is ever present, an exhausted internal lithium energy source is indicated and both the contents of the RTC data and RAM data are questionable. + This bit is unaffected by !RESET. */ + RTC_VRT = 0b10000000 +}; + +void rtc_tick(); +void time_update(char *nvrram, int reg); +void time_get(char *nvrram); diff --git a/src/win-config.c b/src/win-config.c index 6072f44..3036a7b 100644 --- a/src/win-config.c +++ b/src/win-config.c @@ -11,6 +11,7 @@ #include "fdd.h" #include "gameport.h" #include "model.h" +#include "nvr.h" #include "resources.h" #include "sound.h" #include "video.h" @@ -142,6 +143,9 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR h=GetDlgItem(hdlg, IDC_CHECK4); SendMessage(h, BM_SETCHECK, cga_comp, 0); + h=GetDlgItem(hdlg, IDC_CHECKSYNC); + SendMessage(h, BM_SETCHECK, enable_sync, 0); + h=GetDlgItem(hdlg, IDC_CHECKVOODOO); SendMessage(h, BM_SETCHECK, voodoo_enabled, 0); @@ -277,6 +281,9 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR h = GetDlgItem(hdlg, IDC_CHECKSSI); temp_SSI2001 = SendMessage(h, BM_GETCHECK, 0, 0); + h = GetDlgItem(hdlg, IDC_CHECKSYNC); + enable_sync = SendMessage(h, BM_GETCHECK, 0, 0); + h = GetDlgItem(hdlg, IDC_CHECKVOODOO); temp_voodoo = SendMessage(h, BM_GETCHECK, 0, 0); @@ -302,6 +309,7 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR { if (MessageBox(NULL,"This will reset PCem!\nOkay to continue?","PCem",MB_OKCANCEL)==IDOK) { + savenvr(); model = temp_model; romset = model_getromset(); gfxcard = gfx; diff --git a/src/win-time.c b/src/win-time.c deleted file mode 100644 index 9e49091..0000000 --- a/src/win-time.c +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include "ibm.h" -#include "nvr.h" - -void time_get(char *nvrram) -{ - SYSTEMTIME systemtime; - int c, d; - uint8_t baknvr[10]; - - memcpy(baknvr,nvrram,10); - GetLocalTime(&systemtime); - - d = systemtime.wSecond % 10; - c = systemtime.wSecond / 10; - nvrram[0] = d | (c << 4); - d = systemtime.wMinute % 10; - c = systemtime.wMinute / 10; - nvrram[2] = d | (c << 4); - d = systemtime.wHour % 10; - c = systemtime.wHour / 10; - nvrram[4] = d | (c << 4); - d = systemtime.wDayOfWeek % 10; - c = systemtime.wDayOfWeek / 10; - nvrram[6] = d | (c << 4); - d = systemtime.wDay % 10; - c = systemtime.wDay / 10; - nvrram[7] = d | (c << 4); - d = systemtime.wMonth % 10; - c = systemtime.wMonth / 10; - nvrram[8] = d | (c << 4); - d = systemtime.wYear % 10; - c = (systemtime.wYear / 10) % 10; - nvrram[9] = d | (c << 4); - if (baknvr[0] != nvrram[0] || - baknvr[2] != nvrram[2] || - baknvr[4] != nvrram[4] || - baknvr[6] != nvrram[6] || - baknvr[7] != nvrram[7] || - baknvr[8] != nvrram[8] || - baknvr[9] != nvrram[9]) - nvrram[0xA]|=0x80; -} diff --git a/src/win.c b/src/win.c index 8f383a0..6386726 100644 --- a/src/win.c +++ b/src/win.c @@ -717,6 +717,7 @@ int WINAPI WinMain (HINSTANCE hThisInstance, TerminateThread(mainthreadh,0); // pclog("Quited? %i\n",quited); // pclog("Closepc\n"); + savenvr(); if (save_window_pos && window_remember) saveconfig(); closepc(); @@ -877,18 +878,21 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM case IDM_FILE_RESET: pause=1; Sleep(100); + savenvr(); resetpc(); pause=0; break; case IDM_FILE_HRESET: pause=1; Sleep(100); + savenvr(); resetpchard(); pause=0; break; case IDM_FILE_RESET_CAD: pause=1; Sleep(100); + savenvr(); resetpc_cad(); pause=0; break;