From 0236637c5aa7e5ff7e3d63f8b6a4cea2f6257fb7 Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 11 Jul 2016 01:27:23 +0200 Subject: [PATCH] Proper RTC emulation based on work by Mahod. Updated README.md. Updated AMD makefile. --- README.md | 6 +- src/Makefile.mingw | 4 +- src/Makefile.mingw64 | 4 +- src/Makefile_AMD.mingw | 12 +- src/linux-time.c | 48 ----- src/nvr.c | 392 ++++++++++++----------------------------- src/nvr.h | 5 +- src/pc.c | 2 +- src/rtc.c | 261 +++++++++++++++++++++++++++ src/rtc.h | 186 +++++++++++++++++++ src/win-config.c | 1 - src/win-time.c | 48 ----- 12 files changed, 570 insertions(+), 399 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/README.md b/README.md index bf659faaf..7fd815522 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# PCem Experimental [![Build Status](http://citadel.rol.im:8080/job/PCem-Experimental/badge/icon)](http://citadel.rol.im:8080/job/PCem-Experimental) -PCem Experimental is an unofficial branch of the PCem emulator, which aims to emulate IBM compatible machines from 1981-2000 period. This branch adds several emulated motherboards. +# PCem Unofficial [![Build Status](http://polar.rol.im:8080/job/PCem-Unofficial/badge/icon)](http://polar.rol.im:8080/job/PCem-Unofficial) +PCem Unofficial is an unofficial branch of the PCem emulator, which aims to emulate IBM compatible machines from 1981-2000 period. This branch adds several emulated motherboards. --- -Keep in touch with the PCem Experimental community: +Keep in touch with the PCem Unoffical community: [![Visit our IRC channel](https://kiwiirc.com/buttons/irc.rol.im/pcem-x.png)](https://kiwiirc.com/client/irc.rol.im/?nick=pcem|?#pcem-x) diff --git a/src/Makefile.mingw b/src/Makefile.mingw index a27320401..33b1bacc3 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 acerm3a.o ali1429 device.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o fdc.o fdc37c665.o fdc37c932fr.o fdd.o fdi2raw.o gameport.o headland.o i430hx.o i430lx.o i430fx.o \ i430nx.o i430vx.o i440fx.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 memregs.o model.o mouse.o mouse_ps2.o \ - mouse_serial.o ne2000.o neat.o nethandler.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o \ + mouse_serial.o ne2000.o neat.o nethandler.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o rtc.o \ scat.o serial.o sis496.o sis85c471.o sio.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 acerm3a.o ali1429 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 w83877f.o wd76c10.o win.o win-config.o win-d3d.o win-d3d-fs.o win-ddraw.o \ win-ddraw-fs.o win-ddraw-screenshot.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 SLIRPOBJ = bootp.o ip_icmp.o misc.o socket.o tcp_timer.o cksum.o ip_input.o queue.o tcp_input.o tftp.o debug.o ip_output.o sbuf.o tcp_output.o udp.o if.o mbuf.o slirp.o tcp_subr.o diff --git a/src/Makefile.mingw64 b/src/Makefile.mingw64 index 9992cec0c..f395af9f4 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 acerm3a.o ali1429 device.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o fdc.o fdc37c665.o fdc37c932fr.o fdd.o fdi2raw.o gameport.o headland.o i430hx.o i430lx.o i430fx.o \ i430nx.o i430vx.o i440fx.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 memregs.o model.o mouse.o mouse_ps2.o \ - mouse_serial.o ne2000.o neat.o nethandler.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o \ + mouse_serial.o ne2000.o neat.o nethandler.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o rtc.o \ scat.o serial.o sis496.o sis85c471.o sio.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 acerm3a.o ali1429 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 w83877f.o wd76c10.o win.o win-config.o win-d3d.o win-d3d-fs.o win-ddraw.o \ win-ddraw-fs.o win-ddraw-screenshot.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 SLIRPOBJ = bootp.o ip_icmp.o misc.o socket.o tcp_timer.o cksum.o ip_input.o queue.o tcp_input.o tftp.o debug.o ip_output.o sbuf.o tcp_output.o udp.o if.o mbuf.o slirp.o tcp_subr.o diff --git a/src/Makefile_AMD.mingw b/src/Makefile_AMD.mingw index 0a1978b8d..76e30062f 100644 --- a/src/Makefile_AMD.mingw +++ b/src/Makefile_AMD.mingw @@ -8,28 +8,28 @@ OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acer386sx.o acerm3a.o ali1429 device.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o fdc.o fdc37c665.o fdc37c932fr.o fdd.o fdi2raw.o gameport.o headland.o i430hx.o i430lx.o i430fx.o \ i430nx.o i430vx.o i440fx.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 memregs.o model.o mouse.o mouse_ps2.o \ - mouse_serial.o ne2000.o neat.o nethandler.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o \ + mouse_serial.o ne2000.o neat.o nethandler.o nmi.o nvr.o olivetti_m24.o opti.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o rom.o rtc.o \ scat.o serial.o sis496.o sis85c471.o sio.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 \ soundopenal.o tandy_eeprom.o tandy_rom.o timer.o um8669f.o vid_ati_eeprom.o vid_ati_mach64.o vid_ati18800.o \ - vid_ati28800.o vid_ati68860_ramdac.o vid_cga.o vid_cga_comp.o vid_cl5429.o vid_ega.o vid_et4000.o \ + vid_ati28800.o vid_ati68860_ramdac.o vid_cga.o vid_cl5429.o vid_ega.o vid_et4000.o \ vid_et4000w32.o vid_hercules.o vid_icd2061.o vid_ics2595.o vid_incolor.o vid_mda.o \ vid_olivetti_m24.o vid_oti067.o vid_paradise.o vid_pc1512.o vid_pc1640.o vid_pc200.o \ vid_pcjr.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_stg_ramdac.o vid_svga.o \ 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 w83877f.o wd76c10.o win.o win-config.o win-d3d.o win-d3d-fs.o win-ddraw.o \ win-ddraw-fs.o win-ddraw-screenshot.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 -FMOBJ = dbopl.o + 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 SLIRPOBJ = bootp.o ip_icmp.o misc.o socket.o tcp_timer.o cksum.o ip_input.o queue.o tcp_input.o tftp.o debug.o ip_output.o sbuf.o tcp_output.o udp.o if.o mbuf.o slirp.o tcp_subr.o LIBS = -mwindows -lwinmm -lopenal.dll -lopenal -lddraw -ldinput8 -ldxguid -ld3d9 -ld3dx9 -lwsock32 -liphlpapi -lstdc++ -static-libstdc++ -static-libgcc -static -PCem.exe: $(OBJ) $(FMOBJ) $(SIDOBJ) $(SLIRPOBJ) - $(CC) $(OBJ) $(FMOBJ) $(SIDOBJ) $(SLIRPOBJ) -o "PCem.exe" $(LIBS) +PCem.exe: $(OBJ) $(DBOBJ) $(SIDOBJ) $(SLIRPOBJ) + $(CC) $(OBJ) $(DBOBJ) $(SIDOBJ) $(SLIRPOBJ) -o "PCem.exe" $(LIBS) strip "PCem.exe" all : PCem.exe diff --git a/src/linux-time.c b/src/linux-time.c deleted file mode 100644 index 8b7511d84..000000000 --- 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 13efe84f4..c8a7cce86 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; @@ -14,230 +15,16 @@ int nvr_dosave = 0; static int nvr_onesec_time = 0, nvr_onesec_cnt = 0; -int enable_sync = 0; - -#define second internal_time[0] -#define minute internal_time[1] -#define hour internal_time[2] -#define day internal_time[3] -#define month internal_time[4] -#define year internal_time[5] -int internal_time[6]; -int days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -static int 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; -} - -static int get_days(int org_month, int org_year) -{ - if (org_month != 2) - { - return days_in_month[org_month]; - } - else - { - return is_leap(org_year) ? 29 : 28; - } -} - -static int convert_to_bcd(int number) -{ - int n1, n2; - n1 = number % 10; - n2 = number - n1; - n2 /= 10; - n2 <<= 4; - return (n2 | n1); -} - -static int convert_from_bcd(int number) -{ - int n1, n2; - n1 = number & 0xF; - n2 = number >> 4; - n2 *= 10; - return (n2 + n1); -} - -static int final_form(int isbcd, int number) -{ - return isbcd ? convert_to_bcd(number) : number; -} - -static int original_form(int isbcd, int number) -{ - return isbcd ? convert_from_bcd(number) : number; -} - -static void nvr_recalc_clock() -{ - if (second == 60) - { - second = 0; - minute++; - } - if (minute == 60) - { - minute = 0; - hour++; - } - if (hour == 24) - { - hour = 0; - day++; - } - if (day == (get_days(month, year) + 1)) - { - day = 1; - month++; - } - if (month == 13) - { - month = 1; - year++; - } - nvr_dosave = 1; -} - -static void nvr_update_internal_clock() -{ - second++; - nvr_recalc_clock(); -} - -void nvr_add_10sec() -{ - time_sleep(10000); - if (!enable_sync) - { - second+=10; - nvr_recalc_clock(); - } -} - -static int to_12_hour(int org_hour) -{ - int hour2 = org_hour; - hour2 %= 12; - if (!hour2) hour2 = 12; - return hour2; -} - -static int from_12_hour(int org_hour) -{ - int hour2 = org_hour & 0x7F; - if (hour2 == 12) hour2 = 0; - if (hour & 0x80) hour2 += 12; - return hour2; -} - -static int week_day() -{ - int day_of_month = day; - int month2 = month; - int year2 = year % 100; - int century = ((year - year2) / 100) % 4; - int sum = day_of_month + month2 + year2 + century; - /* (sum mod 7) gives 0 for Saturday, we need it for Monday, so +5 */ - int raw_wd = ((sum + 5) % 7); - /* +1 so 1 = Monday, 7 = Sunday */ - return raw_wd + 1; -} - -/* Called on every get time. */ -static void set_registers() -{ - int is24hour = (nvrram[0xB] & 2) ? 1 : 0; - int isbcd = (nvrram[0xB] & 4) ? 0 : 1; - - uint8_t baknvr[10]; - - memcpy(baknvr,nvrram,10); - - if (AMSTRAD) - { - is24hour = 1; - isbcd = 1; - } - - nvrram[0] = final_form(isbcd, second); - nvrram[2] = final_form(isbcd, minute); - nvrram[4] = is24hour ? final_form(isbcd, hour) : final_form(isbcd, to_12_hour(hour)); - nvrram[6] = week_day(); - nvrram[7] = final_form(isbcd, day); - nvrram[8] = final_form(isbcd, month); - nvrram[9] = final_form(isbcd, (year % 100)); - - 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; -} - -/* Called on NVR load and write. */ -static void get_registers() -{ - int is24hour = (nvrram[0xB] & 2) ? 1 : 0; - int isbcd = (nvrram[0xB] & 4) ? 0 : 1; - - int temp_hour = 0; - - if (AMSTRAD) - { - is24hour = 1; - isbcd = 1; - } - - second = original_form(isbcd, nvrram[0]); - minute = original_form(isbcd, nvrram[2]); - hour = is24hour ? original_form(isbcd, nvrram[4]) : from_12_hour(original_form(isbcd, nvrram[4])); - day = original_form(isbcd, nvrram[7]); - month = original_form(isbcd, nvrram[8]); - year = original_form(isbcd, nvrram[9]) + 1900; -} - void getnvrtime() { - if (enable_sync) - { - /* Get time from host. */ - time_get(nvrram); - } - else - { - /* Get time from internal clock. */ - set_registers(); - } -} - -void update_sync() -{ - if (enable_sync) - { - /* Get time from host. */ - time_get(nvrram); - } - else - { - /* Save time to registers but keep it as is. */ - get_registers(); - } + time_get(nvrram); } void nvr_recalc() { int c; int newrtctime; - c=1<<((nvrram[0xA]&0xF)-1); + c=1<<((nvrram[RTCREGA]&(RTCRS0|RTCRS1|RTCRS2|RTCRS3))-1); newrtctime=(int)(RTCCONST * c * (1 << TIMER_SHIFT)); if (rtctime>newrtctime) rtctime=newrtctime; } @@ -245,42 +32,80 @@ void nvr_recalc() void nvr_rtc(void *p) { int c; - if (!(nvrram[0xA]&0xF)) + if (!(nvrram[RTCREGA]&(RTCRS0|RTCRS1|RTCRS2|RTCRS3))) { rtctime=0x7fffffff; return; } - c=1<<((nvrram[0xA]&0xF)-1); + c=1<<((nvrram[RTCREGA]&(RTCRS0|RTCRS1|RTCRS2|RTCRS3))-1); rtctime += (int)(RTCCONST * c * (1 << TIMER_SHIFT)); // pclog("RTCtime now %f\n",rtctime); - nvrram[0xC] |= 0x40; - if (nvrram[0xB]&0x40) + nvrram[RTCREGC] |= RTCPF; + if (nvrram[RTCREGB]&RTCPIE) { - nvrram[0xC]|=0x80; + nvrram[RTCREGC]|=RTCIRQF; 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); +} + void nvr_onesec(void *p) { nvr_onesec_cnt++; - if (nvr_onesec_cnt >= 100) + if (nvr_onesec_cnt >= 32768) { nvr_onesec_cnt = 0; - /* If sync is disabled, move internal clock ahead by 1 second. */ - if (!enable_sync) nvr_update_internal_clock(); - nvrram[0xC] |= 0x10; - if (nvrram[0xB] & 0x10) + + /* If sync is disabled, move internal clock ahead by 1 second. */ + if (!(nvrram[RTCREGB] & RTCSET)) { - nvrram[0xC] |= 0x80; - if (AMSTRAD) picint(2); - else picint(0x100); + nvr_update_status = RTCUIP; + if (!enable_sync) rtc_tick(); + getnvrtime(); + nvr_dosave = 1; } -// pclog("RTC onesec\n"); } - nvr_onesec_time += (int)(10000 * TIMER_USEC); + else if (nvr_onesec_cnt == 73) /* 73 of our cycles means 244+1984 us = update in progress time per the specification. */ + { + if (!(nvrram[RTCREGB] & RTCSET)) + { + /* Clear update status. */ + nvr_update_status = 0; + + if (nvr_check_alarm(RTCSECONDS) && nvr_check_alarm(RTCMINUTES) && nvr_check_alarm(RTCHOURS)) + { + nvrram[RTCREGC] |= RTCAF; + if (nvrram[RTCREGB] & RTCAIE) + { + nvrram[RTCREGC] |= RTCIRQF; + if (AMSTRAD) picint(2); + else picint(0x100); + } + } + + /* The flag and interrupt should be issued on update ended, not started. */ + nvrram[RTCREGC] |= RTCUF; + if (nvrram[RTCREGB] & RTCUIE) + { + nvrram[RTCREGC] |= RTCIRQF; + if (AMSTRAD) picint(2); + else picint(0x100); + } +// pclog("RTC onesec\n"); + } + } + /* This is correct! The real RTC's one second timer operates at 32768 Hz, not 100 Hz! */ + nvr_onesec_time += (int)((1000000.0 / 32768.0) * TIMER_USEC); } void writenvr(uint16_t addr, uint8_t val, void *priv) @@ -289,45 +114,50 @@ void writenvr(uint16_t addr, uint8_t val, void *priv) // printf("Write NVR %03X %02X %02X %04X:%04X %i\n",addr,nvraddr,val,cs>>4,pc,ins); if (addr&1) { + if (nvraddr==RTCREGC || nvraddr==RTCREGD) 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); - old = nvrram[nvraddr]; - if (nvraddr >= 0xe && nvrram[nvraddr] != val) + if (nvraddr > RTCREGD && nvrram[nvraddr] != val) nvr_dosave = 1; - // if (nvraddr==0xB) update_reg_0B(val); - if (nvraddr!=0xC && nvraddr!=0xD) nvrram[nvraddr]=val; + + old = nvrram[nvraddr]; + nvrram[nvraddr]=val; - /* If not syncing the time with the host, we need to update our internal clock on write. */ - if (!enable_sync) - { - switch(nvraddr) - { - case 0: - case 2: - case 4: - case 6: - case 7: - case 8: - case 9: - if (old != val) - { - get_registers(); - nvr_dosave = 1; - } - return; - } - } - - if (nvraddr==0xA) + if (nvraddr==RTCREGA) { // pclog("NVR rate %i\n",val&0xF); - if (val&0xF) + if (val&(RTCRS0|RTCRS1|RTCRS2|RTCRS3)) { - c=1<<((val&0xF)-1); + c=1<<((val&(RTCRS0|RTCRS1|RTCRS2|RTCRS3))-1); rtctime += (int)(RTCCONST * c * (1 << TIMER_SHIFT)); } else rtctime = 0x7fffffff; } + else + { + old = nvrram[nvraddr]; + nvrram[nvraddr]=val; + if (nvraddr==RTCREGB) + { + if (((old ^ val) & RTCSET) && (val & RTCSET)) + { + nvrram[RTCREGA] &= ~RTCUIP; /* This has to be done according to the datasheet. */ + nvrram[RTCREGB] &= ~RTCUIE; /* This also has to happen per the specification. */ + } + } + + if ((nvraddr < RTCREGA) || (nvraddr == RTCCENTURY)) + { + if ((nvraddr != 1) || (nvraddr != 3) || (nvraddr != 5)) + { + if ((old != val) && !enable_sync) + { + time_update(nvrram, nvraddr); + nvr_dosave = 1; + } + } + } + } } else nvraddr=val&nvrmask; } @@ -338,20 +168,14 @@ 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==RTCREGA) return ((nvrram[RTCREGA] & 0x7F) | nvr_update_status); + if (nvraddr==RTCREGD) nvrram[RTCREGD]|=RTCVRT; + if (nvraddr==RTCREGC) { if (AMSTRAD) picintc(2); else picintc(0x100); - temp=nvrram[0xC]; - nvrram[0xC]=0; + temp=nvrram[RTCREGC]; + nvrram[RTCREGC]=0; /* All flags in register C are unused (always 0) or cleared on read */ return temp; } // if (AMIBIOS && nvraddr==0x36) return 0; @@ -379,7 +203,7 @@ void loadnvr() case ROM_IBMPS1_2121: f = romfopen("nvr/ibmps1_2121.nvr", "rb"); nvrmask = 127; break; case ROM_CMDPC30: f = romfopen("nvr/cmdpc30.nvr", "rb"); nvrmask = 127; break; case ROM_AMI286: f = romfopen("nvr/ami286.nvr", "rb"); nvrmask = 127; break; - case ROM_AWARD286: f = romfopen("nvr/award286.nvr", "rb"); nvrmask = 127; break; + case ROM_AWARD286: f = romfopen("nvr/award286.nvr", "rb"); nvrmask = 127; break; case ROM_DELL200: f = romfopen("nvr/dell200.nvr", "rb"); nvrmask = 127; break; case ROM_IBMAT386: f = romfopen("nvr/at386.nvr", "rb"); nvrmask = 127; break; case ROM_DESKPRO_386: f = romfopen("nvr/deskpro386.nvr", "rb"); break; @@ -413,19 +237,22 @@ void loadnvr() if (!f) { memset(nvrram,0xFF,128); + if (!enable_sync) + { + nvrram[RTCSECONDS] = nvrram[RTCMINUTES] = nvrram[RTCHOURS] = 0; + nvrram[RTCDOM] = nvrram[RTCMONTH] = 1; + nvrram[RTCYEAR] = BCD(80); + nvrram[RTCCENTURY] = BCD(19); + nvrram[RTCREGB]=RTC2412; + } return; } fread(nvrram,128,1,f); - if (!(feof(f))) - fread(internal_time,6,4,f); - else - { - if (!enable_sync) get_registers(); - } + 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[RTCREGA]=(RTCRS1|RTCRS2); + nvrram[RTCREGB]=RTC2412; + c=1<<((nvrram[RTCREGA]&(RTCRS0|RTCRS1|RTCRS2|RTCRS3))-1); rtctime += (int)(RTCCONST * c * (1 << TIMER_SHIFT)); } void savenvr() @@ -443,7 +270,7 @@ void savenvr() case ROM_IBMPS1_2121: f = romfopen("nvr/ibmps1_2121.nvr", "wb"); break; case ROM_CMDPC30: f = romfopen("nvr/cmdpc30.nvr", "wb"); break; case ROM_AMI286: f = romfopen("nvr/ami286.nvr", "wb"); break; - case ROM_AWARD286: f = romfopen("nvr/award286.nvr", "wb"); break; + case ROM_AWARD286: f = romfopen("nvr/award286.nvr", "wb"); break; case ROM_DELL200: f = romfopen("nvr/dell200.nvr", "wb"); break; case ROM_IBMAT386: f = romfopen("nvr/at386.nvr", "wb"); break; case ROM_DESKPRO_386: f = romfopen("nvr/deskpro386.nvr", "wb"); break; @@ -474,10 +301,7 @@ void savenvr() case ROM_KN97: f = romfopen("nvr/kn97.nvr", "wb"); break; default: return; } - /* If sync is disabled, save internal clock to registers. */ - if (!enable_sync) set_registers(); fwrite(nvrram,128,1,f); - fwrite(internal_time,6,4,f); fclose(f); } diff --git a/src/nvr.h b/src/nvr.h index fe3c72e1b..a8ecbfe03 100644 --- a/src/nvr.h +++ b/src/nvr.h @@ -1,11 +1,8 @@ void nvr_init(); -extern int nvr_dosave; extern int enable_sync; -void time_sleep(int count); +extern int nvr_dosave; void time_get(char *nvrram); -void nvr_add_10sec(); -void update_sync(); diff --git a/src/pc.c b/src/pc.c index 21a413c6e..61a8f5fb5 100644 --- a/src/pc.c +++ b/src/pc.c @@ -675,7 +675,7 @@ void loadconfig(char *fn) enable_overscan = config_get_int(NULL, "enable_overscan", 0); enable_flash = config_get_int(NULL, "enable_flash", 1); - enable_sync = config_get_int(NULL, "enable_sync", 0); + enable_sync = config_get_int(NULL, "enable_sync", 1); mouse_always_serial = config_get_int(NULL, "mouse_always_serial", 0); window_w = config_get_int(NULL, "window_w", 0); diff --git a/src/rtc.c b/src/rtc.c new file mode 100644 index 000000000..203b4eaa4 --- /dev/null +++ b/src/rtc.c @@ -0,0 +1,261 @@ +/* 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; + +typedef struct +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; +} +internal_clock_t; + +internal_clock_t internal_clock; + +/* When the RTC was last updated */ +time_t rtc_set_time = 0; + +/* Table for days in each month */ +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++; + } + nvr_dosave = 1; +} + +/* 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 RTCSECONDS: + internal_clock.sec = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCSECONDS] : DCB(nvrram[RTCSECONDS]); + break; + case RTCMINUTES: + internal_clock.min = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCMINUTES] : DCB(nvrram[RTCMINUTES]); + break; + case RTCHOURS: + temp = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCHOURS] : DCB(nvrram[RTCHOURS]); + + if (nvrram[RTCREGB] & RTC2412) + { + internal_clock.hour = temp; + } + else + { + internal_clock.hour = ((temp & ~RTCAMPM) % 12) + ((temp & RTCAMPM) ? 12 : 0); + } + break; + case RTCDOM: + internal_clock.mday = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCDOM] : DCB(nvrram[RTCDOM]); + break; + case RTCMONTH: + internal_clock.mon = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCMONTH] : DCB(nvrram[RTCMONTH]); + break; + case RTCYEAR: + internal_clock.year = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCYEAR] : DCB(nvrram[RTCYEAR]); + internal_clock.year += (nvrram[RTCREGB] & RTCDM) ? 1900 : (DCB(nvrram[RTCCENTURY]) * 100); + break; + case RTCCENTURY: + if (nvrram[RTCREGB] & RTCDM) return; + internal_clock.year %= 100; + internal_clock.year += (DCB(nvrram[RTCCENTURY]) * 100); + break; + case 0xFF: /* Load the entire internal clock state from the NVR. */ + internal_clock.sec = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCSECONDS] : DCB(nvrram[RTCSECONDS]); + internal_clock.min = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCMINUTES] : DCB(nvrram[RTCMINUTES]); + + temp = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCHOURS] : DCB(nvrram[RTCHOURS]); + + if (nvrram[RTCREGB] & RTC2412) + { + internal_clock.hour = temp; + } + else + { + internal_clock.hour = ((temp & ~RTCAMPM) % 12) + ((temp & RTCAMPM) ? 12 : 0); + } + + internal_clock.mday = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCDOM] : DCB(nvrram[RTCDOM]); + internal_clock.mon = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCMONTH] : DCB(nvrram[RTCMONTH]); + internal_clock.year = (nvrram[RTCREGB] & RTCDM) ? nvrram[RTCYEAR] : DCB(nvrram[RTCYEAR]); + internal_clock.year += (nvrram[RTCREGB] & RTCDM) ? 1900 : (DCB(nvrram[RTCCENTURY]) * 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; +} + +/* Periodic RTC update function + See also: nvr_onesec() in nvr.c + */ +void time_get(char *nvrram) +{ + time_t cur_time; + struct tm* cur_time_tm; + int dow, mon, year; + + if (enable_sync) + { + cur_time = time(NULL); + + /* 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[RTCREGB] & RTCDM) + { + nvrram[RTCSECONDS] = cur_time_tm->tm_sec; + nvrram[RTCMINUTES] = cur_time_tm->tm_min; + nvrram[RTCDOW] = cur_time_tm->tm_wday + 1; + nvrram[RTCDOM] = cur_time_tm->tm_mday; + nvrram[RTCMONTH] = cur_time_tm->tm_mon + 1; + nvrram[RTCYEAR] = cur_time_tm->tm_year % 100; + + if (nvrram[RTCREGB] & RTC2412) + { + nvrram[RTCHOURS] = cur_time_tm->tm_hour; + } + else + { + nvrram[RTCHOURS] = (cur_time_tm->tm_hour % 12) ? (cur_time_tm->tm_hour % 12) : 12; + if (cur_time_tm->tm_hour > 11) + { + nvrram[RTCHOURS] |= RTCAMPM; + } + } + } + else + { + nvrram[RTCSECONDS] = BCD(cur_time_tm->tm_sec); + nvrram[RTCMINUTES] = BCD(cur_time_tm->tm_min); + nvrram[RTCDOW] = BCD(cur_time_tm->tm_wday + 1); + nvrram[RTCDOM] = BCD(cur_time_tm->tm_mday); + nvrram[RTCMONTH] = BCD(cur_time_tm->tm_mon + 1); + nvrram[RTCYEAR] = BCD(cur_time_tm->tm_year % 100); + + if (nvrram[RTCREGB] & RTC2412) + { + nvrram[RTCHOURS] = BCD(cur_time_tm->tm_hour); + } + else + { + nvrram[RTCHOURS] = (cur_time_tm->tm_hour % 12) ? BCD(cur_time_tm->tm_hour % 12) : BCD(12); + if (cur_time_tm->tm_hour > 11) + { + nvrram[RTCHOURS] |= RTCAMPM; + } + } + } +} \ No newline at end of file diff --git a/src/rtc.h b/src/rtc.h new file mode 100644 index 000000000..28a254898 --- /dev/null +++ b/src/rtc.h @@ -0,0 +1,186 @@ +#define BCD(X) (((X) % 10) | (((X) / 10) << 4)) +#define DCB(X) ((((X) & 0xF0) >> 4) * 10 + ((X) & 0x0F)) + +enum RTCADDR +{ + RTCSECONDS, + RTCALARMSECONDS, + RTCMINUTES, + RTCALARMMINUTES, + RTCHOURS, + RTCALARMHOURS, + RTCDOW, + RTCDOM, + RTCMONTH, + RTCYEAR, + RTCREGA, + RTCREGB, + RTCREGC, + RTCREGD +}; + +/* 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 RTCCENTURY 0x32 + +/* When the 12-hour format is selected, the higher-order bit of the hours byte represents PM when it is logic 1. */ +#define RTCAMPM 0b10000000 + +/* Register A bitflags */ +enum RTCRABITS +{ +/* 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. */ + RTCRS0 = 0b1, + RTCRS1 = 0b10, /*! -#include "ibm.h" -#include "nvr.h" - -void time_sleep(int count) -{ - Sleep(count); -} - -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; -}