summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/pd4990a.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/pd4990a.c')
-rw-r--r--src/emu/machine/pd4990a.c352
1 files changed, 352 insertions, 0 deletions
diff --git a/src/emu/machine/pd4990a.c b/src/emu/machine/pd4990a.c
new file mode 100644
index 00000000000..22010476975
--- /dev/null
+++ b/src/emu/machine/pd4990a.c
@@ -0,0 +1,352 @@
+/*
+ * Emulation for the NEC PD4990A.
+ *
+ * The PD4990A is a 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
+
+
+
+ */
+
+#include "driver.h"
+#include "machine/pd4990a.h"
+
+
+/* Set the data in the chip to Monday 09/09/73 00:00:00 */
+/* If you ever read this Leejanne, you know what I mean :-) */
+struct pd4990a_s pd4990a =
+{
+ 0x00, /* seconds BCD */
+ 0x00, /* minutes BCD */
+ 0x00, /* hours BCD */
+ 0x09, /* days BCD */
+ 9, /* month Hexadecimal form */
+ 0x73, /* year BCD */
+ 1 /* weekday BCD */
+};
+
+static UINT32 shiftlo,shifthi;
+
+static int retraces = 0; /* Assumes 60 retraces a second */
+static int testwaits= 0;
+static int maxwaits= 1; /*switch test every frame*/
+static int testbit = 0; /* Pulses a bit in order to simulate */
+ /* test output */
+static int outputbit=0;
+static int bitno=0;
+static INT8 reading=0;
+static INT8 writting=0;
+
+static int clock_line=0;
+static int command_line=0; //??
+
+#define DATA_BIT 0x1
+#define CLOCK_BIT 0x2
+#define END_BIT 0x4
+
+void pd4990a_addretrace()
+{
+ ++testwaits;
+ if(testwaits>=maxwaits)
+ {
+ testbit ^= 1;
+ testwaits=0;
+ }
+ retraces++;
+ if (retraces < 60)
+ return;
+ retraces = 0;
+ pd4990a.seconds++;
+ if ( (pd4990a.seconds & 0x0f) < 10 )
+ return;
+ pd4990a.seconds &= 0xf0;
+ pd4990a.seconds += 0x10;
+ if (pd4990a.seconds < 0x60)
+ return;
+ pd4990a.seconds = 0;
+ pd4990a.minutes++;
+ if ( (pd4990a.minutes & 0x0f) < 10 )
+ return;
+ pd4990a.minutes &= 0xf0;
+ pd4990a.minutes += 0x10;
+ if (pd4990a.minutes < 0x60)
+ return;
+ pd4990a.minutes = 0;
+ pd4990a.hours++;
+ if ( (pd4990a.hours & 0x0f) < 10 )
+ return;
+ pd4990a.hours &= 0xf0;
+ pd4990a.hours += 0x10;
+ if (pd4990a.hours < 0x24)
+ return;
+ pd4990a.hours = 0;
+ pd4990a_increment_day();
+}
+
+void pd4990a_increment_day(void)
+{
+ int real_year;
+
+ pd4990a.days++;
+ if ((pd4990a.days & 0x0f) >= 10)
+ {
+ pd4990a.days &= 0xf0;
+ pd4990a.days += 0x10;
+ }
+
+ pd4990a.weekday++;
+ if (pd4990a.weekday == 7)
+ pd4990a.weekday=0;
+
+ switch(pd4990a.month)
+ {
+ case 1: case 3: case 5: case 7: case 8: case 10: case 12:
+ if (pd4990a.days == 0x32)
+ {
+ pd4990a.days = 1;
+ pd4990a_increment_month();
+ }
+ break;
+ case 2:
+ real_year = (pd4990a.year>>4)*10 + (pd4990a.year&0xf);
+ if ((real_year % 4) && (!(real_year % 100) || (real_year % 400)))
+ {
+ if (pd4990a.days == 0x29)
+ {
+ pd4990a.days = 1;
+ pd4990a_increment_month();
+ }
+ }
+ else
+ {
+ if (pd4990a.days == 0x30)
+ {
+ pd4990a.days = 1;
+ pd4990a_increment_month();
+ }
+ }
+ break;
+ case 4: case 6: case 9: case 11:
+ if (pd4990a.days == 0x31)
+ {
+ pd4990a.days = 1;
+ pd4990a_increment_month();
+ }
+ break;
+ }
+}
+
+void pd4990a_increment_month(void)
+{
+ pd4990a.month++;
+ if (pd4990a.month == 13)
+ {
+ pd4990a.month = 1;
+ pd4990a.year++;
+ if ( (pd4990a.year & 0x0f) >= 10 )
+ {
+ pd4990a.year &= 0xf0;
+ pd4990a.year += 0x10;
+ }
+ if (pd4990a.year == 0xA0)
+ pd4990a.year = 0;
+ }
+}
+
+READ8_HANDLER( pd4990a_testbit_r )
+{
+ return (testbit);
+}
+
+READ8_HANDLER( pd4990a_databit_r )
+{
+ return (outputbit);
+}
+
+static void pd4990a_readbit(void)
+{
+ switch(bitno)
+ {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ outputbit=(pd4990a.seconds >> bitno) & 0x01;
+ break;
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ outputbit=(pd4990a.minutes >> (bitno-0x08)) & 0x01;
+ break;
+ case 0x10: case 0x11: case 0x12: case 0x13:
+ case 0x14: case 0x15: case 0x16: case 0x17:
+ outputbit=(pd4990a.hours >> (bitno-0x10)) & 0x01;
+ break;
+ case 0x18: case 0x19: case 0x1a: case 0x1b:
+ case 0x1c: case 0x1d: case 0x1e: case 0x1f:
+ outputbit=(pd4990a.days >> (bitno-0x18)) & 0x01;
+ break;
+ case 0x20: case 0x21: case 0x22: case 0x23:
+ outputbit=(pd4990a.weekday >> (bitno-0x20)) & 0x01;
+ break;
+ case 0x24: case 0x25: case 0x26: case 0x27:
+ outputbit=(pd4990a.month >> (bitno-0x24)) & 0x01;
+ break;
+ case 0x28: case 0x29: case 0x2a: case 0x2b:
+ case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+ outputbit=(pd4990a.year >> (bitno-0x28)) & 0x01;
+ break;
+ case 0x30: case 0x31: case 0x32: case 0x33:
+ //unknown
+ break;
+ }
+}
+
+
+
+
+static void pd4990a_resetbitstream(void)
+{
+ shiftlo=0;
+ shifthi=0;
+ bitno=0;
+}
+
+static void pd4990a_writebit(UINT8 bit)
+{
+ if(bitno<=31) //low part
+ shiftlo|=bit<<bitno;
+ else //high part
+ shifthi|=bit<<(bitno-32);
+}
+
+static void pd4990a_nextbit(void)
+{
+ ++bitno;
+ if(reading)
+ pd4990a_readbit();
+ if(reading && bitno==0x34)
+ {
+ reading=0;
+ pd4990a_resetbitstream();
+ }
+
+}
+
+static UINT8 pd4990a_getcommand(void)
+{
+ //Warning: problems if the 4 bits are in different
+ //parts, It's very strange that this case could happen.
+ if(bitno<=31)
+ return shiftlo>>(bitno-4);
+ else
+ return shifthi>>(bitno-32-4);
+}
+
+static void pd4990a_update_date(void)
+{
+ pd4990a.seconds=(shiftlo>>0 )&0xff;
+ pd4990a.minutes=(shiftlo>>8 )&0xff;
+ pd4990a.hours =(shiftlo>>16)&0xff;
+ pd4990a.days =(shiftlo>>24)&0xff;
+ pd4990a.weekday=(shifthi>>0 )&0x0f;
+ pd4990a.month =(shifthi>>4 )&0x0f;
+ pd4990a.year =(shifthi>>8 )&0xff;
+}
+
+static void pd4990a_process_command(void)
+{
+ switch(pd4990a_getcommand())
+ {
+ case 0x1: //load output register
+ bitno=0;
+ if(reading)
+ pd4990a_readbit(); //prepare first bit
+ shiftlo=0;
+ shifthi=0;
+ break;
+ case 0x2:
+ writting=0; //store register to current date
+ pd4990a_update_date();
+ break;
+ case 0x3: //start reading
+ reading=1;
+ break;
+ case 0x7: //switch testbit every frame
+ maxwaits=1;
+ break;
+ case 0x8: //switch testbit every half-second
+ maxwaits=30;
+ break;
+ }
+ pd4990a_resetbitstream();
+}
+
+
+static void pd4990a_serial_control(UINT8 data)
+{
+ //Check for command end
+ if(command_line && !(data&END_BIT)) //end of command
+ {
+ pd4990a_process_command();
+ }
+ command_line=data&END_BIT;
+
+ if(clock_line && !(data&CLOCK_BIT)) //clock lower edge
+ {
+ pd4990a_writebit(data&DATA_BIT);
+ pd4990a_nextbit();
+ }
+ clock_line=data&CLOCK_BIT;
+}
+
+WRITE16_HANDLER( pd4990a_control_16_w )
+{
+ pd4990a_serial_control(data&0x7);
+}
+
+void pd4990a_init(void)
+{
+ state_save_register_item("pd4990a", 0, pd4990a.seconds);
+ state_save_register_item("pd4990a", 0, pd4990a.minutes);
+ state_save_register_item("pd4990a", 0, pd4990a.hours);
+ state_save_register_item("pd4990a", 0, pd4990a.days);
+ state_save_register_item("pd4990a", 0, pd4990a.month);
+ state_save_register_item("pd4990a", 0, pd4990a.year);
+ state_save_register_item("pd4990a", 0, pd4990a.weekday);
+
+ state_save_register_item("pd4990a", 0, shiftlo);
+ state_save_register_item("pd4990a", 0, shifthi);
+
+ state_save_register_item("pd4990a", 0, retraces);
+ state_save_register_item("pd4990a", 0, testwaits);
+ state_save_register_item("pd4990a", 0, maxwaits);
+ state_save_register_item("pd4990a", 0, testbit);
+
+ state_save_register_item("pd4990a", 0, outputbit);
+ state_save_register_item("pd4990a", 0, bitno);
+ state_save_register_item("pd4990a", 0, reading);
+ state_save_register_item("pd4990a", 0, writting);
+
+ state_save_register_item("pd4990a", 0, clock_line);
+ state_save_register_item("pd4990a", 0, command_line);
+}