summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/mc146818.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/mc146818.c')
-rw-r--r--src/emu/machine/mc146818.c392
1 files changed, 392 insertions, 0 deletions
diff --git a/src/emu/machine/mc146818.c b/src/emu/machine/mc146818.c
new file mode 100644
index 00000000000..eaf2cc98773
--- /dev/null
+++ b/src/emu/machine/mc146818.c
@@ -0,0 +1,392 @@
+/*********************************************************************
+
+ mc146818.c
+
+ Implementation of the MC146818 chip
+
+ Real time clock chip with battery buffered ram (or CMOS)
+ Used in IBM PC/AT, several PC clones, Amstrad NC200
+
+ Nathan Woods (npwoods@mess.org)
+ Peter Trauner (peter.trauner@jk.uni-linz.ac.at)
+
+ PC CMOS info (based on info from Padgett Peterson):
+
+ Clock Related:
+ 0x00 Seconds (BCD 00-59, Hex 00-3B) Note: Bit 7 is read only
+ 0x01 Second Alarm (BCD 00-59, Hex 00-3B; "don't care" if C0-FF)
+ 0x02 Minutes (BCD 00-59, Hex 00-3B)
+ 0x03 Minute Alarm (BCD 00-59, Hex 00-3B; "don't care" if C0-FF))
+ 0x04 Hours (BCD 00-23, Hex 00-17 if 24 hr mode)
+ (BCD 01-12, Hex 01-0C if 12 hr am)
+ (BCD 81-92. Hex 81-8C if 12 hr pm)
+ 0x05 Hour Alarm (same as hours; "don't care" if C0-FF))
+ 0x06 Day of Week (01-07 Sunday=1)
+ 0x07 Date of Month (BCD 01-31, Hex 01-1F)
+ 0x08 Month (BCD 01-12, Hex 01-0C)
+ 0x09 Year (BCD 00-99, Hex 00-63)
+ 0x0B Status Register B (read/write)
+ Bit 7 - 1 enables cycle update, 0 disables
+ Bit 6 - 1 enables periodic interrupt
+ Bit 5 - 1 enables alarm interrupt
+ Bit 4 - 1 enables update-ended interrupt
+ Bit 3 - 1 enables square wave output
+ Bit 2 - Data Mode - 0: BCD, 1: Binary
+ Bit 1 - 24/12 hour selection - 1 enables 24 hour mode
+ Bit 0 - Daylight Savings Enable - 1 enables
+ 0x0C Status Register C (Read only)
+ Bit 7 - Interrupt request flag - 1 when any or all of bits 6-4 are
+ 1 and appropriate enables (Register B) are set to 1. Generates
+ IRQ 8 when triggered.
+ Bit 6 - Periodic Interrupt flag
+ Bit 5 - Alarm Interrupt flag
+ Bit 4 - Update-Ended Interrupt Flag
+ Bit 3-0 ???
+ 0x0D Status Register D (read only)
+ Bit 7 - Valid RAM - 1 indicates batery power good, 0 if dead or
+ disconnected.
+ Bit 6-0 ???
+
+ Non-clock related:
+ 0x0E (PS/2) Diagnostic Status Byte
+ Bit 7 - When set (1) indicates clock has lost power
+ Bit 6 - (1) indicates incorrect checksum
+ Bit 5 - (1) indicates that equipment configuration is incorrect
+ power-on check requires that atleast one floppy be installed
+ Bit 4 - (1) indicates error in memory size
+ Bit 3 - (1) indicates that controller or disk drive failed initialization
+ Bit 2 - (1) indicates that time is invalid
+ Bit 1 - (1) indicates installed adaptors do not match configuration
+ Bit 0 - (1) indicates a time-out while reading adaptor ID
+ 0x0E (AMSTRAD) 6 BYTEs time and date machine last used
+ 0x0F Reset Code (IBM PS/2 "Shutdown Status Byte")
+ 0x00-0x03 perform power-on reset
+ 0x04 INT 19h reboot
+ 0x05 flush keyboard and jump via 0040:0067
+ 0x06-0x07 reserved
+ 0x08 used by POST during protected-mode RAM test
+ 0x09 used for INT 15/87h (block move) support
+ 0x0A jump via 0040:0067
+ 0x0B-0xFF perform power-on reset
+
+*********************************************************************/
+
+#include "driver.h"
+#include "memconv.h"
+#include "machine/mc146818.h"
+
+
+
+#define LOG_MC146818 0
+#define MC146818_DATA_SIZE 0x80
+
+struct mc146818_chip
+{
+ MC146818_TYPE type;
+
+ UINT8 index;
+ UINT8 data[MC146818_DATA_SIZE];
+
+ UINT16 eindex;
+ UINT8 edata[0x2000];
+
+ attotime last_refresh;
+};
+
+static struct mc146818_chip *mc146818;
+
+
+
+#define HOURS_24 (mc146818->data[0xb]&2)
+#define BCD_MODE !(mc146818->data[0xb]&4) // book has other description!
+#define CENTURY mc146818->data[50]
+#define YEAR mc146818->data[9]
+#define MONTH mc146818->data[8]
+#define DAY mc146818->data[7]
+#define WEEK_DAY mc146818->data[6]
+
+
+
+static void mc146818_set_base_datetime(void);
+
+static TIMER_CALLBACK( mc146818_timer )
+{
+ int year, month;
+
+ if (BCD_MODE)
+ {
+ mc146818->data[0]=bcd_adjust(mc146818->data[0]+1);
+ if (mc146818->data[0]>=0x60)
+ {
+ mc146818->data[0]=0;
+ mc146818->data[2]=bcd_adjust(mc146818->data[2]+1);
+ if (mc146818->data[2]>=0x60)
+ {
+ mc146818->data[2]=0;
+ mc146818->data[4]=bcd_adjust(mc146818->data[4]+1);
+ // different handling of hours
+ if (mc146818->data[4]>=0x24)
+ {
+ mc146818->data[4]=0;
+ WEEK_DAY=bcd_adjust(WEEK_DAY+1)%7;
+ DAY=bcd_adjust(DAY+1);
+ month=bcd_2_dec(MONTH);
+ year=bcd_2_dec(YEAR);
+ if (mc146818->type!=MC146818_IGNORE_CENTURY) year+=bcd_2_dec(CENTURY)*100;
+ else year+=2000; // save for julian_days_in_month calculation
+ DAY=bcd_adjust(DAY+1);
+ if (DAY>gregorian_days_in_month(MONTH, year))
+ {
+ DAY=1;
+ MONTH=bcd_adjust(MONTH+1);
+ if (MONTH>0x12)
+ {
+ MONTH=1;
+ YEAR=year=bcd_adjust(YEAR+1);
+ if (mc146818->type!=MC146818_IGNORE_CENTURY)
+ {
+ if (year>=0x100)
+ {
+ CENTURY=bcd_adjust(CENTURY+1);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ mc146818->data[0]=mc146818->data[0]+1;
+ if (mc146818->data[0]>=60)
+ {
+ mc146818->data[0]=0;
+ mc146818->data[2]=mc146818->data[2]+1;
+ if (mc146818->data[2]>=60) {
+ mc146818->data[2]=0;
+ mc146818->data[4]=mc146818->data[4]+1;
+ // different handling of hours //?
+ if (mc146818->data[4]>=24) {
+ mc146818->data[4]=0;
+ WEEK_DAY=(WEEK_DAY+1)%7;
+ year=YEAR;
+ if (mc146818->type!=MC146818_IGNORE_CENTURY) year+=CENTURY*100;
+ else year+=2000; // save for julian_days_in_month calculation
+ if (++DAY>gregorian_days_in_month(MONTH, year)) {
+ DAY=1;
+ if (++MONTH>12) {
+ MONTH=1;
+ YEAR++;
+ if (mc146818->type!=MC146818_IGNORE_CENTURY) {
+ if (YEAR>=100) { CENTURY++;YEAR=0; }
+ } else {
+ YEAR%=100;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ mc146818->last_refresh = timer_get_time();
+}
+
+
+
+void mc146818_init(MC146818_TYPE type)
+{
+ mc146818 = auto_malloc(sizeof(*mc146818));
+ memset(mc146818, 0, sizeof(*mc146818));
+ mc146818->type = type;
+ mc146818->last_refresh = timer_get_time();
+ timer_pulse(ATTOTIME_IN_HZ(1), 0, mc146818_timer);
+ mc146818_set_base_datetime();
+}
+
+
+
+void mc146818_load(void)
+{
+ mame_file *file;
+
+ file = nvram_fopen(Machine, OPEN_FLAG_READ);
+ if (file)
+ {
+ mc146818_load_stream(file);
+ mame_fclose(file);
+ }
+}
+
+
+
+void mc146818_load_stream(mame_file *file)
+{
+ mame_fread(file, mc146818->data, sizeof(mc146818->data));
+}
+
+
+
+static int dec_2_local(int a)
+{
+ return BCD_MODE ? dec_2_bcd(a) : a;
+}
+
+
+
+static void mc146818_set_base_datetime(void)
+{
+ mame_system_time systime;
+
+ mame_get_base_datetime(Machine, &systime);
+
+ if (HOURS_24 || (systime.local_time.hour < 12))
+ mc146818->data[4] = dec_2_local(systime.local_time.hour);
+ else
+ mc146818->data[4] = dec_2_local(systime.local_time.hour - 12) | 0x80;
+
+ if (mc146818->type != MC146818_IGNORE_CENTURY)
+ CENTURY = dec_2_local(systime.local_time.year /100);
+
+ mc146818->data[0] = dec_2_local(systime.local_time.second);
+ mc146818->data[2] = dec_2_local(systime.local_time.minute);
+ DAY = dec_2_local(systime.local_time.day);
+ MONTH = dec_2_local(systime.local_time.month + 1);
+ YEAR = dec_2_local(systime.local_time.year % 100);
+
+ WEEK_DAY = systime.local_time.weekday;
+ if (systime.local_time.is_dst)
+ mc146818->data[0xb] |= 1;
+ else
+ mc146818->data[0xb] &= ~1;
+}
+
+
+
+void mc146818_save(void)
+{
+ mame_file *file;
+
+ file = nvram_fopen(Machine, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ if (file)
+ {
+ mame_fwrite(file, mc146818->data, sizeof(mc146818->data));
+ mame_fclose(file);
+ }
+}
+
+
+
+void mc146818_save_stream(mame_file *file)
+{
+ mame_fwrite(file, mc146818->data, sizeof(mc146818->data));
+}
+
+
+
+NVRAM_HANDLER( mc146818 )
+{
+ if (file == NULL)
+ {
+ mc146818_set_base_datetime();
+ // init only
+ }
+ else if (read_or_write)
+ {
+ mc146818_save_stream(file);
+ }
+ else
+ {
+ mc146818_load_stream(file);
+ }
+}
+
+
+
+READ8_HANDLER(mc146818_port_r)
+{
+ UINT8 data = 0;
+ switch (offset) {
+ case 0:
+ data = mc146818->index;
+ break;
+
+ case 1:
+ switch (mc146818->index % MC146818_DATA_SIZE) {
+ case 0xa:
+ data = mc146818->data[mc146818->index % MC146818_DATA_SIZE];
+ if (attotime_compare(attotime_sub(timer_get_time(), mc146818->last_refresh), ATTOTIME_IN_HZ(32768)) < 0)
+ data |= 0x80;
+#if 0
+ /* for pc1512 bios realtime clock test */
+ mc146818->data[mc146818->index % MC146818_DATA_SIZE] ^= 0x80; /* 0x80 update in progress */
+#endif
+ break;
+
+ case 0xd:
+ /* battery ok */
+ data = mc146818->data[mc146818->index % MC146818_DATA_SIZE] | 0x80;
+ break;
+
+ default:
+ data = mc146818->data[mc146818->index % MC146818_DATA_SIZE];
+ break;
+ }
+ break;
+ }
+
+ if (LOG_MC146818)
+ logerror("mc146818_port_r(): index=0x%02x data=0x%02x\n", mc146818->index, data);
+ return data;
+}
+
+
+
+WRITE8_HANDLER(mc146818_port_w)
+{
+ if (LOG_MC146818)
+ logerror("mc146818_port_w(): index=0x%02x data=0x%02x\n", mc146818->index, data);
+
+ switch (offset) {
+ case 0:
+ mc146818->index = data;
+ break;
+
+ case 1:
+ mc146818->data[mc146818->index % MC146818_DATA_SIZE] = data;
+ break;
+ }
+}
+
+
+
+READ16_HANDLER(mc146818_port16le_r)
+{
+ return read16le_with_read8_handler(mc146818_port_r, offset, mem_mask);
+}
+
+WRITE16_HANDLER(mc146818_port16le_w)
+{
+ write16le_with_write8_handler(mc146818_port_w, offset, data, mem_mask);
+}
+
+READ32_HANDLER(mc146818_port32le_r)
+{
+ return read32le_with_read8_handler(mc146818_port_r, offset, mem_mask);
+}
+
+WRITE32_HANDLER(mc146818_port32le_w)
+{
+ write32le_with_write8_handler(mc146818_port_w, offset, data, mem_mask);
+}
+
+READ64_HANDLER(mc146818_port64be_r)
+{
+ return read64be_with_read8_handler(mc146818_port_r, offset, mem_mask);
+}
+
+WRITE64_HANDLER(mc146818_port64be_w)
+{
+ write64be_with_write8_handler(mc146818_port_w, offset, data, mem_mask);
+}