diff options
Diffstat (limited to 'src/emu/machine/mc146818.c')
-rw-r--r-- | src/emu/machine/mc146818.c | 392 |
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); +} |