diff options
Diffstat (limited to 'trunk/src/emu/machine/pd4990a.c')
-rw-r--r-- | trunk/src/emu/machine/pd4990a.c | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/pd4990a.c b/trunk/src/emu/machine/pd4990a.c new file mode 100644 index 00000000000..86c928cea15 --- /dev/null +++ b/trunk/src/emu/machine/pd4990a.c @@ -0,0 +1,537 @@ +/*************************************************************************** + + NEC uPD4990A + + Serial I/O Calendar & Clock IC used in the + NEO GEO and probably a couple of other machines. + + + Completed by ElSemi. + + I haven't found any schematics for this device + so I had to make some assumptions about how it works. + + The three input bits seem to be used for a serial protocol + + bit 0 - data + bit 1 - clock + bit 2 - command end (?) + + the commands I've found so far are: + + 0x0 - ?? sent after 2 + 0x1 - Reset the (probable) shift register used for output + 0x2 - Store the contents of the shift reg to the current date + 0x3 - Load Shift register with current date + + 0x7 - Switch test bit every frame + 0x8 - Switch test bit every half-second + + + 2009-06 Converted to be a device + +***************************************************************************/ + +#include "emu.h" +#include "machine/pd4990a.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define DATA_BIT 0x01 +#define CLOCK_BIT 0x02 +#define END_BIT 0x04 + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _upd4990a_state upd4990a_state; +struct _upd4990a_state +{ + int seconds; /* seconds BCD */ + int minutes; /* minutes BCD */ + int hours; /* hours BCD */ + int days; /* days BCD */ + int month; /* month Hexadecimal form */ + int year; /* year BCD */ + int weekday; /* weekday BCD */ + + UINT32 shiftlo; + UINT32 shifthi; + + int retraces; /* Assumes 60 retraces a second */ + int testwaits; + int maxwaits; /* Switch test every frame*/ + int testbit; /* Pulses a bit in order to simulate test output */ + + int outputbit; + int bitno; + INT8 reading; + INT8 writing; + + int clock_line; + int command_line; //?? +}; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE upd4990a_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert((device->type() == UPD4990A)); + return (upd4990a_state *)downcast<legacy_device_base *>(device)->token(); +} + +INLINE UINT8 convert_to_bcd(int val) +{ + return ((val / 10) << 4) | (val % 10); +} + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + upd4990a_increment_month +-------------------------------------------------*/ + +static void upd4990a_increment_month( device_t *device ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + + upd4990a->month++; + if (upd4990a->month == 13) + { + upd4990a->month = 1; + upd4990a->year++; + if ((upd4990a->year & 0x0f) >= 10) + { + upd4990a->year &= 0xf0; + upd4990a->year += 0x10; + } + if (upd4990a->year == 0xA0) + upd4990a->year = 0; + } +} + +/*------------------------------------------------- + upd4990a_increment_day +-------------------------------------------------*/ + +static void upd4990a_increment_day( device_t *device ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + int real_year; + + upd4990a->days++; + if ((upd4990a->days & 0x0f) >= 10) + { + upd4990a->days &= 0xf0; + upd4990a->days += 0x10; + } + + upd4990a->weekday++; + if (upd4990a->weekday == 7) + upd4990a->weekday = 0; + + switch (upd4990a->month) + { + case 1: case 3: case 5: case 7: case 8: case 10: case 12: + if (upd4990a->days == 0x32) + { + upd4990a->days = 1; + upd4990a_increment_month(device); + } + break; + case 2: + real_year = (upd4990a->year >> 4) * 10 + (upd4990a->year & 0xf); + if ((real_year % 4) && (!(real_year % 100) || (real_year % 400))) + { + if (upd4990a->days == 0x29) + { + upd4990a->days = 1; + upd4990a_increment_month(device); + } + } + else + { + if (upd4990a->days == 0x30) + { + upd4990a->days = 1; + upd4990a_increment_month(device); + } + } + break; + case 4: case 6: case 9: case 11: + if (upd4990a->days == 0x31) + { + upd4990a->days = 1; + upd4990a_increment_month(device); + } + break; + } +} + +/*------------------------------------------------- + upd4990a_addretrace +-------------------------------------------------*/ + +void upd4990a_addretrace( device_t *device ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + + ++upd4990a->testwaits; + if(upd4990a->testwaits >= upd4990a->maxwaits) + { + upd4990a->testbit ^= 1; + upd4990a->testwaits = 0; + } + + upd4990a->retraces++; + if (upd4990a->retraces < 60) + return; + + upd4990a->retraces = 0; + upd4990a->seconds++; + if ((upd4990a->seconds & 0x0f) < 10) + return; + + upd4990a->seconds &= 0xf0; + upd4990a->seconds += 0x10; + if (upd4990a->seconds < 0x60) + return; + + upd4990a->seconds = 0; + upd4990a->minutes++; + if ((upd4990a->minutes & 0x0f) < 10) + return; + + upd4990a->minutes &= 0xf0; + upd4990a->minutes += 0x10; + if (upd4990a->minutes < 0x60) + return; + + upd4990a->minutes = 0; + upd4990a->hours++; + if ((upd4990a->hours & 0x0f) < 10) + return; + + upd4990a->hours &= 0xf0; + upd4990a->hours += 0x10; + if (upd4990a->hours < 0x24) + return; + + upd4990a->hours = 0; + upd4990a_increment_day(device); +} + +/*------------------------------------------------- + upd4990a_testbit_r +-------------------------------------------------*/ + +READ8_DEVICE_HANDLER( upd4990a_testbit_r ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + return upd4990a->testbit; +} + +/*------------------------------------------------- + upd4990a_databit_r +-------------------------------------------------*/ + +READ8_DEVICE_HANDLER( upd4990a_databit_r ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + return upd4990a->outputbit; +} + +/*------------------------------------------------- + upd4990a_readbit +-------------------------------------------------*/ + +static void upd4990a_readbit( device_t *device ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + + switch (upd4990a->bitno) + { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + upd4990a->outputbit = (upd4990a->seconds >> upd4990a->bitno) & 0x01; + break; + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + upd4990a->outputbit = (upd4990a->minutes >> (upd4990a->bitno - 0x08)) & 0x01; + break; + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + upd4990a->outputbit = (upd4990a->hours >> (upd4990a->bitno - 0x10)) & 0x01; + break; + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + upd4990a->outputbit = (upd4990a->days >> (upd4990a->bitno - 0x18)) & 0x01; + break; + case 0x20: case 0x21: case 0x22: case 0x23: + upd4990a->outputbit = (upd4990a->weekday >> (upd4990a->bitno - 0x20)) & 0x01; + break; + case 0x24: case 0x25: case 0x26: case 0x27: + upd4990a->outputbit = (upd4990a->month >> (upd4990a->bitno - 0x24)) & 0x01; + break; + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + upd4990a->outputbit = (upd4990a->year >> (upd4990a->bitno - 0x28)) & 0x01; + break; + case 0x30: case 0x31: case 0x32: case 0x33: + //unknown + break; + } +} + +/*------------------------------------------------- + upd4990a_resetbitstream +-------------------------------------------------*/ + +static void upd4990a_resetbitstream( device_t *device ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + + upd4990a->shiftlo = 0; + upd4990a->shifthi = 0; + upd4990a->bitno = 0; +} + +/*------------------------------------------------- + upd4990a_writebit +-------------------------------------------------*/ + +static void upd4990a_writebit( device_t *device , UINT8 bit ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + if (upd4990a->bitno <= 31) //low part + upd4990a->shiftlo |= bit << upd4990a->bitno; + else //high part + upd4990a->shifthi |= bit << (upd4990a->bitno - 32); +} + +/*------------------------------------------------- + upd4990a_nextbit +-------------------------------------------------*/ + +static void upd4990a_nextbit( device_t *device ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + ++upd4990a->bitno; + + if (upd4990a->reading) + upd4990a_readbit(device); + + if (upd4990a->reading && upd4990a->bitno == 0x34) + { + upd4990a->reading = 0; + upd4990a_resetbitstream(device); + } + +} + +/*------------------------------------------------- + upd4990a_getcommand +-------------------------------------------------*/ + +static UINT8 upd4990a_getcommand( device_t *device ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + //Warning: problems if the 4 bits are in different + //parts, It's very strange that this case could happen. + if(upd4990a->bitno <= 31) + return upd4990a->shiftlo >> (upd4990a->bitno - 4); + else + return upd4990a->shifthi >> (upd4990a->bitno - 32 - 4); +} + +/*------------------------------------------------- + upd4990a_update_date +-------------------------------------------------*/ + +static void upd4990a_update_date( device_t *device ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + + upd4990a->seconds = (upd4990a->shiftlo >> 0 ) & 0xff; + upd4990a->minutes = (upd4990a->shiftlo >> 8 ) & 0xff; + upd4990a->hours = (upd4990a->shiftlo >> 16) & 0xff; + upd4990a->days = (upd4990a->shiftlo >> 24) & 0xff; + upd4990a->weekday = (upd4990a->shifthi >> 0 ) & 0x0f; + upd4990a->month = (upd4990a->shifthi >> 4 ) & 0x0f; + upd4990a->year = (upd4990a->shifthi >> 8 ) & 0xff; +} + +/*------------------------------------------------- + upd4990a_process_command +-------------------------------------------------*/ + +static void upd4990a_process_command( device_t *device ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + + switch(upd4990a_getcommand(device)) + { + case 0x1: //load output register + upd4990a->bitno = 0; + if (upd4990a->reading) + upd4990a_readbit(device); //prepare first bit + upd4990a->shiftlo = 0; + upd4990a->shifthi = 0; + break; + case 0x2: + upd4990a->writing = 0; //store register to current date + upd4990a_update_date(device); + break; + case 0x3: //start reading + upd4990a->reading = 1; + break; + case 0x7: //switch testbit every frame + upd4990a->maxwaits = 1; + break; + case 0x8: //switch testbit every half-second + upd4990a->maxwaits = 30; + break; + } + upd4990a_resetbitstream(device); +} + +/*------------------------------------------------- + upd4990a_serial_control +-------------------------------------------------*/ + +static void upd4990a_serial_control( device_t *device, UINT8 data ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + + //Check for command end + if(upd4990a->command_line && !(data & END_BIT)) //end of command + { + upd4990a_process_command(device); + } + upd4990a->command_line = data & END_BIT; + + if(upd4990a->clock_line && !(data & CLOCK_BIT)) //clock lower edge + { + upd4990a_writebit(device, data & DATA_BIT); + upd4990a_nextbit(device); + } + upd4990a->clock_line = data & CLOCK_BIT; +} + +/*------------------------------------------------- + upd4990a_control_16_w +-------------------------------------------------*/ + +WRITE16_DEVICE_HANDLER( upd4990a_control_16_w ) +{ + upd4990a_serial_control(device, data & 0x7); +} + + +/*------------------------------------------------- + DEVICE_START( upd4990a ) +-------------------------------------------------*/ + +static DEVICE_START( upd4990a ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + + system_time curtime, *systime = &curtime; + device->machine().current_datetime(curtime); + +#if 0 + upd4990a->seconds = 0x00; + upd4990a->minutes = 0x00; + upd4990a->hours = 0x00; + upd4990a->days = 0x09; + upd4990a->month = 9; + upd4990a->year = 0x73; + upd4990a->weekday = 1; +#endif + /* HACK: load time counter from system time */ + upd4990a->seconds = convert_to_bcd(systime->local_time.second); + upd4990a->minutes = convert_to_bcd(systime->local_time.minute); + upd4990a->hours = convert_to_bcd(systime->local_time.hour); + upd4990a->days = convert_to_bcd(systime->local_time.mday); + upd4990a->month = systime->local_time.month + 1; + upd4990a->year = ((((systime->local_time.year - 1900) % 100) / 10) << 4) | ((systime->local_time.year - 1900) % 10); + upd4990a->weekday = systime->local_time.weekday; + + /* register for state saving */ + device->save_item(NAME(upd4990a->seconds)); + device->save_item(NAME(upd4990a->minutes)); + device->save_item(NAME(upd4990a->hours)); + device->save_item(NAME(upd4990a->days)); + device->save_item(NAME(upd4990a->month)); + device->save_item(NAME(upd4990a->year)); + device->save_item(NAME(upd4990a->weekday)); + + device->save_item(NAME(upd4990a->shiftlo)); + device->save_item(NAME(upd4990a->shifthi)); + + device->save_item(NAME(upd4990a->retraces)); + device->save_item(NAME(upd4990a->testwaits)); + device->save_item(NAME(upd4990a->maxwaits)); + device->save_item(NAME(upd4990a->testbit)); + + device->save_item(NAME(upd4990a->outputbit)); + device->save_item(NAME(upd4990a->bitno)); + device->save_item(NAME(upd4990a->reading)); + device->save_item(NAME(upd4990a->writing)); + + device->save_item(NAME(upd4990a->clock_line)); + device->save_item(NAME(upd4990a->command_line)); +} + + +/*------------------------------------------------- + DEVICE_RESET( upd4990a ) +-------------------------------------------------*/ + +static DEVICE_RESET( upd4990a ) +{ + upd4990a_state *upd4990a = get_safe_token(device); + + upd4990a->shiftlo = 0; + upd4990a->shifthi = 0; + + upd4990a->retraces = 0; + upd4990a->testwaits = 0; + upd4990a->maxwaits = 1; + upd4990a->testbit = 0; + + upd4990a->outputbit = 0; + upd4990a->bitno = 0; + upd4990a->reading = 0; + upd4990a->writing = 0; + + upd4990a->clock_line = 0; + upd4990a->command_line = 0; +} + +/*------------------------------------------------- + device definition +-------------------------------------------------*/ + +static const char DEVTEMPLATE_SOURCE[] = __FILE__; + +#define DEVTEMPLATE_ID(p,s) p##upd4990a##s +#define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_RESET +#define DEVTEMPLATE_NAME "NEC uPD4990A" +#define DEVTEMPLATE_FAMILY "NEC uPD4990A Calendar & Clock" +#include "devtempl.h" + + +DEFINE_LEGACY_DEVICE(UPD4990A, upd4990a); |