diff options
Diffstat (limited to 'src/emu/machine/timekpr.c')
-rw-r--r-- | src/emu/machine/timekpr.c | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/src/emu/machine/timekpr.c b/src/emu/machine/timekpr.c new file mode 100644 index 00000000000..125ac7308a6 --- /dev/null +++ b/src/emu/machine/timekpr.c @@ -0,0 +1,483 @@ +/* + * STmicroelectronics TIMEKEEPER SRAM + * + * Supports: MK48T08, M48T02 & M48T58 + * + */ + +#include "driver.h" +#include "machine/timekpr.h" + +struct timekeeper_chip +{ + UINT8 control; + UINT8 seconds; + UINT8 minutes; + UINT8 hours; + UINT8 day; + UINT8 date; + UINT8 month; + UINT8 year; + UINT8 century; + UINT8 *data; + int type; + int size; + int offset_control; + int offset_seconds; + int offset_minutes; + int offset_hours; + int offset_day; + int offset_date; + int offset_month; + int offset_year; + int offset_century; + int offset_flags; +}; + +struct timekeeper_chip timekeeper[ MAX_TIMEKEEPER_CHIPS ]; + +#define MASK_SECONDS ( 0x7f ) +#define MASK_MINUTES ( 0x7f ) +#define MASK_HOURS ( 0x3f ) +#define MASK_DAY ( 0x07 ) +#define MASK_DATE ( 0x3f ) +#define MASK_MONTH ( 0x1f ) +#define MASK_YEAR ( 0xff ) +#define MASK_CENTURY ( 0xff ) + +#define CONTROL_W ( 0x80 ) +#define CONTROL_R ( 0x40 ) +#define CONTROL_S ( 0x20 ) /* not emulated */ +#define CONTROL_CALIBRATION ( 0x1f ) /* not emulated */ + +#define SECONDS_ST ( 0x80 ) + +#define DAY_FT ( 0x40 ) /* not emulated */ +#define DAY_CEB ( 0x20 ) /* M48T58 */ +#define DAY_CB ( 0x10 ) /* M48T58 */ + +#define DATE_BLE ( 0x80 ) /* M48T58: not emulated */ +#define DATE_BL ( 0x40 ) /* M48T58: not emulated */ + +#define FLAGS_BL ( 0x10 ) /* MK48T08: not emulated */ + +INLINE UINT8 make_bcd(UINT8 data) +{ + return ( ( ( data / 10 ) % 10 ) << 4 ) + ( data % 10 ); +} + +INLINE UINT8 from_bcd( UINT8 data ) +{ + return ( ( ( data >> 4 ) & 15 ) * 10 ) + ( data & 15 ); +} + +static int inc_bcd( UINT8 *data, int mask, int min, int max ) +{ + int bcd; + int carry; + + bcd = ( *( data ) + 1 ) & mask; + carry = 0; + + if( ( bcd & 0x0f ) > 9 ) + { + bcd &= 0xf0; + bcd += 0x10; + if( bcd > max ) + { + bcd = min; + carry = 1; + } + } + + *( data ) = ( *( data ) & ~mask ) | ( bcd & mask ); + return carry; +} + +static void counter_to_ram( UINT8 *data, int offset, int counter ) +{ + if( offset >= 0 ) + { + data[ offset ] = counter; + } +} + +static void counters_to_ram( struct timekeeper_chip *c ) +{ + counter_to_ram( c->data, c->offset_control, c->control ); + counter_to_ram( c->data, c->offset_seconds, c->seconds ); + counter_to_ram( c->data, c->offset_minutes, c->minutes ); + counter_to_ram( c->data, c->offset_hours, c->hours ); + counter_to_ram( c->data, c->offset_day, c->day ); + counter_to_ram( c->data, c->offset_date, c->date ); + counter_to_ram( c->data, c->offset_month, c->month ); + counter_to_ram( c->data, c->offset_year, c->year ); + counter_to_ram( c->data, c->offset_century, c->century ); +} + +static int counter_from_ram( UINT8 *data, int offset ) +{ + if( offset >= 0 ) + { + return data[ offset ]; + } + return 0; +} + +static void counters_from_ram( int chip ) +{ + struct timekeeper_chip *c = &timekeeper[ chip ]; + + c->control = counter_from_ram( c->data, c->offset_control ); + c->seconds = counter_from_ram( c->data, c->offset_seconds ); + c->minutes = counter_from_ram( c->data, c->offset_minutes ); + c->hours = counter_from_ram( c->data, c->offset_hours ); + c->day = counter_from_ram( c->data, c->offset_day ); + c->date = counter_from_ram( c->data, c->offset_date ); + c->month = counter_from_ram( c->data, c->offset_month ); + c->year = counter_from_ram( c->data, c->offset_year ); + c->century = counter_from_ram( c->data, c->offset_century ); +} + +static TIMER_CALLBACK_PTR( timekeeper_tick ) +{ + struct timekeeper_chip *c = param; + + int carry; + + if( ( c->seconds & SECONDS_ST ) != 0 || + ( c->control & CONTROL_W ) != 0 ) + { + return; + } + + carry = inc_bcd( &c->seconds, MASK_SECONDS, 0x00, 0x59 ); + if( carry ) + { + carry = inc_bcd( &c->minutes, MASK_MINUTES, 0x00, 0x59 ); + } + if( carry ) + { + carry = inc_bcd( &c->hours, MASK_HOURS, 0x00, 0x23 ); + } + + if( carry ) + { + UINT8 month; + UINT8 year; + UINT8 maxdays; + static const UINT8 daysinmonth[] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 }; + + inc_bcd( &c->day, MASK_DAY, 0x01, 0x07 ); + + month = from_bcd( c->month ); + year = from_bcd( c->year ); + + if( month == 2 && ( year % 4 ) == 0 ) + { + maxdays = 0x29; + } + else if( month >= 1 && month <= 12 ) + { + maxdays = daysinmonth[ month - 1 ]; + } + else + { + maxdays = 0x31; + } + + carry = inc_bcd( &c->date, MASK_DATE, 0x01, maxdays ); + } + if( carry ) + { + carry = inc_bcd( &c->month, MASK_MONTH, 0x01, 0x12 ); + } + if( carry ) + { + carry = inc_bcd( &c->year, MASK_YEAR, 0x00, 0x99 ); + } + if( carry ) + { + carry = inc_bcd( &c->century, MASK_CENTURY, 0x00, 0x99 ); + if( c->type == TIMEKEEPER_M48T58 && ( c->day & DAY_CEB ) != 0 ) + { + c->day ^= DAY_CB; + } + } + + if( ( c->control & CONTROL_R ) == 0 ) + { + counters_to_ram( c ); + } +} + +void timekeeper_init( int chip, int type, UINT8 *data ) +{ + emu_timer *timer; + attotime duration; + mame_system_time systime; + struct timekeeper_chip *c; + + if( chip >= MAX_TIMEKEEPER_CHIPS ) + { + logerror( "timekeeper_init( %d ) invalid chip\n", chip ); + return; + } + c = &timekeeper[ chip ]; + + c->type = type; + + switch( c->type ) + { + case TIMEKEEPER_M48T02: + c->offset_control = 0x7f8; + c->offset_seconds = 0x7f9; + c->offset_minutes = 0x7fa; + c->offset_hours = 0x7fb; + c->offset_day = 0x7fc; + c->offset_date = 0x7fd; + c->offset_month = 0x7fe; + c->offset_year = 0x7ff; + c->offset_century = -1; + c->offset_flags = -1; + c->size = 0x800; + break; + case TIMEKEEPER_M48T58: + c->offset_control = 0x1ff8; + c->offset_seconds = 0x1ff9; + c->offset_minutes = 0x1ffa; + c->offset_hours = 0x1ffb; + c->offset_day = 0x1ffc; + c->offset_date = 0x1ffd; + c->offset_month = 0x1ffe; + c->offset_year = 0x1fff; + c->offset_century = -1; + c->offset_flags = -1; + c->size = 0x2000; + break; + case TIMEKEEPER_MK48T08: + c->offset_control = 0x1ff8; + c->offset_seconds = 0x1ff9; + c->offset_minutes = 0x1ffa; + c->offset_hours = 0x1ffb; + c->offset_day = 0x1ffc; + c->offset_date = 0x1ffd; + c->offset_month = 0x1ffe; + c->offset_year = 0x1fff; + c->offset_century = 0x1ff1; + c->offset_flags = 0x1ff0; + c->size = 0x2000; + break; + } + + if( data == NULL ) + { + data = auto_malloc( c->size ); + memset( data, 0xff, c->size ); + } + c->data = data; + + mame_get_base_datetime(Machine, &systime); + + c->control = 0; + c->seconds = make_bcd( systime.local_time.second ); + c->minutes = make_bcd( systime.local_time.minute ); + c->hours = make_bcd( systime.local_time.hour ); + c->day = make_bcd( systime.local_time.weekday + 1 ); + c->date = make_bcd( systime.local_time.mday ); + c->month = make_bcd( systime.local_time.month + 1 ); + c->year = make_bcd( systime.local_time.year % 100 ); + c->century = make_bcd( systime.local_time.year / 100 ); + + state_save_register_item( "timekeeper", chip, c->control ); + state_save_register_item( "timekeeper", chip, c->seconds ); + state_save_register_item( "timekeeper", chip, c->minutes ); + state_save_register_item( "timekeeper", chip, c->hours ); + state_save_register_item( "timekeeper", chip, c->day ); + state_save_register_item( "timekeeper", chip, c->date ); + state_save_register_item( "timekeeper", chip, c->month ); + state_save_register_item( "timekeeper", chip, c->year ); + state_save_register_item( "timekeeper", chip, c->century ); + state_save_register_item_pointer( "timekeeper", chip, c->data, c->size ); + + timer = timer_alloc_ptr( timekeeper_tick, c ); + duration = ATTOTIME_IN_SEC(1); + timer_adjust_ptr( timer, duration, duration ); +} + +static void timekeeper_nvram( int chip, mame_file *file, int read_or_write ) +{ + struct timekeeper_chip *c; + if( chip >= MAX_TIMEKEEPER_CHIPS ) + { + logerror( "timekeeper_nvram( %d ) invalid chip\n", chip ); + return; + } + c = &timekeeper[ chip ]; + + if( read_or_write ) + { + mame_fwrite( file, c->data, c->size ); + } + else + { + if( file ) + { + mame_fread( file, c->data, c->size ); + } + counters_to_ram( c ); + } +} + +NVRAM_HANDLER( timekeeper_0 ) { timekeeper_nvram( 0, file, read_or_write ); } + +static UINT8 timekeeper_read( UINT32 chip, offs_t offset ) +{ + UINT8 data; + struct timekeeper_chip *c; + if( chip >= MAX_TIMEKEEPER_CHIPS ) + { + logerror( "%08x: timekeeper_read( %d, %04x ) chip out of range\n", activecpu_get_pc(), chip, offset ); + return 0; + } + c = &timekeeper[ chip ]; + + data = c->data[ offset ]; +// logerror( "%08x: timekeeper_read( %d, %04x ) %02x\n", activecpu_get_pc(), chip, offset, data ); + return data; +} + +static void timekeeper_write( UINT32 chip, offs_t offset, UINT8 data ) +{ + struct timekeeper_chip *c; + if( chip >= MAX_TIMEKEEPER_CHIPS ) + { + logerror( "%08x: timekeeper_write( %d, %04x, %02x ) chip out of range\n", activecpu_get_pc(), chip, offset, data ); + return; + } + c = &timekeeper[ chip ]; + + if( offset == c->offset_control ) + { + if( ( c->control & CONTROL_W ) != 0 && + ( data & CONTROL_W ) == 0 ) + { + counters_from_ram( chip ); + } + c->control = data; + } + else if( c->type == TIMEKEEPER_M48T58 && offset == c->offset_day ) + { + c->day = ( c->day & ~DAY_CEB ) | ( data & DAY_CEB ); + } + else if( c->type == TIMEKEEPER_M48T58 && offset == c->offset_date ) + { + data &= ~DATE_BL; + } + else if( c->type == TIMEKEEPER_MK48T08 && offset == c->offset_flags ) + { + data &= ~FLAGS_BL; + } + +// logerror( "%08x: timekeeper_write( %d, %04x, %02x )\n", activecpu_get_pc(), chip, offset, data ); + c->data[ offset ] = data; +} + +/* 16bit memory handlers */ + +static UINT16 timekeeper_msb16_read( UINT32 chip, offs_t offset, UINT16 mem_mask ) +{ + UINT16 data = 0; + if( ACCESSING_MSB16 ) + { + data |= timekeeper_read( chip, offset ) << 8; + } + return data; +} + +static void timekeeper_msb16_write( UINT32 chip, offs_t offset, UINT16 data, UINT16 mem_mask ) +{ + if( ACCESSING_MSB16 ) + { + timekeeper_write( chip, offset, data >> 8 ); + } +} + +READ16_HANDLER( timekeeper_0_msb16_r ) { return timekeeper_msb16_read( 0, offset, mem_mask ); } +WRITE16_HANDLER( timekeeper_0_msb16_w ) { timekeeper_msb16_write( 0, offset, data, mem_mask ); } + +/* 32bit memory handlers */ + +static UINT32 timekeeper_32be_read( UINT32 chip, offs_t offset, UINT32 mem_mask ) +{ + UINT32 data = 0; + if( ACCESSING_MSB32 ) + { + data |= timekeeper_read( chip, ( offset * 4 ) + 0 ) << 24; + } + if( ( mem_mask & 0x00ff0000 ) == 0 ) + { + data |= timekeeper_read( chip, ( offset * 4 ) + 1 ) << 16; + } + if( ( mem_mask & 0x0000ff00 ) == 0 ) + { + data |= timekeeper_read( chip, ( offset * 4 ) + 2 ) << 8; + } + if( ACCESSING_LSB32 ) + { + data |= timekeeper_read( chip, ( offset * 4 ) + 3 ) << 0; + } + return data; +} + +static void timekeeper_32be_write( UINT32 chip, offs_t offset, UINT32 data, UINT32 mem_mask ) +{ + if( ACCESSING_MSB32 ) + { + timekeeper_write( chip, ( offset * 4 ) + 0, data >> 24 ); + } + if( ( mem_mask & 0x00ff0000 ) == 0 ) + { + timekeeper_write( chip, ( offset * 4 ) + 1, data >> 16 ); + } + if( ( mem_mask & 0x0000ff00 ) == 0 ) + { + timekeeper_write( chip, ( offset * 4 ) + 2, data >> 8 ); + } + if( ACCESSING_LSB32 ) + { + timekeeper_write( chip, ( offset * 4 ) + 3, data >> 0 ); + } +} + +READ32_HANDLER( timekeeper_0_32be_r ) { return timekeeper_32be_read( 0, offset, mem_mask ); } +WRITE32_HANDLER( timekeeper_0_32be_w ) { timekeeper_32be_write( 0, offset, data, mem_mask ); } + +static UINT32 timekeeper_32le_lsb16_read( UINT32 chip, offs_t offset, UINT32 mem_mask ) +{ + UINT32 data = 0; + if( ACCESSING_LSB32 ) + { + data |= timekeeper_read( chip, ( offset * 2 ) + 0 ) << 0; + } + if( ( mem_mask & 0x00ff0000 ) == 0 ) + { + data |= timekeeper_read( chip, ( offset * 2 ) + 1 ) << 16; + } + return data; +} + +static void timekeeper_32le_lsb16_write( UINT32 chip, offs_t offset, UINT32 data, UINT32 mem_mask ) +{ + if( ACCESSING_LSB32 ) + { + timekeeper_write( chip, ( offset * 2 ) + 0, data >> 0 ); + } + if( ( mem_mask & 0x00ff0000 ) == 0 ) + { + timekeeper_write( chip, ( offset * 2 ) + 1, data >> 16 ); + } +} + +READ32_HANDLER( timekeeper_0_32le_lsb16_r ) { return timekeeper_32le_lsb16_read( 0, offset, mem_mask ); } +WRITE32_HANDLER( timekeeper_0_32le_lsb16_w ) { timekeeper_32le_lsb16_write( 0, offset, data, mem_mask ); } |