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.
This commit is contained in:
SarahW 2016-07-19 20:25:27 +01:00
commit 074de51639
14 changed files with 561 additions and 135 deletions

View file

@ -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 \

View file

@ -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

View file

@ -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

View file

@ -1,48 +0,0 @@
#include <sys/time.h>
#include <time.h>
#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;
}

146
src/nvr.c
View file

@ -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);
}

View file

@ -1,5 +1,7 @@
void nvr_init();
extern int enable_sync;
extern int nvr_dosave;
void time_get(char *nvrram);

View file

@ -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);
}

View file

@ -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

View file

@ -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

244
src/rtc.c Normal file
View file

@ -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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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;
}
}
}

172
src/rtc.h Normal file
View file

@ -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);

View file

@ -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;

View file

@ -1,43 +0,0 @@
#include <windows.h>
#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;
}

View file

@ -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;