Emulation of National Semiconductor MM58274 RTC

Used in Olivetti M240
This commit is contained in:
javi-s
2022-10-30 16:34:42 +01:00
parent 52f700608a
commit 5bad039be6
2 changed files with 185 additions and 7 deletions

View File

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

View File

@@ -2036,7 +2036,7 @@ const machine_t machines[] = {
.max = 640,
.step = 128
},
.nvrmask = 0,
.nvrmask = 15,
.kbc = KBC_OLIVETTI,
.kbc_p1 = 0xff04,
.gpio = 0xffffffff,