From 5bad039be619a761f4f16e95c091ee9a232ec8d4 Mon Sep 17 00:00:00 2001 From: javi-s Date: Sun, 30 Oct 2022 16:34:42 +0100 Subject: [PATCH] Emulation of National Semiconductor MM58274 RTC Used in Olivetti M240 --- src/machine/m_xt_olivetti.c | 190 ++++++++++++++++++++++++++++++++++-- src/machine/machine_table.c | 2 +- 2 files changed, 185 insertions(+), 7 deletions(-) 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,