Proper RTC emulation based on work by Mahod.
Updated README.md. Updated AMD makefile.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# PCem Experimental [](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 [](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:
|
||||
|
||||
[](https://kiwiirc.com/client/irc.rol.im/?nick=pcem|?#pcem-x)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
392
src/nvr.c
392
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);
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
|
2
src/pc.c
2
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);
|
||||
|
261
src/rtc.c
Normal file
261
src/rtc.c
Normal file
@@ -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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
186
src/rtc.h
Normal file
186
src/rtc.h
Normal file
@@ -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, /*!<RS1*/
|
||||
RTCRS2 = 0b100, /*!<RS2*/
|
||||
RTCRS3 = 0b1000, /*!<RS3*/
|
||||
/* 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. */
|
||||
RTCDV0 = 0b10000,
|
||||
RTCDV1 = 0b100000, /*!<DV1*/
|
||||
RTCDV2 = 0b1000000, /*!<DV2*/
|
||||
/* 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. */
|
||||
RTCUIP = 0b10000000
|
||||
};
|
||||
|
||||
/* Register B bitflags */
|
||||
enum RTCRBBITS
|
||||
{
|
||||
/* 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. */
|
||||
RTCDSE = 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. */
|
||||
RTC2412 = 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. */
|
||||
RTCDM = 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. */
|
||||
RTCSQWE = 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. */
|
||||
RTCUIE = 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. */
|
||||
RTCAIE = 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. */
|
||||
RTCPIE = 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. */
|
||||
RTCSET = 0b10000000
|
||||
};
|
||||
|
||||
/* Register C bitflags */
|
||||
enum RTCRCBITS
|
||||
{
|
||||
/* Unused
|
||||
|
||||
These bits are unused in Register C. These bits always read 0 and cannot be written. */
|
||||
RTCRC0 = 0b1,
|
||||
RTCRC1 = 0b10, /*!<Unused*/
|
||||
RTCRC2 = 0b100, /*!<Unused*/
|
||||
RTCRC3 = 0b1000, /*!<Unused*/
|
||||
/* 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. */
|
||||
RTCUF = 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. */
|
||||
RTCAF = 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. */
|
||||
RTCPF = 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. */
|
||||
RTCIRQF = 0b10000000
|
||||
};
|
||||
|
||||
/* Register D bitflags */
|
||||
enum RTCRDBITS
|
||||
{
|
||||
/* Unused
|
||||
|
||||
The remaining bits of Register D are not usable. They cannot be written and they always read 0. */
|
||||
RTCRD0 = 0b1,
|
||||
RTCRD1 = 0b10, /*!<Unused*/
|
||||
RTCRD2 = 0b100, /*!<Unused*/
|
||||
RTCRD3 = 0b1000, /*!<Unused*/
|
||||
RTCRD4 = 0b10000, /*!<Unused*/
|
||||
RTCRD5 = 0b100000, /*!<Unused*/
|
||||
RTCRD6 = 0b1000000, /*!<Unused*/
|
||||
/* 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. */
|
||||
RTCVRT = 0b10000000
|
||||
};
|
||||
|
||||
void rtc_tick();
|
||||
void time_update(char *nvrram, int reg);
|
||||
void time_get(char *nvrram);
|
@@ -339,7 +339,6 @@ static BOOL CALLBACK config_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPAR
|
||||
|
||||
h = GetDlgItem(hdlg, IDC_CHECKSYNC);
|
||||
enable_sync = SendMessage(h, BM_GETCHECK, 0, 0);
|
||||
update_sync();
|
||||
|
||||
h = GetDlgItem(hdlg, IDC_CHECKSERIAL);
|
||||
temp_always_serial = SendMessage(h, BM_GETCHECK, 0, 0);
|
||||
|
@@ -1,48 +0,0 @@
|
||||
#include <windows.h>
|
||||
#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;
|
||||
}
|
Reference in New Issue
Block a user