Emulation of National Semiconductor MM58274 RTC
Used in Olivetti M240
This commit is contained in:
@@ -8,7 +8,8 @@
|
||||
*
|
||||
* Emulation of the Olivetti XT-compatible machines.
|
||||
*
|
||||
* - Supports MM58174 real-time clock emulation
|
||||
* - Supports MM58174 real-time clock emulation (M24)
|
||||
* - Supports MM58274 real-time clock emulation (M240)
|
||||
*
|
||||
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* Miran Grca, <mgrca8@gmail.com>
|
||||
@@ -90,6 +91,26 @@ enum MM58174_ADDR {
|
||||
MM58174_IRQ /* Interrupt register, read / write */
|
||||
};
|
||||
|
||||
enum MM58274_ADDR {
|
||||
/* Registers */
|
||||
MM58274_CONTROL, /* Control register */
|
||||
MM58274_TENTHS, /* Tenths of second, read only */
|
||||
MM58274_SECOND1,
|
||||
MM58274_SECOND10,
|
||||
MM58274_MINUTE1,
|
||||
MM58274_MINUTE10,
|
||||
MM58274_HOUR1,
|
||||
MM58274_HOUR10,
|
||||
MM58274_DAY1,
|
||||
MM58274_DAY10,
|
||||
MM58274_MONTH1,
|
||||
MM58274_MONTH10,
|
||||
MM58274_YEAR1,
|
||||
MM58274_YEAR10,
|
||||
MM58274_WEEKDAY,
|
||||
MM58274_SETTINGS /* Settings register */
|
||||
};
|
||||
|
||||
static struct tm intclk;
|
||||
|
||||
typedef struct {
|
||||
@@ -180,7 +201,7 @@ mm58174_time_get(uint8_t *regs, struct tm *tm)
|
||||
|
||||
/* One more second has passed, update the internal clock. */
|
||||
static void
|
||||
mm58174_recalc()
|
||||
mm58x74_recalc()
|
||||
{
|
||||
/* Ping the internal clock. */
|
||||
if (++intclk.tm_sec == 60) {
|
||||
@@ -205,7 +226,7 @@ mm58174_recalc()
|
||||
static void
|
||||
mm58174_tick(nvr_t *nvr)
|
||||
{
|
||||
mm58174_recalc();
|
||||
mm58x74_recalc();
|
||||
mm58174_time_set(nvr->regs, &intclk);
|
||||
}
|
||||
|
||||
@@ -307,6 +328,157 @@ mm58174_init(nvr_t *nvr, int size)
|
||||
mm58174_read, NULL, NULL, mm58174_write, NULL, NULL, nvr);
|
||||
}
|
||||
|
||||
/* Set the chip time. */
|
||||
static void
|
||||
mm58274_time_set(uint8_t *regs, struct tm *tm)
|
||||
{
|
||||
regs[MM58274_SECOND1] = (tm->tm_sec % 10);
|
||||
regs[MM58274_SECOND10] = (tm->tm_sec / 10);
|
||||
regs[MM58274_MINUTE1] = (tm->tm_min % 10);
|
||||
regs[MM58274_MINUTE10] = (tm->tm_min / 10);
|
||||
regs[MM58274_HOUR1] = (tm->tm_hour % 10);
|
||||
regs[MM58274_HOUR10] = (tm->tm_hour / 10);
|
||||
/* Store hour in 24-hour or 12-hour mode */
|
||||
if (regs[MM58274_SETTINGS] & 0x01) {
|
||||
regs[MM58274_HOUR1] = (tm->tm_hour % 10);
|
||||
regs[MM58274_HOUR10] = (tm->tm_hour / 10);
|
||||
} else {
|
||||
regs[MM58274_HOUR1] = ((tm->tm_hour % 12) % 10);
|
||||
regs[MM58274_HOUR10] = (((tm->tm_hour % 12) / 10));
|
||||
if (tm->tm_hour >= 12)
|
||||
regs[MM58274_SETTINGS] |= 0x04;
|
||||
else
|
||||
regs[MM58274_SETTINGS] &= 0x0B;
|
||||
}
|
||||
regs[MM58274_WEEKDAY] = (tm->tm_wday + 1);
|
||||
regs[MM58274_DAY1] = (tm->tm_mday % 10);
|
||||
regs[MM58274_DAY10] = (tm->tm_mday / 10);
|
||||
regs[MM58274_MONTH1] = ((tm->tm_mon + 1) % 10);
|
||||
regs[MM58274_MONTH10] = ((tm->tm_mon + 1) / 10);
|
||||
/* MM58274 can store 00 to 99 years but M240 uses the YEAR1 register to count 8 years from leap year */
|
||||
regs[MM58274_YEAR1] = ((tm->tm_year + 1900) % 8);
|
||||
/* Keep bit 0 and 1 12-hour / 24-hour and AM / PM */
|
||||
regs[MM58274_SETTINGS] &= 0x03;
|
||||
/* Set leap counter bits 2 and 3 */
|
||||
regs[MM58274_SETTINGS] += (4* (regs[MM58274_YEAR1] & 0x03));
|
||||
}
|
||||
|
||||
/* Get the chip time. */
|
||||
static void
|
||||
mm58274_time_get(uint8_t *regs, struct tm *tm)
|
||||
{
|
||||
tm->tm_sec = nibbles(MM58274_SECOND);
|
||||
tm->tm_min = nibbles(MM58274_MINUTE);
|
||||
/* Read hour in 24-hour or 12-hour mode */
|
||||
if (regs[MM58274_SETTINGS] & 0x01)
|
||||
tm->tm_hour = nibbles(MM58274_HOUR);
|
||||
else
|
||||
tm->tm_hour = ((nibbles(MM58274_HOUR) % 12) + (regs[MM58274_SETTINGS] & 0x04) ? 12 : 0);
|
||||
tm->tm_wday = (regs[MM58274_WEEKDAY] - 1);
|
||||
tm->tm_mday = nibbles(MM58274_DAY);
|
||||
tm->tm_mon = (nibbles(MM58274_MONTH) - 1);
|
||||
/* MM58274 can store 00 to 99 years but M240 uses the YEAR1 register to count 8 years from leap year */
|
||||
tm->tm_year = (1984 + regs[MM58274_YEAR1] - 1900);
|
||||
}
|
||||
|
||||
/* This is called every second through the NVR/RTC hook. */
|
||||
static void
|
||||
mm58274_tick(nvr_t *nvr)
|
||||
{
|
||||
mm58x74_recalc();
|
||||
mm58274_time_set(nvr->regs, &intclk);
|
||||
}
|
||||
|
||||
static void
|
||||
mm58274_start(nvr_t *nvr)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
/* Initialize the internal and chip times. */
|
||||
if (time_sync & TIME_SYNC_ENABLED) {
|
||||
/* Use the internal clock's time. */
|
||||
nvr_time_get(&tm);
|
||||
mm58274_time_set(nvr->regs, &tm);
|
||||
} else {
|
||||
/* Set the internal clock from the chip time. */
|
||||
mm58274_time_get(nvr->regs, &tm);
|
||||
nvr_time_set(&tm);
|
||||
}
|
||||
mm58274_time_get(nvr->regs, &intclk);
|
||||
}
|
||||
|
||||
/* Write to one of the chip registers. */
|
||||
static void
|
||||
mm58274_write(uint16_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
nvr_t *nvr = (nvr_t *) priv;
|
||||
|
||||
addr &= 0x0f;
|
||||
val &= 0x0f;
|
||||
|
||||
/* Update non-read-only changed values if not synchronizing time to host */
|
||||
if ((addr != MM58274_TENTHS))
|
||||
if ((nvr->regs[addr] != val) && !(time_sync & TIME_SYNC_ENABLED))
|
||||
nvr_dosave = 1;
|
||||
|
||||
if ((addr == MM58274_CONTROL) && (val & 0x04)) {
|
||||
/* When timer starts, MM58274 sets tenths of second to 0 */
|
||||
nvr->regs[MM58274_TENTHS] = 0;
|
||||
}
|
||||
|
||||
/* Store the new value */
|
||||
nvr->regs[addr] = val;
|
||||
|
||||
/* Update internal clock with MM58274 time */
|
||||
mm58274_time_get(nvr->regs, &intclk);
|
||||
}
|
||||
|
||||
/* Read from one of the chip registers. */
|
||||
static uint8_t
|
||||
mm58274_read(uint16_t addr, void *priv)
|
||||
{
|
||||
nvr_t *nvr = (nvr_t *) priv;
|
||||
|
||||
addr &= 0x0f;
|
||||
|
||||
/* Grab and return the desired value */
|
||||
return (nvr->regs[addr]);
|
||||
}
|
||||
|
||||
/* Reset the MM58274 to a default state. */
|
||||
static void
|
||||
mm58274_reset(nvr_t *nvr)
|
||||
{
|
||||
/* Clear the NVRAM. */
|
||||
memset(nvr->regs, 0xff, nvr->size);
|
||||
|
||||
/* Reset the RTC registers. */
|
||||
memset(nvr->regs, 0x00, 16);
|
||||
nvr->regs[MM58274_WEEKDAY] = 0x01;
|
||||
nvr->regs[MM58274_DAY1] = 0x01;
|
||||
nvr->regs[MM58274_MONTH1] = 0x01;
|
||||
nvr->regs[MM58274_SETTINGS] = 0x01;
|
||||
}
|
||||
|
||||
static void
|
||||
mm58274_init(nvr_t *nvr, int size)
|
||||
{
|
||||
/* This is machine specific. */
|
||||
nvr->size = size;
|
||||
nvr->irq = -1;
|
||||
|
||||
/* Set up any local handlers here. */
|
||||
nvr->reset = mm58274_reset;
|
||||
nvr->start = mm58274_start;
|
||||
nvr->tick = mm58274_tick;
|
||||
|
||||
/* Initialize the actual NVR. */
|
||||
nvr_init(nvr);
|
||||
|
||||
io_sethandler(0x0070, 16,
|
||||
mm58274_read, NULL, NULL, mm58274_write, NULL, NULL, nvr);
|
||||
}
|
||||
|
||||
static void
|
||||
m24_kbd_poll(void *priv)
|
||||
{
|
||||
@@ -975,6 +1147,7 @@ int
|
||||
machine_xt_m240_init(const machine_t *model)
|
||||
{
|
||||
int ret;
|
||||
nvr_t *nvr;
|
||||
|
||||
ret = bios_load_interleaved("roms/machines/m240/olivetti_m240_pch6_2.04_low.bin",
|
||||
"roms/machines/m240/olivetti_m240_pch5_2.04_high.bin",
|
||||
@@ -999,9 +1172,6 @@ machine_xt_m240_init(const machine_t *model)
|
||||
device_add(&keyboard_at_olivetti_device);
|
||||
device_add(&port_6x_olivetti_device);
|
||||
|
||||
/* FIXME: make sure this is correct?? */
|
||||
device_add(&at_nvr_device);
|
||||
|
||||
if (fdc_type == FDC_INTERNAL)
|
||||
device_add(&fdc_xt_device);
|
||||
|
||||
@@ -1010,6 +1180,14 @@ machine_xt_m240_init(const machine_t *model)
|
||||
|
||||
nmi_init();
|
||||
|
||||
/* Allocate an NVR for this machine. */
|
||||
nvr = (nvr_t *) malloc(sizeof(nvr_t));
|
||||
if (nvr == NULL)
|
||||
return (0);
|
||||
memset(nvr, 0x00, sizeof(nvr_t));
|
||||
|
||||
mm58274_init(nvr, model->nvrmask + 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -2036,7 +2036,7 @@ const machine_t machines[] = {
|
||||
.max = 640,
|
||||
.step = 128
|
||||
},
|
||||
.nvrmask = 0,
|
||||
.nvrmask = 15,
|
||||
.kbc = KBC_OLIVETTI,
|
||||
.kbc_p1 = 0xff04,
|
||||
.gpio = 0xffffffff,
|
||||
|
Reference in New Issue
Block a user