summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/i8279.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/i8279.c')
-rw-r--r--trunk/src/emu/machine/i8279.c508
1 files changed, 508 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/i8279.c b/trunk/src/emu/machine/i8279.c
new file mode 100644
index 00000000000..05e75d687de
--- /dev/null
+++ b/trunk/src/emu/machine/i8279.c
@@ -0,0 +1,508 @@
+/**********************************************************************
+
+ i8279
+
+2012-JAN-08 First draft [Robbbert]
+2012-JAN-12 Implemented
+
+Notes:
+- All keys MUST be ACTIVE_LOW
+
+
+ToDo:
+- Command 5 (Nibble masking and blanking)
+- Command 7 (Error Mode)
+- Interrupts
+- BD pin
+- Sensor ram stuff
+- save state
+
+
+What has been done:
+CMD 0:
+- Display Mode
+-- Left & Right with no increment are the same thing
+-- Right with increment is not emulated yet ***
+- Keyboard Mode
+-- No particular code has been added for 2-key/N-key rollover, no need
+-- Sensor mode is not complete yet ***
+-- Encoded and Decoded are done
+-- Strobe is done
+-- Sensor and FIFO may share the same internal RAM, not sure
+CMD 1:
+- Clock Divider
+-- Value is stored, but internally a fixed value is always used
+CMD 2:
+- Read FIFO/Sensor RAM
+-- FIFO works
+-- Sensor RAM works
+CMD 3:
+- Read Display RAM
+-- This works
+CMD 4:
+- Write Display RAM
+-- Right with increment does nothing, the rest is working ***
+CMD 5:
+- Blank Nibble
+-- Not done ***
+- Mask Nibble
+-- Implemented
+CMD 6:
+-- All implemented
+CMD 7:
+- Interrupt
+-- Not done
+- Error Mode
+-- No need to do.
+
+Interface:
+-- All done except BD pin ***
+
+Status word:
+- FIFO bits
+-- All done
+- Error bit
+-- Not done (no need)
+- Display unavailable
+-- Not done (no need)
+
+
+Items marked (***) can be added if a system appears
+that uses this feature.
+
+**********************************************************************/
+
+#include "i8279.h"
+
+#define LOG 0
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+// device type definition
+const device_type I8279 = &device_creator<i8279_device>;
+
+//-------------------------------------------------
+// i8279_device - constructor
+//-------------------------------------------------
+
+i8279_device::i8279_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, I8279, "Intel 8279", tag, owner, clock)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void i8279_device::device_config_complete()
+{
+ // inherit a copy of the static data
+ const i8279_interface *intf = reinterpret_cast<const i8279_interface *>(static_config());
+ if (intf != NULL)
+ {
+ *static_cast<i8279_interface *>(this) = *intf;
+ }
+
+ // or initialize to defaults if none provided
+ else
+ {
+ memset(&m_out_irq_cb, 0, sizeof(m_out_irq_cb));
+ memset(&m_out_sl_cb, 0, sizeof(m_out_sl_cb));
+ memset(&m_out_disp_cb, 0, sizeof(m_out_disp_cb));
+ memset(&m_out_bd_cb, 0, sizeof(m_out_bd_cb));
+ memset(&m_in_rl_cb, 0, sizeof(m_in_rl_cb));
+ memset(&m_in_shift_cb, 0, sizeof(m_in_shift_cb));
+ memset(&m_in_ctrl_cb, 0, sizeof(m_in_ctrl_cb));
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void i8279_device::device_start()
+{
+ /* resolve callbacks */
+ m_out_irq_func.resolve(m_out_irq_cb, *this);
+ m_out_sl_func.resolve(m_out_sl_cb, *this);
+ m_out_disp_func.resolve(m_out_disp_cb, *this);
+ m_out_bd_func.resolve(m_out_bd_cb, *this);
+ m_in_rl_func.resolve(m_in_rl_cb, *this);
+ m_in_shift_func.resolve(m_in_shift_cb, *this);
+ m_in_ctrl_func.resolve(m_in_ctrl_cb, *this);
+ m_clock = clock();
+ m_timer = machine().scheduler().timer_alloc(FUNC(timerproc_callback), (void *)this);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void i8279_device::device_reset()
+{
+ UINT8 i;
+
+ // startup values are unknown: setting to 0
+ for (i = 2; i < 8; i++) m_cmd[i] = 0;
+ for (i = 0; i < 8; i++) m_fifo[i] = 0;
+ for (i = 0; i < 8; i++) m_s_ram[i] = 0;
+ for (i = 0; i < 16; i++) m_d_ram[i] = 0;
+ m_status = 0;
+ m_autoinc = 1;
+ m_d_ram_ptr = 0;
+ m_s_ram_ptr = 0;
+ m_read_flag = 0;
+ m_scanner = 0;
+ m_ctrl_key = 1;
+ m_key_down = 0xffff;
+
+ // from here is confirmed
+ m_cmd[0] = 8;
+ m_cmd[1] = 31;
+ logerror("Initial clock = 3100kHz\n");
+ timer_adjust();
+}
+
+
+void i8279_device::timer_adjust()
+{
+// Real device runs at about 100kHz internally, clock divider is chosen so that
+// this is the case. We do not need such speed, 2000Hz is enough.
+// If this is too long, the sensor mode doesn't work correctly.
+
+#if 0
+ UINT8 divider = (m_cmd[1]) ? m_cmd[1] : 1;
+ UINT32 new_clock = clock() / divider;
+#else
+ UINT32 new_clock = 2000;
+#endif
+
+ if (m_clock != new_clock)
+ {
+ m_timer->adjust(attotime::from_hz(new_clock), 0, attotime::from_hz(new_clock));
+
+ m_clock = new_clock;
+ }
+}
+
+
+void i8279_device::clear_display()
+{
+ // clear all digits
+ UINT8 i,patterns[4] = { 0, 0, 0x20, 0xff };
+ UINT8 data = patterns[(m_cmd[6] & 12) >> 2];
+
+ // The CD high bit (also done by CA)
+ if (m_cmd[6] & 0x11)
+ for (i = 0; i < 16; i++)
+ m_d_ram[i] = data;
+
+ m_status &= 0x7f; // bit 7 not emulated, but do it anyway
+ m_d_ram_ptr = 0; // not in the datasheet, but needed
+
+ // The CF bit (also done by CA)
+ if (m_cmd[6] & 3)
+ {
+ m_status &= 0xc0; // blow away fifo
+ m_s_ram_ptr = 0; // reset sensor pointer
+ set_irq(0); // reset irq
+ }
+}
+
+
+void i8279_device::set_irq(bool state)
+{
+ if ( !m_out_irq_func.isnull() )
+ m_out_irq_func( state );
+}
+
+
+void i8279_device::new_key(UINT8 data, bool skey, bool ckey)
+{
+ UINT8 i, rl, sl;
+ for (i = 0; BIT(data, i); i++);
+ rl = i;
+ if (BIT(m_cmd[0], 0))
+ {
+ for (i = 0; !BIT(data, i); i++);
+ sl = i;
+ }
+ else
+ sl = m_scanner;
+
+ new_fifo( (ckey << 7) | (skey << 6) | (sl << 3) | rl);
+}
+
+
+void i8279_device::new_fifo(UINT8 data)
+{
+ // see if already overrun
+ if (BIT(m_status, 5))
+ return;
+
+ // set overrun flag if full
+ if (BIT(m_status, 3))
+ {
+ m_status |= 0x20;
+ return;
+ }
+
+ m_fifo[m_status & 7] = data;
+
+ // bump fifo size & turn off underrun
+ UINT8 fifo_size = m_status & 7;
+ if ((fifo_size)==7)
+ m_status |= 8; // full
+ else
+ m_status = (m_status & 0xe8) + fifo_size + 1;
+
+ if (!fifo_size)
+ set_irq(1); // something just went into fifo, so int
+}
+
+
+TIMER_CALLBACK( i8279_device::timerproc_callback )
+{
+ reinterpret_cast<i8279_device*>(ptr)->timer_mainloop();
+}
+
+
+void i8279_device::timer_mainloop()
+{
+ // control byte 0
+ // bit 0 - encoded or decoded keyboard scan
+ // bits 1,2 - keyboard type
+ // bit 3 - number of digits to display
+ // bit 4 - left or right entry
+
+ UINT8 scanner_mask = BIT(m_cmd[0], 0) ? 15 : BIT(m_cmd[0], 3) ? 15 : 7;
+ bool decoded = BIT(m_cmd[0], 0);
+ UINT8 kbd_type = (m_cmd[0] & 6) >> 1;
+ bool shift_key = 1;
+ bool ctrl_key = 1;
+ bool strobe_pulse = 0;
+
+ // keyboard
+ // type 0 = kbd, 2-key lockout
+ // type 1 = kdb, n-key
+ // type 2 = sensor
+ // type 3 = strobed
+
+ // Get shift keys
+ if ( !m_in_shift_func.isnull() )
+ shift_key = m_in_shift_func();
+
+ if ( !m_in_ctrl_func.isnull() )
+ ctrl_key = m_in_ctrl_func();
+
+ if (ctrl_key && !m_ctrl_key)
+ strobe_pulse = 1; // low-to-high is a strobe
+
+ m_ctrl_key = ctrl_key;
+
+ // Read a row of keys
+
+ if ( !m_in_rl_func.isnull() )
+ {
+ UINT8 rl = m_in_rl_func(0);
+
+ // see if key still down from last time
+ UINT16 key_down = (m_scanner << 8) | rl;
+ if (key_down == m_key_down)
+ rl = 0xff;
+ else
+ if ((rl == 0xff) && (m_scanner == m_key_down >> 8))
+ m_key_down = 0xffff;
+
+ // now process new key
+ if (rl < 0xff || kbd_type == 2)
+ {
+ m_key_down = key_down;
+ switch (kbd_type)
+ {
+ case 0:
+ case 1:
+ new_key(rl, shift_key, ctrl_key);
+ break;
+ case 2:
+ {
+ UINT8 addr = m_scanner;
+
+ if (decoded)
+ for (addr=0; !BIT(m_scanner, addr); addr++);
+
+ rl ^= 0xff; // inverted
+ if (m_s_ram[addr] != rl)
+ {
+ m_s_ram[addr] = rl;
+
+ // IRQ line goes high if a row change value
+ set_irq(1);
+ }
+ }
+ break;
+ case 3:
+ if (strobe_pulse) new_fifo(rl);
+ break;
+ }
+ }
+ }
+
+ // Increment scanline
+
+ if (decoded)
+ {
+ m_scanner<<= 1;
+ if ((m_scanner & 15)==0)
+ m_scanner = 1;
+ }
+ else
+ m_scanner++;
+
+ m_scanner &= scanner_mask; // 4-bit port
+
+ if ( !m_out_sl_func.isnull() )
+ m_out_sl_func(0, m_scanner);
+
+ // output a digit
+
+ if ( !m_out_disp_func.isnull() )
+ m_out_disp_func(0, m_d_ram[m_scanner] );
+}
+
+
+READ8_MEMBER( i8279_device::status_r )
+{
+ return m_status;
+}
+
+
+READ8_MEMBER( i8279_device::data_r )
+{
+ UINT8 i;
+ bool sensor_mode = ((m_cmd[0] & 6)==4);
+ UINT8 data;
+ if (m_read_flag)
+ {
+ // read the display ram
+ data = m_d_ram[m_d_ram_ptr];
+ if (m_autoinc)
+ m_d_ram_ptr++;
+ }
+ else
+ if (sensor_mode)
+ {
+ // read sensor ram
+ data = m_s_ram[m_s_ram_ptr];
+ if (m_autoinc)
+ m_s_ram_ptr++;
+ else
+ set_irq(0);
+ }
+ else
+ {
+ // read a key from fifo
+ data = m_fifo[0];
+ UINT8 fifo_size = m_status & 7;
+ switch (m_status & 0x38)
+ {
+ case 0x00: // no errors
+ if (!fifo_size)
+ m_status |= 0x10; // underrun
+ else
+ {
+ for (i = 1; i < 8; i++)
+ m_fifo[i-1] = m_fifo[i];
+ fifo_size--;
+ if (!fifo_size)
+ set_irq(0);
+ }
+ break;
+ case 0x28: // overrun
+ case 0x08: // fifo full
+ for (i = 1; i < 8; i++)
+ m_fifo[i-1] = m_fifo[i];
+ break;
+ case 0x10: // underrun
+ if (!fifo_size)
+ break;
+ default:
+ printf("Invalid status: %X\n", m_status);
+ }
+ m_status = (m_status & 0xd0) | fifo_size; // turn off overrun & full
+ }
+
+ m_d_ram_ptr &= 15;
+ m_s_ram_ptr &= 7;
+ return data;
+}
+
+
+WRITE8_MEMBER( i8279_device::cmd_w )
+{//printf("Command: %X=%X ",data>>5,data&31);
+ UINT8 cmd = data >> 5;
+ data &= 0x1f;
+ m_cmd[cmd] = data;
+ switch (cmd)
+ {
+ case 0:
+ if (LOG) logerror("I8279 '%s' kb mode %x, display mode %x\n", tag(), data & 7, (data>>3) & 3);
+ break;
+ case 1:
+ if (data > 1)
+ {
+ logerror("Clock set to %dkHz\n",data*100);
+ timer_adjust();
+ }
+ break;
+ case 2:
+ m_read_flag = 0;
+ if ((m_cmd[0] & 6)==4) // sensor mode only
+ {
+ m_autoinc = BIT(data, 4);
+ m_s_ram_ptr = data & 7;
+ if (LOG) logerror("I8279 '%s' selct sensor row %x, AI %d\n", tag(), m_s_ram_ptr, m_autoinc);
+ }
+ break;
+ case 3:
+ m_read_flag = 1;
+ m_d_ram_ptr = data & 15;
+ m_autoinc = BIT(data, 4);
+ break;
+ case 4:
+ m_d_ram_ptr = data & 15;
+ m_autoinc = BIT(data, 4);
+ break;
+ case 6:
+ if (LOG) logerror("I8279 '%s' clear cmd %x\n", tag(), data);
+ clear_display();
+ break;
+ }
+}
+
+
+WRITE8_MEMBER( i8279_device::data_w )
+{//printf("Data: %X ",data);
+ if (BIT(m_cmd[0], 4) & m_autoinc)
+ {
+ // right-entry autoincrement not implemented yet
+ }
+ else
+ {
+ if (!(m_cmd[5] & 0x04))
+ m_d_ram[m_d_ram_ptr] = (m_d_ram[m_d_ram_ptr] & 0xf0) | (data & 0x0f);
+ if (!(m_cmd[5] & 0x08))
+ m_d_ram[m_d_ram_ptr] = (m_d_ram[m_d_ram_ptr] & 0x0f) | (data & 0xf0);
+
+ if (m_autoinc)
+ m_d_ram_ptr++;
+ }
+ m_d_ram_ptr &= 15;
+}
+