diff --git a/src/machine/m_xt_olivetti.c b/src/machine/m_xt_olivetti.c
index efca944e9..e52450daa 100644
--- a/src/machine/m_xt_olivetti.c
+++ b/src/machine/m_xt_olivetti.c
@@ -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,
* Miran Grca,
@@ -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;
}
diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c
index c7d2be4ec..2f51d559f 100644
--- a/src/machine/machine_table.c
+++ b/src/machine/machine_table.c
@@ -2036,7 +2036,7 @@ const machine_t machines[] = {
.max = 640,
.step = 128
},
- .nvrmask = 0,
+ .nvrmask = 15,
.kbc = KBC_OLIVETTI,
.kbc_p1 = 0xff04,
.gpio = 0xffffffff,