summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/psion_asic5.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/psion_asic5.cpp')
-rw-r--r--src/devices/machine/psion_asic5.cpp459
1 files changed, 459 insertions, 0 deletions
diff --git a/src/devices/machine/psion_asic5.cpp b/src/devices/machine/psion_asic5.cpp
new file mode 100644
index 00000000000..34863197265
--- /dev/null
+++ b/src/devices/machine/psion_asic5.cpp
@@ -0,0 +1,459 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/******************************************************************************
+
+ Psion ASIC5
+
+ ASIC5 is a general purpose I/O chip with a built-in UART that can be set to
+ run in a number of different modes thereby simplifying the task of peripheral
+ design. For example, it is possible to set up ASIC5 to run as a Centronics
+ parallel port interface, an 8-bit parallel I/O port, a serial bar code
+ controller or a serial RS232 converter.
+
+ PC6 PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0
+ Pack Mode Memory Type No. devices Memory Size
+ 1 0 0 0 RAM 0 0 1 device 0 0 0 No memory
+ 0 0 1 Type 1 Flash 0 1 2 devices 0 0 1 32K
+ 0 1 0 Type 2 Flash 1 0 3 devices 0 1 0 64K
+ 1 1 0 ROM 1 1 4 devices 0 1 1 128K
+ 1 1 1 Write protect 1 0 0 256K
+ 1 0 1 512K
+ 1 1 0 1M
+ Peripheral Mode Type
+ 0 x x 0 x x x x 1 RS232 port
+ x x 0 x x x 1 x Centronics (Parallel) port
+ x x 0 x x 1 x x ROM
+ x x 0 x 1 x x x Magnetic Card Reader
+ x x 0 0 x x x x Barcode reader
+ x x 0 1 x x x x USA modem
+ x 1 0 x x x x x Modem
+ 1 x 0 x x x x x RS232 TTL
+
+******************************************************************************/
+
+#include "emu.h"
+#include "psion_asic5.h"
+
+#define VERBOSE 0
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(PSION_ASIC5, psion_asic5_device, "psion_asic5", "Psion ASIC5")
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+psion_asic5_device::psion_asic5_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, PSION_ASIC5, tag, owner, clock)
+ , device_serial_interface(mconfig, *this)
+ , m_mode(~uint8_t(0))
+ , m_in_a_handler(*this, 0)
+ , m_in_b_handler(*this, 0)
+ , m_in_c_handler(*this, 0)
+ , m_out_a_handler(*this)
+ , m_out_b_handler(*this)
+ , m_out_c_handler(*this)
+ , m_out_d_handler(*this)
+ , m_out_cs_handler(*this)
+ , m_int_handler(*this)
+ , m_txd_handler(*this)
+ , m_rts_handler(*this)
+ , m_dtr_handler(*this)
+ , m_rxd(0)
+ , m_cts(0)
+ , m_dsr(0)
+ , m_dcd(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_validity_check - device-specific checks
+//-------------------------------------------------
+
+void psion_asic5_device::device_validity_check(validity_checker &valid) const
+{
+ if ((PACK_MODE != m_mode) && (PERIPHERAL_MODE != m_mode))
+ osd_printf_error("Power on reset pin PC6 not configured\n");
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void psion_asic5_device::device_start()
+{
+ save_item(NAME(m_port_b_counter));
+ save_item(NAME(m_port_b_latch));
+ save_item(NAME(m_port_b_mode));
+ save_item(NAME(m_port_dc_select));
+ save_item(NAME(m_int_mask));
+ save_item(NAME(m_int_status));
+ save_item(NAME(m_control));
+ save_item(NAME(m_bdr));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void psion_asic5_device::device_reset()
+{
+ m_port_b_counter = 0x00;
+ m_port_b_latch = 0x00;
+ m_port_b_mode = 0x00;
+ m_port_dc_select = false;
+ m_int_mask = 0x00;
+ m_int_status = 0x02; // UART transmitter empty
+ m_control = 0x00;
+ m_bdr = -1;
+
+ receive_register_reset();
+ transmit_register_reset();
+}
+
+
+void psion_asic5_device::update_interrupts()
+{
+ int irq = m_int_status & m_int_mask;
+
+ m_int_handler(irq ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+void psion_asic5_device::rcv_callback()
+{
+ if (BIT(m_port_b_mode, 0)) // UART enabled
+ receive_register_update_bit(m_rxd);
+}
+
+void psion_asic5_device::rcv_complete()
+{
+ receive_register_extract();
+
+ if (is_receive_framing_error() || is_receive_parity_error())
+ m_int_status |= 0x04;
+
+ m_int_status |= 0x01;
+ update_interrupts();
+}
+
+void psion_asic5_device::tra_callback()
+{
+ if (BIT(m_port_b_mode, 0)) // UART enabled
+ m_txd_handler(transmit_register_get_data_bit());
+}
+
+void psion_asic5_device::tra_complete()
+{
+ m_int_status |= 0x02;
+ update_interrupts();
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+void psion_asic5_device::data_w(uint16_t data)
+{
+ switch (data & 0x300)
+ {
+ case NULL_FRAME:
+ device_reset();
+ break;
+
+ case CONTROL_FRAME:
+ m_sibo_control = data & 0xff;
+
+ switch (m_sibo_control & 0xc0)
+ {
+ case 0x80: // SerialWrite
+ if (m_sibo_control == 0x93) // Multi Port D and C writes
+ m_port_dc_select = false;
+ break;
+ }
+ break;
+
+ case DATA_FRAME:
+ data &= 0xff;
+ switch (m_sibo_control & 0x0f)
+ {
+ case 0x00: // Port A write data
+ LOG("%s data_w: Port A write data %02x\n", machine().describe_context(), data);
+ m_out_a_handler(data);
+ if (BIT(m_port_b_mode, 1, 2) == 0) // Counter mode
+ {
+ m_out_b_handler(++m_port_b_counter);
+ }
+ break;
+
+ case 0x01: // Port B write data
+ LOG("%s data_w: Port B write data %02x\n", machine().describe_context(), data);
+ if (BIT(m_port_b_mode, 1, 2) == 1 && !BIT(m_control, 3)) // Latch mode
+ {
+ m_out_b_handler(m_port_b_latch = data);
+ }
+ break;
+
+ case 0x02: // Port B control
+ // b0 0 Memory mode, 1 Peripheral mode - enables UART
+ // b1 Port B mode
+ // b2 Port B mode
+ // b3 0 Normal mode, 1 Test mode
+ // b4 Not used
+ // b5 Not used
+ // b6 Not used
+ // b7 Not used
+ //
+ // Port B mode
+ // b2 b1 Mode
+ // 0 0 Counter mode
+ // 0 1 Latch mode
+ // 1 0 Baud rate out on port B
+ // 1 1 Test bus output on port B
+ LOG("%s data_w: Port B mode %02x\n", machine().describe_context(), data);
+ m_port_b_mode = data;
+ break;
+
+ case 0x03: // Port D and C write data
+ switch (BIT(m_port_b_mode, 0))
+ {
+ case 0:
+ if (m_port_dc_select)
+ {
+ LOG("%s data_w: Port C write data %02x\n", machine().describe_context(), data);
+ m_out_c_handler(data);
+ }
+ else
+ {
+ LOG("%s data_w: Port D write data %02x\n", machine().describe_context(), data);
+ m_out_d_handler(data);
+ if (BIT(m_port_b_mode, 1, 2) == 0) // Counter mode
+ {
+ m_out_b_handler(m_port_b_counter = 0);
+ }
+ }
+ m_port_dc_select = true;
+ break;
+ case 1: // UART enabled
+ m_rts_handler(BIT(data, 1));
+ m_dtr_handler(BIT(data, 2));
+ break;
+ }
+ break;
+
+ case 0x06: // Interrupt mask write
+ // b0 UART - UART character received
+ // b1 UART - UART transmitter empty
+ // b2 UART - UART error or modem line status change
+ // b3 PC7 - Barcode switch/general interrupt
+ // b4 SR - Synchronous port1 character received
+ // b5 SR - Synchronous port2 character received
+ // b6 PC4 - Barcode data/general interrupt
+ // b7 PA4 - Centronics busy low/general interrupt
+ LOG("%s data_w: Interrupt mask write %02x\n", machine().describe_context(), data);
+ m_int_mask = data;
+ update_interrupts();
+ break;
+
+ case 0x07: // Control register
+ LOG("%s data_w: Control register %02x\n", machine().describe_context(), data);
+ m_control = data;
+ m_out_cs_handler(data & 7);
+ break;
+
+ case 0x08: // UART Control register
+ // b0 Generate break character
+ // b1 Character length 1
+ // b2 Character length 2
+ // b3 Parity enabled if set
+ // b4 Odd parity if, even if clear
+ // b5 Set for two stop bits, clear for one
+ // b6 Not used
+ // b7 Not used
+ LOG("%s data_w: UART Control register %02x\n", machine().describe_context(), data);
+ set_data_frame(1, 5 + BIT(data, 1, 2), BIT(data, 3) ? (BIT(data, 4) ? PARITY_ODD : PARITY_EVEN) : PARITY_NONE, BIT(data, 5) ? STOP_BITS_2 : STOP_BITS_1);
+ receive_register_reset();
+ transmit_register_reset();
+ break;
+
+ case 0x09: // UART Transmit holding register
+ LOG("%s data_w: UART Transmit holding register %02x\n", machine().describe_context(), data);
+ transmit_register_setup(data);
+ m_int_status &= ~0x02;
+ update_interrupts();
+ break;
+
+ case 0x0a:
+ case 0x0b: // UART Baud rate
+ switch (m_sibo_control & 1)
+ {
+ case 0: m_bdr = (m_bdr & 0xff00) | (data << 0); break; // LSB
+ case 1: m_bdr = (m_bdr & 0x00ff) | (data << 8); break; // MSB
+ }
+ LOG("%s data_w: UART Baud rate divisor %04x, Baud rate %d\n", machine().describe_context(), m_bdr, clock() / 16 / (1 - m_bdr));
+ set_rate(clock() / 16 / (1 - m_bdr));
+ break;
+
+ case 0x0c: // Port 1 and 2 reset
+ LOG("%s data_w: Port 1 and 2 reset %02x\n", machine().describe_context(), data);
+ break;
+
+ default:
+ LOG("%s data_w: unknown control %02x data %02x\n", machine().describe_context(), m_sibo_control, data);
+ break;
+ }
+ break;
+ }
+}
+
+
+uint8_t psion_asic5_device::data_r()
+{
+ uint8_t data = 0x00;
+
+ switch (m_sibo_control & 0xc0)
+ {
+ case 0x40: // SerialSelect
+ switch (m_sibo_control & 0x0f)
+ {
+ case 0x02: // Asic5PackId
+ case 0x03: // Asic5NormalId
+ if (BIT(m_sibo_control, 0) == m_mode)
+ data = m_info_byte; // A5InfoByte
+ break;
+ }
+ break;
+
+ case 0xc0: // SerialRead
+ switch (m_sibo_control & 0x0f)
+ {
+ case 0x00: // Port A read data
+ data = m_in_a_handler();
+ LOG("%s data_r: Port A read data %02x\n", machine().describe_context(), data);
+ if (BIT(m_port_b_mode, 1, 2) == 0) // Counter mode
+ {
+ m_out_b_handler(++m_port_b_counter);
+ }
+ break;
+
+ case 0x01: // Port B read data
+ if (BIT(m_port_b_mode, 1, 2) == 1) // Latch mode
+ {
+ data = m_port_b_latch;
+ }
+ LOG("%s data_r: Port B read data %02x\n", machine().describe_context(), data);
+ break;
+
+ case 0x02: // Port B counter
+ if (BIT(m_port_b_mode, 1, 2) == 0) // Counter mode
+ {
+ m_out_b_handler(++m_port_b_counter);
+ }
+ LOG("%s data_r: Port B counter %02x\n", machine().describe_context(), m_port_b_counter);
+ break;
+
+ case 0x06: // Interrupt mask read
+ data = m_int_mask;
+ LOG("%s data_r: Interrupt mask read %02x\n", machine().describe_context(), data);
+ break;
+
+ case 0x07: // Interrupt status read
+ data = m_int_status & m_int_mask;
+ LOG("%s data_r: Interrupt status read %02x\n", machine().describe_context(), data);
+ break;
+
+ case 0x08: // UART Status register
+ // b0 State of the CTS line (PA1)
+ // b1 State of the DSR line (PA2)
+ // b2 State of the DCD line (PA3)
+ // b3 Transmitter buffer empty
+ // b4 Transmitter busy
+ // b5 Receive data waiting
+ // b6 Overrun or framing error
+ // b7 Parity error
+ data |= m_cts << 0;
+ data |= m_dsr << 1;
+ data |= m_dcd << 2;
+ data |= is_transmit_register_empty() ? 1 << 3 : 0;
+ data |= !is_transmit_register_empty() ? 1 << 4 : 0;
+ data |= is_receive_register_full() ? 1 << 5 : 0;
+ data |= is_receive_framing_error() ? 1 << 6 : 0;
+ data |= is_receive_parity_error() ? 1 << 7 : 0;
+ LOG("%s data_r: UART Status register %02x\n", machine().describe_context(), data);
+ m_int_status &= ~0x04;
+ update_interrupts();
+ break;
+
+ case 0x09: // UART Receive register
+ data = get_received_char();
+ LOG("%s data_r: UART Receive register %02x\n", machine().describe_context(), data);
+ m_int_status &= ~0x01;
+ update_interrupts();
+ break;
+
+ case 0x0c: // Synchronous Port 1 read
+ LOG("%s data_r: Synchronous Port 1 read %02x\n", machine().describe_context(), data);
+ break;
+
+ case 0x0d: // Barcode read data
+ // b0 PC4
+ // b1 PC7
+ // b2 PB2
+ // b3 PB3
+ // b4 PB4
+ // b5 PB5
+ // b6 PB6
+ // b7 PB7
+ LOG("%s data_r: Barcode read data %02x\n", machine().describe_context(), data);
+ break;
+
+ case 0x0e: // Synchronous Port 2 read
+ LOG("%s data_r: Synchronous Port 2 read %02x\n", machine().describe_context(), data);
+ break;
+
+ default:
+ LOG("%s data_r: unknown control %02x data %02x\n", machine().describe_context(), m_sibo_control, data);
+ break;
+ }
+ break;
+ }
+
+ return data;
+}
+
+
+void psion_asic5_device::write_rxd(int state)
+{
+ m_rxd = state;
+ device_serial_interface::rx_w(state);
+}
+
+void psion_asic5_device::write_cts(int state)
+{
+ if (m_cts != state)
+ m_int_status |= 0x04;
+ m_cts = state;
+ update_interrupts();
+
+}
+void psion_asic5_device::write_dsr(int state)
+{
+ if (m_dsr != state)
+ m_int_status |= 0x04;
+ m_dsr = state;
+ update_interrupts();
+}
+
+void psion_asic5_device::write_dcd(int state)
+{
+ if (m_dcd != state)
+ m_int_status |= 0x04;
+ m_dcd = state;
+ update_interrupts();
+}