summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Nigel Barnes <ngbarnes@gmail.com>2023-03-25 12:23:20 +0000
committer Nigel Barnes <ngbarnes@gmail.com>2023-03-25 12:25:50 +0000
commit469c2906d96a0cec96ab5940d5890eb6c11f8ef9 (patch)
tree92b37eb90a284cc4d0a3971cccae024856d2581b
parent2c3cbda8d043e7b7d017cde5f0666f04d33859a9 (diff)
New systems marked not working
------------------------------ Series 3a [Nigel Barnes, The Last Psion] Series 3c [Nigel Barnes, The Last Psion] Series 3mx [Nigel Barnes, The Last Psion] Siena [Nigel Barnes, The Last Psion] Workabout [Nigel Barnes, The Last Psion] Workabout mx [Nigel Barnes, The Last Psion] New clones marked not working ----------------------------- Pocket Book II [Nigel Barnes, The Last Psion] Series 3a (2M) [Nigel Barnes, The Last Psion] Series 3a (2M) (German) [Nigel Barnes, The Last Psion]
-rw-r--r--scripts/src/machine.lua15
-rw-r--r--src/devices/machine/psion_asic5.cpp225
-rw-r--r--src/devices/machine/psion_asic5.h116
-rw-r--r--src/devices/machine/psion_asic9.cpp1134
-rw-r--r--src/devices/machine/psion_asic9.h155
-rw-r--r--src/emu/xtal.cpp1
-rwxr-xr-xsrc/mame/mame.lst15
-rw-r--r--src/mame/psion/psion3a.cpp396
-rw-r--r--src/mame/psion/psion_ssd.cpp284
-rw-r--r--src/mame/psion/psion_ssd.h84
-rw-r--r--src/mame/psion/siena.cpp226
-rw-r--r--src/mame/psion/workabout.cpp265
12 files changed, 2916 insertions, 0 deletions
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua
index ad603da4458..d77e3f27042 100644
--- a/scripts/src/machine.lua
+++ b/scripts/src/machine.lua
@@ -2869,6 +2869,21 @@ end
---------------------------------------------------
--
+--@src/devices/machine/psion_asic5.h,MACHINES["PSION_ASIC"] = true
+--@src/devices/machine/psion_asic9.h,MACHINES["PSION_ASIC"] = true
+---------------------------------------------------
+
+if (MACHINES["PSION_ASIC"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/machine/psion_asic5.cpp",
+ MAME_DIR .. "src/devices/machine/psion_asic5.h",
+ MAME_DIR .. "src/devices/machine/psion_asic9.cpp",
+ MAME_DIR .. "src/devices/machine/psion_asic9.h",
+ }
+end
+
+---------------------------------------------------
+--
--@src/devices/machine/pxa255.h,MACHINES["PXA255"] = true
---------------------------------------------------
diff --git a/src/devices/machine/psion_asic5.cpp b/src/devices/machine/psion_asic5.cpp
new file mode 100644
index 00000000000..8bd69f7e776
--- /dev/null
+++ b/src/devices/machine/psion_asic5.cpp
@@ -0,0 +1,225 @@
+// 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.
+
+ TODO:
+ - implement peripheral mode
+
+******************************************************************************/
+
+#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_PACK, psion_asic5_pack_device, "psion_asic5_pack", "Psion ASIC5 (Pack mode)")
+DEFINE_DEVICE_TYPE(PSION_ASIC5_PERIPHERAL, psion_asic5_peripheral_device, "psion_asic5_peripheral", "Psion ASIC5 (Peripheral mode)")
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+psion_asic5_device::psion_asic5_device(const machine_config &mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , m_in_a_handler(*this)
+ , m_in_b_handler(*this)
+ , m_out_a_handler(*this)
+ , m_out_b_handler(*this)
+ , m_out_c_handler(*this)
+ , m_out_d_handler(*this)
+{
+}
+
+psion_asic5_pack_device::psion_asic5_pack_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : psion_asic5_device(mconfig, PSION_ASIC5_PACK, tag, owner, clock)
+{
+}
+
+psion_asic5_peripheral_device::psion_asic5_peripheral_device(const machine_config &mconfig, const char * tag, device_t *owner, uint32_t clock)
+ : psion_asic5_device(mconfig, PSION_ASIC5_PERIPHERAL, tag, owner, clock)
+ , m_serial(*this, "serial")
+ //, m_irq4_callback(*this)
+ //, m_txd1_callback(*this)
+ //, m_dtr1_callback(*this)
+ //, m_rts1_callback(*this)
+{
+}
+
+
+void psion_asic5_peripheral_device::device_add_mconfig(machine_config &config)
+{
+ NS16550(config, m_serial, clock() / 13);
+ //m_serial->out_int_callback().set([this](int state) { m_irq4_callback(state); });
+ //m_serial->out_tx_callback().set([this](int state) { m_txd1_callback(state); });
+ //m_serial->out_dtr_callback().set([this](int state) { m_dtr1_callback(state); });
+ //m_serial->out_rts_callback().set([this](int state) { m_rts1_callback(state); });
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void psion_asic5_device::device_start()
+{
+ m_in_a_handler.resolve();
+ m_in_b_handler.resolve();
+ m_out_a_handler.resolve_safe();
+ m_out_b_handler.resolve_safe();
+ m_out_c_handler.resolve_safe();
+ m_out_d_handler.resolve_safe();
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void psion_asic5_device::device_reset()
+{
+ m_portb_ctrl = 0x00;
+ m_portb_counter = 0x00;
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+void psion_asic5_device::data_w(uint16_t data)
+{
+ switch (data & 0x300)
+ {
+ case NULL_FRAME:
+ break;
+
+ case CONTROL_FRAME:
+ m_sibo_control = data & 0xff;
+
+ switch (m_sibo_control & 0xc0)
+ {
+ case 0x80: // SerialWrite
+ //if (m_sibo_control == 0x82) // write A3Setup
+ if (m_sibo_control == 0x93) // Multi Port D and C writes
+ m_port_dc_writes = 0;
+ 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);
+ break;
+
+ case 0x01: // Port B write data
+ LOG("%s data_w: Port B write data %02x\n", machine().describe_context(), data);
+ m_out_b_handler(data);
+ break;
+
+ case 0x02: // Port B control
+ LOG("%s data_w: Port B control %02x\n", machine().describe_context(), data);
+ m_portb_ctrl = data;
+ break;
+
+ case 0x03: // Port D and C write data
+ if (m_port_dc_writes)
+ {
+ 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);
+ m_portb_counter = 0x00;
+ m_out_b_handler(m_portb_counter);
+ }
+ m_port_dc_writes++;
+ break;
+
+ case 0x06: // Interrupt mask write
+ LOG("%s data_w: out int mask %02x\n", machine().describe_context(), data);
+ m_interrupt_mask = data;
+ break;
+
+ //case 0x07: // Control register
+ // m = 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
+ if (m_sibo_control == 0x42) // Asic5PackId
+ data = m_info_byte; // A3InfoByte
+ 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 (m_sibo_control & 0x90) // Multi, increment Port B
+ {
+ m_portb_counter++;
+ m_out_b_handler(m_portb_counter);
+ }
+ break;
+
+ case 0x01: // Port B read data
+ data = m_in_b_handler();
+ LOG("%s data_r: Port B read data %02x\n", machine().describe_context(), data);
+ break;
+
+ case 0x02: // Port B counter
+ m_portb_counter++;
+ m_out_b_handler(m_portb_counter);
+ LOG("%s data_r: Port B counter %02x\n", machine().describe_context(), m_portb_counter);
+ break;
+
+ case 0x06: // Interrupt mask write
+ data = m_interrupt_mask;
+ LOG("%s data_r: in int mask %02x\n", machine().describe_context(), data);
+ break;
+
+ //case 0x07: // Control register
+ // m = data;
+ // break;
+
+ default:
+ LOG("%s data_r: unknown control %02x data %02x\n", machine().describe_context(), m_sibo_control, data);
+ break;
+ }
+ break;
+ }
+
+ return data;
+}
diff --git a/src/devices/machine/psion_asic5.h b/src/devices/machine/psion_asic5.h
new file mode 100644
index 00000000000..fbd07e89d46
--- /dev/null
+++ b/src/devices/machine/psion_asic5.h
@@ -0,0 +1,116 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/******************************************************************************
+
+ Psion ASIC5
+
+******************************************************************************/
+
+#ifndef MAME_MACHINE_PSION_ASIC5_H
+#define MAME_MACHINE_PSION_ASIC5_H
+
+#pragma once
+
+#include "machine/ins8250.h"
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> psion_asic5_device
+
+class psion_asic5_device : public device_t
+{
+public:
+ auto readpa_handler() { return m_in_a_handler.bind(); }
+ auto readpb_handler() { return m_in_b_handler.bind(); }
+
+ auto writepa_handler() { return m_out_a_handler.bind(); }
+ auto writepb_handler() { return m_out_b_handler.bind(); }
+ auto writepc_handler() { return m_out_c_handler.bind(); }
+ auto writepd_handler() { return m_out_d_handler.bind(); }
+
+ void set_info_byte(uint8_t info) { m_info_byte = info; }
+
+ void data_w(uint16_t data);
+ uint8_t data_r();
+
+protected:
+ // construction/destruction
+ psion_asic5_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+private:
+ static constexpr uint16_t NULL_FRAME = 0x000;
+ static constexpr uint16_t CONTROL_FRAME = 0x100;
+ static constexpr uint16_t DATA_FRAME = 0x200;
+
+ devcb_read8 m_in_a_handler;
+ devcb_read8 m_in_b_handler;
+ devcb_write8 m_out_a_handler;
+ devcb_write8 m_out_b_handler;
+ devcb_write8 m_out_c_handler;
+ devcb_write8 m_out_d_handler;
+
+ // Slave control mode
+ static constexpr uint8_t READ_WRITE_SELECT = 0xc0;
+ static constexpr uint8_t BYTE_WORD_TRANSFER = 0xa0;
+ static constexpr uint8_t SINGLE_MULTI_TRANSFER = 0x90;
+
+ // registers
+ uint8_t m_portb_counter;
+ uint8_t m_portb_ctrl;
+ int m_port_dc_writes;
+ uint8_t m_interrupt_mask;
+
+ // internal
+ uint8_t m_info_byte;
+ uint8_t m_sibo_control;
+};
+
+
+// ======================> psion_asic5_pack_device
+
+class psion_asic5_pack_device : public psion_asic5_device
+{
+public:
+ // construction/destruction
+ psion_asic5_pack_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+};
+
+
+// ======================> psion_asic5_peripheral_device
+
+class psion_asic5_peripheral_device : public psion_asic5_device
+{
+public:
+ // construction/destruction
+ psion_asic5_peripheral_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ //auto irq4() { return m_irq4_callback.bind(); }
+ //auto txd1() { return m_txd1_callback.bind(); }
+ //auto dtr1() { return m_dtr1_callback.bind(); }
+ //auto rts1() { return m_rts1_callback.bind(); }
+
+protected:
+ virtual void device_add_mconfig(machine_config &config) override;
+
+private:
+ required_device<ns16550_device> m_serial;
+
+ //devcb_write_line m_irq4_callback;
+ //devcb_write_line m_txd1_callback;
+ //devcb_write_line m_dtr1_callback;
+ //devcb_write_line m_rts1_callback;
+};
+
+
+// device type definition
+DECLARE_DEVICE_TYPE(PSION_ASIC5_PACK, psion_asic5_pack_device)
+DECLARE_DEVICE_TYPE(PSION_ASIC5_PERIPHERAL, psion_asic5_peripheral_device)
+
+#endif // MAME_MACHINE_PSION_ASIC5_H
diff --git a/src/devices/machine/psion_asic9.cpp b/src/devices/machine/psion_asic9.cpp
new file mode 100644
index 00000000000..3f78e177dc3
--- /dev/null
+++ b/src/devices/machine/psion_asic9.cpp
@@ -0,0 +1,1134 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/******************************************************************************
+
+ Psion ASIC9
+
+ ASIC9 is a composite chip comprising of a V30H processor, ASIC1, ASIC2 and
+ general I/O and PSU control logic all on one IC. ASIC9 thus integrates all
+ the digital logic required to produce a SIBO architecture computer less the
+ memory onto one chip. ASIC9 has a few additional features such as an extra
+ free-running clock (FRC) and a codec interface for sound.
+
+ TODO:
+ - memory protection
+ - improve RAM configuration for mx machines
+ - set RTC timer
+
+******************************************************************************/
+
+#include "emu.h"
+#include "psion_asic9.h"
+#include "cpu/nec/nec.h"
+#include "screen.h"
+
+
+#define VERBOSE 0
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(PSION_ASIC9, psion_asic9_device, "psion_asic9", "Psion ASIC9")
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+psion_asic9_device::psion_asic9_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, PSION_ASIC9, tag, owner, clock)
+ , device_memory_interface(mconfig, *this)
+ , device_video_interface(mconfig, *this)
+ , m_maincpu(*this, "maincpu")
+ , m_ram(*this, finder_base::DUMMY_TAG)
+ , m_rom(*this, finder_base::DUMMY_TAG)
+ , m_ram_config("asic9_ram", ENDIANNESS_LITTLE, 16, 24, 0)
+ , m_rom_config("asic9_rom", ENDIANNESS_LITTLE, 16, 24, 0)
+ , m_tick_timer(nullptr)
+ , m_frc1_timer(nullptr)
+ , m_frc2_timer(nullptr)
+ , m_watchdog_timer(nullptr)
+ , m_rtc_timer(nullptr)
+ , m_configure_ram_cb(*this)
+ , m_buz_cb(*this)
+ , m_col_cb(*this)
+ , m_port_ab_r(*this)
+ , m_port_ab_w(*this)
+ , m_data_r(*this)
+ , m_data_w(*this)
+{
+}
+
+
+void psion_asic9_device::mem_map(address_map &map)
+{
+ map(0x00000, 0xfffff).rw(FUNC(psion_asic9_device::mem_r), FUNC(psion_asic9_device::mem_w));
+}
+
+void psion_asic9_device::io_map(address_map &map)
+{
+ map(0x0000, 0x00ff).rw(FUNC(psion_asic9_device::io_r), FUNC(psion_asic9_device::io_w));
+}
+
+
+//-------------------------------------------------
+// device_add_mconfig - add device configuration
+//-------------------------------------------------
+
+void psion_asic9_device::device_add_mconfig(machine_config &config)
+{
+ V30(config, m_maincpu, DERIVED_CLOCK(1, 1));
+ m_maincpu->set_addrmap(AS_PROGRAM, &psion_asic9_device::mem_map);
+ m_maincpu->set_addrmap(AS_IO, &psion_asic9_device::io_map);
+ m_maincpu->set_irq_acknowledge_callback(FUNC(psion_asic9_device::inta_cb));
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+device_memory_interface::space_config_vector psion_asic9_device::memory_space_config() const
+{
+ return space_config_vector{
+ std::make_pair(AS_A9_RAM, &m_ram_config),
+ std::make_pair(AS_A9_ROM, &m_rom_config)
+ };
+}
+
+
+//-------------------------------------------------
+// device_resolve_objects - resolve objects that
+// may be needed for other devices to set
+// initial conditions at start time
+//-------------------------------------------------
+
+void psion_asic9_device::device_resolve_objects()
+{
+ m_buz_cb.resolve_safe();
+ m_col_cb.resolve_safe();
+ m_port_ab_r.resolve_safe(0);
+ m_port_ab_w.resolve_safe();
+ m_data_r.resolve_all_safe(0x00);
+ m_data_w.resolve_all_safe();
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void psion_asic9_device::device_start()
+{
+ m_ram_space = &space(AS_A9_RAM);
+ m_rom_space = &space(AS_A9_ROM);
+
+ switch (m_ram->size())
+ {
+ case 0x010000: case 0x020000:
+ m_ram_type = 0;
+ break;
+
+ case 0x040000: case 0x080000:
+ m_ram_type = 1;
+ break;
+
+ case 0x100000: case 0x200000:
+ m_ram_type = 2;
+ break;
+
+ case 0x400000: case 0x800000:
+ m_ram_type = 3;
+ break;
+ }
+
+ configure_ram(m_ram_type);
+ configure_rom();
+
+ m_tick_timer = timer_alloc(FUNC(psion_asic9_device::tick), this);
+ m_frc1_timer = timer_alloc(FUNC(psion_asic9_device::frc1), this);
+ m_frc2_timer = timer_alloc(FUNC(psion_asic9_device::frc2), this);
+ m_watchdog_timer = timer_alloc(FUNC(psion_asic9_device::watchdog), this);
+ m_rtc_timer = timer_alloc(FUNC(psion_asic9_device::rtc), this);
+ m_busy_timer = timer_alloc(FUNC(psion_asic9_device::busy), this);
+
+ m_a9_control = 0x00;
+ m_a9_status = 0x00;
+
+ save_item(NAME(m_a9_control));
+ save_item(NAME(m_a9_lcd_size));
+ save_item(NAME(m_a9_interrupt_status));
+ save_item(NAME(m_a9_interrupt_mask));
+ save_item(NAME(m_a9_protection_mode));
+ save_item(NAME(m_a9_protection_upper));
+ save_item(NAME(m_a9_protection_lower));
+ save_item(NAME(m_a9_port_ab_ddr));
+ save_item(NAME(m_a9_port_c_ddr));
+ save_item(NAME(m_a9_port_d_ddr));
+ save_item(NAME(m_a9_psel_6000));
+ save_item(NAME(m_a9_psel_7000));
+ save_item(NAME(m_a9_psel_8000));
+ save_item(NAME(m_a9_psel_9000));
+ save_item(NAME(m_a9_control_extra));
+ save_item(NAME(m_frc1_count));
+ save_item(NAME(m_frc1_reload));
+ save_item(NAME(m_frc2_count));
+ save_item(NAME(m_frc2_reload));
+ save_item(NAME(m_watchdog_count));
+ save_item(NAME(m_rtc));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void psion_asic9_device::device_reset()
+{
+ m_tick_timer->adjust(attotime::from_hz(32.768), 0, attotime::from_hz(32.768));
+ m_frc1_timer->adjust(attotime::from_hz(512000), 0, attotime::from_hz(512000));
+ m_frc2_timer->adjust(attotime::from_hz(512000), 0, attotime::from_hz(512000));
+ m_watchdog_timer->adjust(attotime::from_hz(4), 0, attotime::from_hz(4));
+ m_rtc_timer->adjust(attotime::from_hz(1), 0, attotime::from_hz(1));
+
+ m_post = 0x00;
+
+ m_a9_interrupt_status = 0x00;
+ m_a9_interrupt_mask = 0x00;
+
+ m_frc1_count = 0;
+ m_frc1_reload = 0;
+ m_frc2_count = 0;
+ m_frc2_reload = 0;
+ m_watchdog_count = 0;
+ m_a9_protection_mode = false;
+ m_a9_protection_lower = 0x00;
+ m_a9_protection_upper = 0x00;
+ m_a9_port_ab_ddr = 0x00;
+ m_a9_port_c_ddr = 0x00;
+ m_a9_port_d_ddr = 0x00;
+ m_a9_psel_6000 = 0x00;
+ m_a9_psel_7000 = 0x00;
+ m_a9_psel_8000 = 0x00;
+ m_a9_psel_9000 = 0x00;
+ m_a9_control_extra = 0x00;
+ m_rtc = time(nullptr) - 946684800;
+
+ //m_a9_status |= 0x2000; // A9MReset
+ m_a9_status |= 0xe000; // A9MCold
+ m_a9_serial_control = 0x00;
+ m_a9_channel_select = 0x00;
+}
+
+
+TIMER_CALLBACK_MEMBER(psion_asic9_device::tick)
+{
+ m_a9_interrupt_status |= 0x02; // Timer
+ update_interrupts();
+}
+
+TIMER_CALLBACK_MEMBER(psion_asic9_device::frc1)
+{
+ switch (--m_frc1_count)
+ {
+ case 0x0000:
+ m_a9_interrupt_status |= 0x40; // A9MFrc1
+ update_interrupts();
+ break;
+
+ case 0xffff:
+ if (BIT(m_a9_control, 12)) // A9MFrc1PreScale
+ m_frc1_count = m_frc1_reload;
+ break;
+ }
+}
+
+TIMER_CALLBACK_MEMBER(psion_asic9_device::frc2)
+{
+ switch (--m_frc2_count)
+ {
+ case 0x0000:
+ m_a9_interrupt_status |= 0x80; // A9MFrc2
+ update_interrupts();
+ break;
+
+ case 0xffff:
+ if (BIT(m_a9_control, 14)) // A9MFrc2PreScale
+ m_frc2_count = m_frc2_reload;
+ break;
+ }
+}
+
+TIMER_CALLBACK_MEMBER(psion_asic9_device::watchdog)
+{
+ m_watchdog_count++;
+ m_watchdog_count &= 3;
+
+ if (m_watchdog_count == 3)
+ {
+ m_a9_status |= 0x0001; // A9MWatchDogNMI
+ update_interrupts();
+ }
+}
+
+TIMER_CALLBACK_MEMBER(psion_asic9_device::rtc)
+{
+ m_rtc++;
+}
+
+WRITE_LINE_MEMBER(psion_asic9_device::eint_w)
+{
+ if (state)
+ m_a9_interrupt_status |= 0x10; // Asic2Int
+ else
+ m_a9_interrupt_status &= ~0x10;
+
+ update_interrupts();
+}
+
+WRITE_LINE_MEMBER(psion_asic9_device::enmi_w)
+{
+ if (state)
+ m_a9_control |= 0x0200; // ExternalNmi
+ else
+ m_a9_control &= ~0x0200;
+
+ update_interrupts();
+}
+
+
+WRITE_LINE_MEMBER(psion_asic9_device::medchng_w)
+{
+ if (state)
+ m_a9_status |= 0x04; // A9MDoorNMI
+ else
+ m_a9_status &= ~0x04;
+
+ update_interrupts();
+}
+
+void psion_asic9_device::update_interrupts()
+{
+ bool irq = m_a9_interrupt_status & m_a9_interrupt_mask;
+ bool nmi = m_a9_status & 0x000f;
+
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, irq ? ASSERT_LINE : CLEAR_LINE);
+ m_maincpu->set_input_line(INPUT_LINE_NMI, nmi ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+IRQ_CALLBACK_MEMBER(psion_asic9_device::inta_cb)
+{
+ // IRQ Vector Name Description
+ // 0 0x78 CSINT CODEC sound interrupt.
+ // 1 0x79 TINT 32 Hz tick interrupt.
+ // 2 0x7A SSINT Serial slave interrupt.
+ // 3 0x7B EINT0 External interrupt input 0.
+ // 4 0x7C EINT1 External interrupt input 1 (inverted).
+ // 5 0x7D EINT2 External interrupt input 2 (inverted).
+ // 6 0x7E FRC1OI FRC1 overflow interrupt.
+ // 7 0x7F FRC2OI FRC2 overflow interrupt.
+ uint8_t vector = 0x78;
+ for (int irq = 0; irq < 8; irq++)
+ {
+ if (m_a9_interrupt_status & m_a9_interrupt_mask & (1 << irq))
+ {
+ vector += irq;
+ break;
+ }
+ }
+ return vector;
+}
+
+
+TIMER_CALLBACK_MEMBER(psion_asic9_device::busy)
+{
+ m_maincpu->set_input_line(NEC_INPUT_LINE_POLL, ASSERT_LINE);
+}
+
+
+//**************************************************************************
+// RAM/ROM CONFIGURATION
+//**************************************************************************
+
+uint32_t psion_asic9_device::ram_device_size(uint8_t device_type)
+{
+ uint32_t size = 0;
+
+ switch (device_type & 3)
+ {
+ case 0: size = 0x010000; break; // 256KBits
+ case 1: size = 0x040000; break; // 1MBits
+ case 2: size = 0x100000; break; // 4MBits
+ case 3: size = 0x400000; break; // 16MBits
+ }
+ return size;
+}
+
+void psion_asic9_device::configure_ram(uint8_t device_type)
+{
+ uint32_t device_size_actual = ram_device_size(m_ram_type);
+ uint32_t device_size_test = ram_device_size(device_type);
+
+ m_ram_space->unmap_readwrite(0, 0xffffff);
+
+ for (int i = 0; i < (m_ram->size() / device_size_actual); i++)
+ {
+ offs_t addrstart = device_size_test * i;
+ offs_t addrend = (device_size_test * i) + std::min(device_size_actual, device_size_test) - 1;
+ offs_t addrmirror = 0xffffff ^ ((device_size_test * 4) - 1);
+
+ if (device_size_actual < device_size_test)
+ addrmirror ^= (device_size_test - device_size_actual);
+
+ LOG("configure_ram: %d type %d ramsize %06x devsize %06x start %06x end %06x mirror %06x\n", i, device_type, m_ram->size(), device_size_actual, addrstart, addrend, addrmirror);
+ m_ram_space->install_ram(addrstart, addrend, addrmirror, m_ram->pointer() + (device_size_actual * i));
+ }
+}
+
+void psion_asic9_device::configure_rom()
+{
+ m_rom_space->install_rom(0x800000, 0x800000 + m_rom->bytes() - 1, 0x7fffff ^ (m_rom->bytes() - 1), m_rom->base());
+}
+
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+uint8_t psion_asic9_device::col_r()
+{
+ return m_a9_control_extra & 0x0f;
+}
+
+
+bool psion_asic9_device::is_protected(offs_t offset)
+{
+ //if (m_a9_protection_mode && (offset <= m_a9_protection_lower || offset > m_a9_protection_upper))
+ //{
+ // LOG("%s is_protected: %05x < %05x <= %05x\n", machine().describe_context(), m_a9_protection_lower, offset, m_a9_protection_upper);
+ // m_a9_status |= 0x0002; // A9MProtectedModeNMI
+ // update_interrupts();
+ // return true;
+ //}
+ return false;
+}
+
+uint16_t psion_asic9_device::mem_r(offs_t offset, uint16_t mem_mask)
+{
+ uint16_t data = 0xffff;
+
+ offset <<= 1;
+
+ switch (offset & 0xf0000)
+ {
+ case 0x00000: case 0x10000: case 0x20000: case 0x30000: case 0x40000: case 0x50000:
+ data = m_ram_space->read_word(offset, mem_mask);
+ break;
+
+ case 0x60000:
+ data = m_ram_space->read_word((m_a9_psel_6000 << 16) | (offset & 0xffff), mem_mask);
+ break;
+
+ case 0x70000:
+ data = m_ram_space->read_word((m_a9_psel_7000 << 16) | (offset & 0xffff), mem_mask);
+ break;
+
+ case 0x80000:
+ data = m_rom_space->read_word((m_a9_psel_8000 << 16) | (offset & 0xffff), mem_mask);
+ break;
+
+ case 0x90000:
+ data = m_rom_space->read_word((m_a9_psel_9000 << 16) | (offset & 0xffff), mem_mask);
+ break;
+
+ case 0xa0000: case 0xb0000: case 0xc0000: case 0xd0000: case 0xe0000: case 0xf0000:
+ data = m_rom_space->read_word(0xf00000 | offset, mem_mask);
+ break;
+ }
+
+ return data;
+}
+
+void psion_asic9_device::mem_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ offset <<= 1;
+
+ if (!is_protected(offset))
+ {
+ switch (offset & 0xf0000)
+ {
+ case 0x00000: case 0x10000: case 0x20000: case 0x30000: case 0x40000: case 0x50000:
+ m_ram_space->write_word(offset, data, mem_mask);
+ break;
+
+ case 0x60000:
+ m_ram_space->write_word((m_a9_psel_6000 << 16) | (offset & 0xffff), data, mem_mask);
+ break;
+
+ case 0x70000:
+ m_ram_space->write_word((m_a9_psel_7000 << 16) | (offset & 0xffff), data, mem_mask);
+ break;
+ }
+ }
+}
+
+
+uint16_t psion_asic9_device::io_r(offs_t offset, uint16_t mem_mask)
+{
+ uint16_t data = 0x00;
+
+ offset <<= 1;
+
+ switch (offset)
+ {
+ case 0x00: // A9Index - Not used
+ LOG("%s io_r: A9Index => %02x\n", machine().describe_context(), data);
+ break;
+
+ case 0x02: // A9WControl
+ // b0 A9MDisableClockDivide
+ // b1 A9MDisableIoWait
+ // b2 A9MDisableMemWait
+ // b3-b4 A9MRamDeviceSize
+ // b5 A9MDisableDMADivide
+ // b6 A9MArmStandBy
+ // b7 A9MZeroIsGrayMode
+ // b8 A9MDoorNMIEnable
+ // b9 A9MLowBatNMIEnable
+ // b10 A9MLcdEnable
+ // b11 A9MSoundEnable
+ // b12 A9MFrc1PreScale
+ // b13 A9MFrc1Is512KHzOr1Hz
+ // b14 A9MFrc2PreScale
+ // b15 A9MFrc2Is512KHzOr1KHz
+ data = m_a9_control; // | 0x18;
+ LOG("%s io_r: A9WControl => %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x04: // A9WStatus
+ // b0 A9MWatchDogNMI
+ // b1 A9MProtectedModeNMI
+ // b2 A9MDoorNMI
+ // b3 A9MLowBatNMI
+ // b4 A9MDoorSwitch
+ // b5 A9MMainsPresent
+ // b6 A9MSlaveClock
+ // b7 A9MKeyboard
+ // b8 A9MSlaveDataValid
+ // b9 A9MSlaveControlFrame
+ // b10 A9MSlaveOverrun
+ // b11 A9MFifoFull
+ // b12 A9MNoBattery
+ // b13 A9MReset
+ // b14 A9MPowerFail
+ // b15 A9MCold
+ data = m_a9_status;
+ if (!machine().side_effects_disabled())
+ {
+ m_a9_status &= ~0x2000; // clear A9MReset
+ }
+ LOG("%s io_r: A9WStatus => %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x06: // A9BInterruptStatus
+ if (ACCESSING_BITS_0_7)
+ {
+ data = m_a9_interrupt_status;
+ LOG("%s io_r: A1InterruptStatus => %02x\n", machine().describe_context(), data);
+ }
+ break;
+
+ case 0x08: // A9BInterruptMask
+ if (ACCESSING_BITS_0_7)
+ {
+ data = m_a9_interrupt_mask;
+ LOG("%s io_r: A9BInterruptMask => %02x\n", machine().describe_context(), data);
+ }
+ break;
+
+ case 0x12: // A9WFrc1Data
+ data = m_frc1_count;
+ LOG("%s io_r: A9WFrc1Data => %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x14: // A9BProtectionOff
+ if (ACCESSING_BITS_0_7)
+ {
+ //LOG("%s io_r: A9BProtectionOff => %02x\n", machine().describe_context(), data);
+ m_a9_protection_mode = false;
+ }
+ break;
+
+ case 0x1a: // A9BSoundData
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_r: A9BSoundData => %02x\n", machine().describe_context(), data);
+ }
+ break;
+
+ case 0x1e: // A9WFrc2Data
+ data = m_frc2_count;
+ LOG("%s io_r: A9WFrc2Data => %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x20: // A9WPortABData
+ data = m_port_ab_r() & ~m_a9_port_ab_ddr;
+ LOG("%s io_r: A9WPortABData => %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x22: // A9WPortABDDR
+ if (ACCESSING_BITS_0_7)
+ {
+ data = m_a9_port_ab_ddr & 0x00ff;
+ LOG("%s io_r: A9WPortADDR => %02x\n", machine().describe_context(), data);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ data = m_a9_port_ab_ddr & 0xff00;
+ LOG("%s io_r: A9WPortBDDR => %02x\n", machine().describe_context(), data >> 8);
+ }
+ break;
+
+ case 0x24: // A9WPortCDData
+ if (ACCESSING_BITS_0_7)
+ {
+ data = 0x00;
+ LOG("%s io_r: A9WPortCData => %02x\n", machine().describe_context(), data);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ data = 0x00 << 8;
+ LOG("%s io_r: A9WPortDData => %02x\n", machine().describe_context(), data >> 8);
+ }
+ break;
+
+ case 0x26: // A9BPortCDDDR
+ if (ACCESSING_BITS_0_7)
+ {
+ data |= m_a9_port_c_ddr;
+ LOG("%s io_r: A9BPortCDDR => %04x\n", machine().describe_context(), data);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ data |= m_a9_port_d_ddr << 8;
+ LOG("%s io_r: A9WPortDDDR <= %02x\n", machine().describe_context(), data >> 8);
+ }
+ break;
+
+ case 0x28: // A9BPageSelect6000
+ if (ACCESSING_BITS_0_7)
+ {
+ data |= m_a9_psel_6000;
+ LOG("%s io_r: A9BPageSelect6000 => %02x\n", machine().describe_context(), data);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ data |= m_a9_psel_7000 << 8;
+ LOG("%s io_r: A9BPageSelect7000 => %02x\n", machine().describe_context(), data >> 8);
+ }
+ break;
+
+ case 0x2a: // A9BPageSelect8000
+ if (ACCESSING_BITS_0_7)
+ {
+ data |= m_a9_psel_8000;
+ //LOG("%s io_r: A9BPageSelect8000 => %02x\n", machine().describe_context(), data);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ data |= m_a9_psel_9000 << 8;
+ //LOG("%s io_r: A9BPageSelect9000 => %02x\n", machine().describe_context(), data >> 8);
+ }
+ break;
+
+ case 0x2c: // A9WControlExtra
+ data = m_a9_control_extra;
+ LOG("%s io_r: A9WControlExtra => %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x2e: // A9WPumpControl
+ LOG("%s io_r: A9WPumpControl <= %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x80: // A9WRtcLSW
+ data = m_rtc & 0x0000ffff;
+ //LOG("%s io_r: A9WRtcLSW <= %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x82: // A9WRtcMSW
+ data = (m_rtc & 0xffff0000) >> 16;
+ //LOG("%s io_r: A9WRtcMSW <= %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x88: // A9BSlaveData
+ LOG("%s io_r: A9BSlaveData <= %02x\n", machine().describe_context(), data);
+ break;
+
+ case 0x8a: // A9BSerialData
+ data = m_a9_serial_data;
+ if ((m_a9_serial_control & 0x10) == 0x10)
+ m_a9_serial_data = receive_frame();
+ LOG("%s io_r: A9BSerialData => %02x\n", machine().describe_context(), data);
+ break;
+
+ case 0x8e: // A9BChannelSelect
+ data = m_a9_channel_select;
+ break;
+
+ default:
+ logerror("%s io_r: Unhandled register %02x => %04x\n", machine().describe_context(), offset, data);
+ break;
+ }
+ return data;
+}
+
+void psion_asic9_device::io_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ offset <<= 1;
+
+ switch (offset)
+ {
+ case 0x00: // Post
+ if (m_post != data)
+ {
+ switch (data)
+ {
+ case 0x10: LOG("%s io_w: Post <= %02x ROM Test\n", machine().describe_context(), data); break;
+ case 0x20: LOG("%s io_w: Post <= %02x System RAM Test\n", machine().describe_context(), data); break;
+ case 0x30: LOG("%s io_w: Post <= %02x Video RAM Test\n", machine().describe_context(), data); break;
+ case 0xf0: LOG("%s io_w: Post <= %02x Complete\n", machine().describe_context(), data); break;
+ default: LOG("%s io_w: Post <= %02x Unknown\n", machine().describe_context(), data); break;
+ }
+ m_post = data;
+ }
+ break;
+
+ case 0x02: // A9WControl
+ // b0 A9MDisableClockDivide
+ // b1 A9MDisableIoWait
+ // b2 A9MDisableMemWait
+ // b3-b4 A9MRamDeviceSize
+ // b5 A9MDisableDMADivide
+ // b6 A9MArmStandBy
+ // b7 A9MZeroIsGrayMode
+ // b8 A9MDoorNMIEnable
+ // b9 A9MLowBatNMIEnable
+ // b10 A9MLcdEnable
+ // b11 A9MSoundEnable
+ // b12 A9MFrc1PreScale
+ // b13 A9MFrc1Is512KHzOr1Hz
+ // b14 A9MFrc2PreScale
+ // b15 A9MFrc2Is512KHzOr1KHz
+ LOG("%s io_w: A9WControl <= %04x, RamDeviceSize %s, Frc1 %s%s, Frc2 %s%s\n", machine().describe_context(), data,
+ BIT(data, 3,2) == 0 ? "256KBits" : (BIT(data, 3,2) == 1 ? "1MBits" : (BIT(data, 3,2) == 2 ? "4MBits" : "16MBits")),
+ BIT(data, 13) ? "512KHz" : "1Hz", BIT(data, 12) ? " PreScale" : "",
+ BIT(data, 15) ? "512KHz" : "1024Hz", BIT(data, 14) ? " PreScale" : "");
+
+ if (BIT(data, 3, 2) != BIT(m_a9_control, 3, 2))
+ {
+ configure_ram(BIT(data, 3, 2));
+ }
+ if (BIT(data, 13) != BIT(m_a9_control, 13))
+ {
+ if (data & 0x2000)
+ m_frc1_timer->adjust(attotime::zero, 0, attotime::from_hz(512000));
+ else
+ m_frc1_timer->adjust(attotime::zero, 0, attotime::from_hz(1));
+ }
+ if (BIT(data, 15) != BIT(m_a9_control, 15))
+ {
+ if (data & 0x8000)
+ m_frc2_timer->adjust(attotime::zero, 0, attotime::from_hz(512000));
+ else
+ m_frc2_timer->adjust(attotime::zero, 0, attotime::from_hz(1024));
+ }
+ m_a9_control = data;
+ break;
+
+ case 0x04: // A9WLcdSize
+ // b0-b10 A9MLcdNumberOfPixels - (end of frame address / 16) - 1
+ // b11-b15 A9MLcdLineLength - (number of pixels per line / 32) - 1
+ LOG("%s io_w: A9WLcdSize <= %04x, Pixels in line %d, Total pixels in display %d\n", machine().describe_context(), data, (BIT(data, 11, 5) + 1) * 32, (BIT(data, 0, 11) + 1) * 128);
+ m_a9_lcd_size = data;
+ break;
+
+ case 0x06: // A9WLcdControl
+ // b0-b4 LcdPixelRate - 3
+ // b5-b9 LcdACLineRate - 13
+ // b10-b11 LcdMode - 3 (Dual Screen mode)
+ LOG("%s io_w: A9WLcdControl <= %04x, Mode %d %s Page\n", machine().describe_context(), data, BIT(data, 10,2), BIT(data, 10,2) == 3 ? "Dual" : "Single");
+ break;
+
+ case 0x08: // A9BInterruptMask
+ if (ACCESSING_BITS_0_7)
+ {
+ // b0 A9MSound
+ // b1 A9MTimer
+ // b2 A9MSlave
+ // b3 A9MExpIntC
+ // b4 A9MExpIntA
+ // b5 A9MExpIntB
+ // b6 A9MFrc1
+ // b7 A9MFrc2
+ LOG("%s io_w: A9BInterruptMask <= %02x\n", machine().describe_context(), data & 0xff);
+ m_a9_interrupt_mask = data & 0xff;
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ LOG("%s io_w: A9BNmiClear <= %02x\n", machine().describe_context(), data >> 8);
+ m_a9_status &= 0xfff0;
+ }
+ update_interrupts();
+ break;
+
+ case 0x0a: // A9BNonSpecificEoi
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9BNonSpecificEoi <= %02x\n", machine().describe_context(), data & 0xff);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ LOG("%s io_w: A9BStartFlagClear <= %02x\n", machine().describe_context(), data >> 8);
+ }
+ break;
+
+ case 0x0c: // A9BTimerEoiW
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9BTimerEoi <= %02x\n", machine().describe_context(), data & 0xff);
+ m_a9_interrupt_status &= ~0x02; // Timer
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ LOG("%s io_w: A9BSerialSlaveEoi <= %02x\n", machine().describe_context(), data >> 8);
+ m_a9_interrupt_status &= ~0x04; // SerialSlave
+ }
+ update_interrupts();
+ break;
+
+ case 0x0e: // A9Frc1Eoi
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9Frc1Eoi <= %02x\n", machine().describe_context(), data & 0xff);
+ m_a9_interrupt_status &= ~0x40; // Frc1Expired
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ LOG("%s io_w: A9Frc2Eoi <= %02x\n", machine().describe_context(), data >> 8);
+ m_a9_interrupt_status &= ~0x80; // Frc2Expired
+ }
+ update_interrupts();
+ break;
+
+ case 0x10: // A9WResetWatchDog
+ //LOG("%s io_w: A9WResetWatchDog <= %04x\n", machine().describe_context(), data);
+ m_watchdog_count = 0;
+ break;
+
+ case 0x12: // A9WFrc1Data
+ LOG("%s io_w: A9WFrc1Data <= %04x\n", machine().describe_context(), data);
+ m_frc1_reload = data;
+ m_frc1_count = data;
+ break;
+
+ case 0x14: // A9BProtectionOn
+ if (ACCESSING_BITS_0_7)
+ {
+ //LOG("%s io_w: A9BProtectionOn <= %02x\n", machine().describe_context(), data);
+ m_a9_protection_mode = true;
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ //LOG("%s io_w: A9BProtectionOff <= %02x\n", machine().describe_context(), data);
+ m_a9_protection_mode = false;
+ }
+ break;
+
+ case 0x16: // A9WProtectionUpper
+ //LOG("%s io_w: A9WProtectionUpper <= %04x\n", machine().describe_context(), data);
+ m_a9_protection_upper = (data << 4) | 0x0f;
+ break;
+
+ case 0x18: // A9WProtectionLower
+ //LOG("%s io_w: A9WProtectionLower <= %04x\n", machine().describe_context(), data);
+ m_a9_protection_lower = data << 4;
+ break;
+
+ case 0x1a: // A9BSoundData
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9BSoundData <= %04x\n", machine().describe_context(), data);
+ }
+ break;
+
+ case 0x1c: // A9BSoundEoi
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9BSoundEoi <= %02x\n", machine().describe_context(), data);
+ m_a9_interrupt_status &= ~0x01; // Sound
+ update_interrupts();
+ }
+ break;
+
+ case 0x1e: // A9WFrc2Data
+ LOG("%s io_w: A9WFrc2Data <= %04x\n", machine().describe_context(), data);
+ m_frc2_reload = data;
+ m_frc2_count = data;
+ break;
+
+ case 0x20: // A9WPortABData
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9WPortAData <= %02x\n", machine().describe_context(), data & 0xff);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ LOG("%s io_w: A9WPortBData <= %02x\n", machine().describe_context(), data >> 8);
+ }
+ break;
+
+ case 0x22: // A9WPortABDDR
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9WPortADDR <= %02x\n", machine().describe_context(), data & 0xff);
+ m_a9_port_ab_ddr = (m_a9_port_ab_ddr & 0xff00) | data;
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ LOG("%s io_w: A9WPortBDDR <= %02x\n", machine().describe_context(), data >> 8);
+ m_a9_port_ab_ddr = (m_a9_port_ab_ddr & 0x00ff) | (data << 8);
+ }
+ break;
+
+ case 0x24: // A9WPortCDData
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9WPortCData <= %02x\n", machine().describe_context(), data & 0xff);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ LOG("%s io_w: A9WPortDData <= %02x\n", machine().describe_context(), data >> 8);
+ }
+ break;
+
+ case 0x26: // A9BPortCDDDR
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9WPortCDDR <= %02x\n", machine().describe_context(), data & 0xff);
+ m_a9_port_c_ddr = data & 0xff;
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ LOG("%s io_w: A9WPortDDDR <= %02x\n", machine().describe_context(), data >> 8);
+ m_a9_port_d_ddr = data >> 8;
+ }
+ break;
+
+ case 0x28: // A9BPageSelect6000
+ if (ACCESSING_BITS_0_7)
+ {
+ LOG("%s io_w: A9BPageSelect6000 <= %02x\n", machine().describe_context(), data & 0xff);
+ m_a9_psel_6000 = data & 0xff;
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ LOG("%s io_w: A9BPageSelect7000 <= %02x\n", machine().describe_context(), data >> 8);
+ m_a9_psel_7000 = data >> 8;
+ }
+ break;
+
+ case 0x2a: // A9BPageSelect8000
+ if (ACCESSING_BITS_0_7)
+ {
+ //LOG("%s io_w: A9BPageSelect8000 <= %02x\n", machine().describe_context(), data & 0xff);
+ m_a9_psel_8000 = data & 0xff;
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ //LOG("%s io_w: A9BPageSelect9000 <= %02x\n", machine().describe_context(), data >> 8);
+ m_a9_psel_9000 = data >> 8;
+ }
+ break;
+
+ case 0x2c: // A9WControlExtra
+ // b0-b3 A9MKeyCol
+ // b4 A9MBuzzTog
+ // b5 A9MBuzzFromFrc1OrTog
+ // b6 A9MExonDisable
+ // b7 A9MSoundDir
+ // b8 A9MClockEnable1
+ // b9 A9MClockEnable2
+ // b10 A9MClockEnable3
+ // b11 A9MClockEnable4
+ // b12 A9MClockEnable5
+ // b13-b14 A9MClkDiv
+ // b15 A9MSlaveIntEnable
+ LOG("%s io_w: A9WControlExtra <= %04x\n", machine().describe_context(), data);
+ m_a9_control_extra = data;
+
+ // enable keyboard COLs
+ switch (data & 0xf)
+ {
+ case 0x0: m_col_cb(0xff); break;
+ case 0x8: m_col_cb(0x01); break;
+ case 0x9: m_col_cb(0x02); break;
+ case 0xa: m_col_cb(0x04); break;
+ case 0xb: m_col_cb(0x08); break;
+ case 0xc: m_col_cb(0x10); break;
+ case 0xd: m_col_cb(0x20); break;
+ case 0xe: m_col_cb(0x40); break;
+ case 0xf: m_col_cb(0x80); break;
+ default: m_col_cb(0x00); break;
+ }
+
+ if (!BIT(data, 5))
+ {
+ m_buz_cb(BIT(data, 4));
+ }
+ break;
+
+ case 0x2e: // A9WPumpControl
+ LOG("%s io_w: A9WPumpControl <= %04x\n", machine().describe_context(), data);
+ break;
+
+ case 0x80: // A9WRtcLSW
+ //LOG("%s io_w: A9WRtcLSW <= %04x\n", machine().describe_context(), data);
+ m_rtc = (m_rtc & 0xffff0000) | data;
+ break;
+
+ case 0x82: // A9WRtcMSW
+ //LOG("%s io_w: A9WRtcMSW <= %04x\n", machine().describe_context(), data);
+ m_rtc = (m_rtc & 0x0000ffff) | (data << 16);
+ break;
+
+ case 0x84: // A9WNullFrame
+ LOG("%s io_w: A9WNullFrame <= %02x\n", machine().describe_context(), data);
+ transmit_frame(NULL_FRAME);
+ break;
+
+ case 0x8a: // A9BSerialData
+ LOG("%s io_w: A9BSerialData <= %02x\n", machine().describe_context(), data & 0xff);
+ if ((m_a9_serial_control & 0xc0) == 0x80)
+ transmit_frame(DATA_FRAME | (data & 0xff));
+ break;
+
+ case 0x8c: // A9BSerialControl - Serial channel write control register
+ // WriteSingle 10000000b; ReadSingle 11000000b
+ // WriteMulti 10010000b; ReadMulti 11010000b
+ // Reset 00000000b; Select 01000000b
+ // Asic4Id 001h; Asic5PackId 002h
+ // Asic5NormalId 003h; Asic6Id 004h
+ // Asic8Id 005h; Asic2SlaveId 01fh
+ LOG("%s io_w: A2SerialControl <= %02x\n", machine().describe_context(), data & 0xff);
+ m_a9_serial_control = data & 0xff;
+ transmit_frame(CONTROL_FRAME | m_a9_serial_control);
+
+ if ((m_a9_serial_control & 0x40) == 0x40)
+ m_a9_serial_data = receive_frame();
+ break;
+
+ case 0x8e: // A9BChannelSelect - Serial channel select register
+ // b0 Pack1Enable - 1 to select pack 1
+ // b1 Pack2Enable - 1 to select pack 2
+ // b2 Pack3Enable - 1 to select pack 3
+ // b3 Pack4Enable - 1 to select pack 4
+ // b4 Pack5Enable - 1 to select pack 5
+ // b5-b6 SerialClockRate - 0 Medium, 1 Special, 2 Slow, 3 Fast
+ // b7 MultiplexEnable - 1 to loop slave channel to pack channel
+ LOG("%s io_w: A9BChannelSelect <= %02x Channels %c %c %c %c %c\n", machine().describe_context(), data & 0xff,
+ BIT(data, 0) ? '0' : ' ',
+ BIT(data, 1) ? '1' : ' ',
+ BIT(data, 2) ? '2' : ' ',
+ BIT(data, 3) ? '3' : ' ',
+ BIT(data, 4) ? '4' : ' ');
+ m_a9_channel_select = data & 0xff;
+ break;
+
+ default:
+ logerror("%s io_w: Unhandled register %02x <= %04x\n", machine().describe_context(), offset, data);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// SIBO Serial Protocol Controller
+//-------------------------------------------------
+
+bool psion_asic9_device::channel_active(int channel)
+{
+ switch (channel)
+ {
+ case 0: case 1: case 2: case 3: case 4:
+ return BIT(m_a9_channel_select, channel);
+ }
+ return false;
+}
+
+void psion_asic9_device::transmit_frame(uint16_t data)
+{
+ m_busy_timer->adjust(attotime::from_ticks(12, clock() / 2));
+ m_maincpu->set_input_line(NEC_INPUT_LINE_POLL, CLEAR_LINE);
+
+ for (int ch = 0; ch < 8; ch++)
+ {
+ if (channel_active(ch))
+ {
+ LOG("%s Channel %d Transmit %s frame %02x\n", machine().describe_context(), ch, (data & DATA_FRAME) ? "Data" : (data & CONTROL_FRAME) ? "Control" : "Null", data & 0xff);
+ m_data_w[ch](data);
+ }
+ }
+}
+
+uint8_t psion_asic9_device::receive_frame()
+{
+ uint8_t data = 0x00;
+
+ m_busy_timer->adjust(attotime::from_ticks(12, clock() / 2));
+ m_maincpu->set_input_line(NEC_INPUT_LINE_POLL, CLEAR_LINE);
+
+ for (int ch = 0; ch < 8; ch++)
+ {
+ if (channel_active(ch))
+ {
+ data |= m_data_r[ch]();
+ LOG("%s Channel %d Receive Data frame %02x\n", machine().describe_context(), ch, data);
+ }
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// LCD Controller
+//-------------------------------------------------
+
+uint32_t psion_asic9_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ if (m_a9_control & 0x0400) // LCD enable bit
+ {
+ pen_t const *const pens = screen.palette().pens();
+
+ int const width = (BIT(m_a9_lcd_size, 11, 5) + 1) * 32;
+ uint16_t const size = (BIT(m_a9_lcd_size, 0, 11) + 1) * 16;
+
+ for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
+ {
+ for (int x = screen.visible_area().min_x; x <= (screen.visible_area().max_x / 8); x++)
+ {
+ uint8_t const black = m_ram_space->read_byte(0x0400 + y * (width / 8) + x);
+ uint8_t const grey = m_ram_space->read_byte(0x0400 + size + y * (width / 8) + x);
+ uint16_t *p = &bitmap.pix(y, x << 3);
+ for (int i = 0; i < 8; i++)
+ *p++ = BIT(black, i) ? pens[1] : (BIT(grey, i) ? pens[2] : pens[0]);
+ }
+ }
+ }
+ else
+ {
+ bitmap.fill(0, cliprect);
+ }
+ return 0;
+}
diff --git a/src/devices/machine/psion_asic9.h b/src/devices/machine/psion_asic9.h
new file mode 100644
index 00000000000..142ab33c7c9
--- /dev/null
+++ b/src/devices/machine/psion_asic9.h
@@ -0,0 +1,155 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/******************************************************************************
+
+ Psion ASIC9
+
+******************************************************************************/
+
+#ifndef MAME_MACHINE_PSION_ASIC9_H
+#define MAME_MACHINE_PSION_ASIC9_H
+
+#pragma once
+
+#include "machine/ram.h"
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> psion_asic9_device
+
+class psion_asic9_device : public device_t,
+ public device_memory_interface,
+ public device_video_interface
+{
+public:
+ psion_asic9_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ template <typename T, typename U> void set_ram_rom(T &&ram_tag, U &&rom_tag) { m_ram.set_tag(std::forward<T>(ram_tag)); m_rom.set_tag(std::forward<U>(rom_tag)); }
+
+ // callbacks
+ auto buz_cb() { return m_buz_cb.bind(); }
+ auto col_cb() { return m_col_cb.bind(); }
+ auto port_ab_r() { return m_port_ab_r.bind(); }
+ auto port_ab_w() { return m_port_ab_w.bind(); }
+
+ template <unsigned N> auto data_r() { static_assert(N < 8); return m_data_r[N].bind(); }
+ template <unsigned N> auto data_w() { static_assert(N < 8); return m_data_w[N].bind(); }
+
+ uint16_t io_r(offs_t offset, uint16_t mem_mask);
+ void io_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+ uint16_t mem_r(offs_t offset, uint16_t mem_mask);
+ void mem_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+
+ uint8_t col_r();
+
+ IRQ_CALLBACK_MEMBER(inta_cb);
+
+ DECLARE_WRITE_LINE_MEMBER(eint_w);
+ DECLARE_WRITE_LINE_MEMBER(enmi_w);
+ DECLARE_WRITE_LINE_MEMBER(medchng_w);
+
+ uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_resolve_objects() override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual space_config_vector memory_space_config() const override;
+
+private:
+ required_device<cpu_device> m_maincpu;
+ required_device<ram_device> m_ram;
+ required_memory_region m_rom;
+
+ void mem_map(address_map &map);
+ void io_map(address_map &map);
+
+ static constexpr int AS_A9_RAM = AS_OPCODES + 1;
+ static constexpr int AS_A9_ROM = AS_OPCODES + 2;
+
+ const address_space_config m_ram_config;
+ const address_space_config m_rom_config;
+
+ address_space *m_ram_space;
+ address_space *m_rom_space;
+
+ emu_timer *m_tick_timer;
+ emu_timer *m_frc1_timer;
+ emu_timer *m_frc2_timer;
+ emu_timer *m_watchdog_timer;
+ emu_timer *m_rtc_timer;
+
+ TIMER_CALLBACK_MEMBER(tick);
+ TIMER_CALLBACK_MEMBER(frc1);
+ TIMER_CALLBACK_MEMBER(frc2);
+ TIMER_CALLBACK_MEMBER(watchdog);
+ TIMER_CALLBACK_MEMBER(rtc);
+
+ void update_interrupts();
+ bool is_protected(offs_t offset);
+
+ uint8_t m_ram_type;
+ uint32_t ram_device_size(uint8_t device_type);
+ void configure_ram(uint8_t device_type = 0);
+ void configure_rom();
+
+ uint8_t m_post;
+ uint16_t m_a9_control;
+ uint16_t m_a9_status;
+ uint16_t m_a9_lcd_size;
+ uint8_t m_a9_interrupt_status;
+ uint8_t m_a9_interrupt_mask;
+ uint16_t m_frc1_count;
+ uint16_t m_frc1_reload;
+ uint16_t m_frc2_count;
+ uint16_t m_frc2_reload;
+ uint8_t m_watchdog_count;
+ bool m_a9_protection_mode;
+ uint16_t m_a9_protection_upper;
+ uint16_t m_a9_protection_lower;
+ uint8_t m_a9_port_ab_ddr;
+ uint8_t m_a9_port_c_ddr;
+ uint8_t m_a9_port_d_ddr;
+ uint8_t m_a9_psel_6000;
+ uint8_t m_a9_psel_7000;
+ uint8_t m_a9_psel_8000;
+ uint8_t m_a9_psel_9000;
+ uint16_t m_a9_control_extra;
+ uint32_t m_rtc;
+
+ uint8_t m_a9_serial_data;
+ uint8_t m_a9_serial_control;
+ uint8_t m_a9_channel_select;
+
+ devcb_write8 m_configure_ram_cb;
+ devcb_write_line m_buz_cb;
+ devcb_write8 m_col_cb;
+ devcb_read16 m_port_ab_r;
+ devcb_write16 m_port_ab_w;
+
+ devcb_read8::array<8> m_data_r;
+ devcb_write16::array<8> m_data_w;
+
+ emu_timer *m_busy_timer;
+
+ TIMER_CALLBACK_MEMBER(busy);
+
+ bool channel_active(int channel);
+ void transmit_frame(uint16_t data);
+ uint8_t receive_frame();
+
+ static constexpr uint16_t NULL_FRAME = 0x000;
+ static constexpr uint16_t CONTROL_FRAME = 0x100;
+ static constexpr uint16_t DATA_FRAME = 0x200;
+};
+
+
+// device type definition
+DECLARE_DEVICE_TYPE(PSION_ASIC9, psion_asic9_device)
+
+#endif // MAME_MACHINE_PSION_ASIC9_H
diff --git a/src/emu/xtal.cpp b/src/emu/xtal.cpp
index 8367a55cfb4..18190ddda56 100644
--- a/src/emu/xtal.cpp
+++ b/src/emu/xtal.cpp
@@ -144,6 +144,7 @@ const double XTAL::known_xtals[] = {
7'159'090, /* 7.15909_MHz_XTAL Blood Bros (2x NTSC subcarrier) */
7'200'000, /* 7.2_MHz_XTAL Novag Constellation (later models, with /2 divider), Kawai K1 keyscan IC */
7'372'800, /* 7.3728_MHz_XTAL - */
+ 7'680'000, /* 7.68_MHz_XTAL Psion Series 3 */
7'864'300, /* 7.8643_MHz_XTAL Used on InterFlip games as video clock */
7'987'000, /* 7.987_MHz_XTAL PC9801-86 YM2608 clock */
7'995'500, /* 7.9955_MHz_XTAL Used on Electronic Devices Italy Galaxy Gunners sound board */
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 09fe053dcd7..0f2e5e5b354 100755
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -37602,9 +37602,24 @@ psionp350 //
psionp464 //
psionxp //
+@source:psion/psion3a.cpp
+pocketbk2 // 1994 Acorn Pocket Book II
+psion3a // 1993 Psion Series 3a
+psion3a2 // 1995 Psion Series 3a
+psion3a2_de // 1995 Psion Series 3a (German)
+psion3c // 1996 Psion Series 3c
+psion3mx // 1998 Psion Series 3mx
+
@source:psion/psion5.cpp
psion5mx //
+@source:psion/siena.cpp
+siena // 1996 Psion Siena
+
+@source:psion/workabout.cpp
+psionwa // 1995 Psion Workabout
+psionwamx // 1998 Psion Workabout mx
+
@source:quantel/dpb7000.cpp
dpb7000 //
diff --git a/src/mame/psion/psion3a.cpp b/src/mame/psion/psion3a.cpp
new file mode 100644
index 00000000000..71ad5126654
--- /dev/null
+++ b/src/mame/psion/psion3a.cpp
@@ -0,0 +1,396 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/******************************************************************************
+
+ Psion Series 3a/3c/3mx
+
+ TODO:
+ - battery backed RAM
+ - sound devices
+ - serial ports
+ - fix RAM detection for 3mx
+
+******************************************************************************/
+
+#include "emu.h"
+#include "machine/psion_asic9.h"
+#include "machine/ram.h"
+#include "sound/spkrdev.h"
+//#include "bus/psion/exp/slot.h"
+#include "psion_ssd.h"
+
+#include "emupal.h"
+#include "screen.h"
+#include "softlist_dev.h"
+#include "speaker.h"
+#include "utf8.h"
+
+
+namespace {
+
+class psion3a_state : public driver_device
+{
+public:
+ psion3a_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_asic9(*this, "asic9")
+ , m_ram(*this, "ram")
+ , m_palette(*this, "palette")
+ , m_keyboard(*this, "COL%u", 0U)
+ , m_speaker(*this, "speaker")
+ , m_ssd(*this, "ssd%u", 1U)
+ //, m_exp(*this, "exp")
+ { }
+
+ void psion3a(machine_config &config);
+ void psion3a2(machine_config &config);
+ void psion3c(machine_config &config);
+ void psion3mx(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+private:
+ required_device<psion_asic9_device> m_asic9;
+ required_device<ram_device> m_ram;
+ required_device<palette_device> m_palette;
+ required_ioport_array<8> m_keyboard;
+ required_device<speaker_sound_device> m_speaker;
+ required_device_array<psion_ssd_device, 2> m_ssd;
+ //required_device<psion_exp_slot_device> m_exp;
+
+ void palette_init(palette_device &palette);
+
+ uint16_t kbd_r();
+
+ uint8_t m_key_col = 0;
+};
+
+
+void psion3a_state::machine_start()
+{
+}
+
+void psion3a_state::machine_reset()
+{
+}
+
+
+static INPUT_PORTS_START( psion3a )
+ PORT_START("COL0")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME(UTF8_RIGHT" End")
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(0x09) PORT_NAME("Tab")
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(UTF8_LEFT" Home")
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME(UTF8_DOWN" Pg Dn")
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("Psion")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("Sheet")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("Time")
+ PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("Data")
+
+ PORT_START("COL1")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR(';')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('+') PORT_CHAR('=')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') PORT_CHAR(']')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR('*') PORT_CHAR(':')
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (L)")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("Calc")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("Agenda")
+ PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("System")
+
+ PORT_START("COL2")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08) PORT_NAME("Delete")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('?') PORT_CHAR('}')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PORT_CHAR('[')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Control")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("World")
+ PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("Word")
+
+ PORT_START("COL3")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("Help Dial")
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_CHAR('{')
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (R)")
+
+ PORT_START("COL4")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR('~')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('\'')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME("Caps")
+
+ PORT_START("COL5")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(0xa3) PORT_CHAR('\\')
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("Menu")
+
+ PORT_START("COL6")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
+
+ PORT_START("COL7")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_NAME("1 ! Off")
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') PORT_CHAR('#')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PORT_CHAR('@')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME(UTF8_UP" Pg Up")
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Esc On")
+INPUT_PORTS_END
+
+
+static INPUT_PORTS_START( psion3a_de )
+ PORT_INCLUDE(psion3a)
+
+ PORT_MODIFY("COL0")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME(UTF8_RIGHT" Ende")
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('z') PORT_CHAR('Z')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(UTF8_LEFT" Pos1")
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME(UTF8_DOWN" Bild Dn")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("Tabelle")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("Uhr")
+ PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("Daten")
+
+ PORT_MODIFY("COL1")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR(0xdf)
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('+') PORT_CHAR('<')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=') PORT_CHAR('}')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR('*') PORT_CHAR('>')
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("Rechner")
+
+ PORT_MODIFY("COL2")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08) PORT_NAME("Entf")
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') PORT_CHAR('[')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')') PORT_CHAR(']')
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Strg")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("Welt")
+
+ PORT_MODIFY("COL3")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("Hilfe Wahl")
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('?') PORT_CHAR('{')
+
+ PORT_MODIFY("COL4")
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR('@')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('\'')
+
+ PORT_MODIFY("COL5")
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(0xa7) PORT_CHAR('\\')
+
+ PORT_MODIFY("COL6")
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('y') PORT_CHAR('Y')
+
+ PORT_MODIFY("COL7")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_NAME("1 ! Aus")
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&') PORT_CHAR(0xba)
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME(UTF8_UP" Bild Up")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Esc Ein")
+INPUT_PORTS_END
+
+
+static INPUT_PORTS_START( psion3c )
+ PORT_INCLUDE(psion3a)
+
+ PORT_MODIFY("COL2")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_NAME("Jotter")
+INPUT_PORTS_END
+
+
+//static INPUT_PORTS_START( psion3c_de )
+// PORT_INCLUDE(psion3a_de)
+//
+// PORT_MODIFY("COL2")
+// PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_NAME("Notiz")
+//INPUT_PORTS_END
+
+
+static INPUT_PORTS_START( pocketbk2 )
+ PORT_INCLUDE(psion3a)
+
+ PORT_MODIFY("COL0")
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("Acorn")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("Abacus")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("Time")
+ PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("Cards")
+
+ PORT_MODIFY("COL1")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("Calc")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("Schedule")
+ PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("Desktop")
+
+ PORT_MODIFY("COL2")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("World")
+ PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("Write")
+INPUT_PORTS_END
+
+
+uint16_t psion3a_state::kbd_r()
+{
+ uint16_t data = 0x00;
+
+ for (int i = 0; i < 8; i++)
+ {
+ if (BIT(m_key_col, i))
+ data |= m_keyboard[i]->read();
+ }
+
+ return data;
+}
+
+
+void psion3a_state::palette_init(palette_device &palette)
+{
+ palette.set_pen_color(0, rgb_t(190, 220, 190));
+ palette.set_pen_color(1, rgb_t(130, 130, 110));
+ palette.set_pen_color(2, rgb_t(190, 210, 180));
+}
+
+
+void psion3a_state::psion3a(machine_config &config)
+{
+ PSION_ASIC9(config, m_asic9, 7.68_MHz_XTAL); // V30H
+ m_asic9->set_ram_rom("ram", "rom");
+ m_asic9->port_ab_r().set(FUNC(psion3a_state::kbd_r));
+ m_asic9->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
+ //m_asic9->buzvol_cb().set([this](int state) { m_speaker->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.25); });
+ m_asic9->col_cb().set([this](uint8_t data) { m_key_col = data; });
+ m_asic9->data_r<0>().set(m_ssd[0], FUNC(psion_ssd_device::data_r)); // SSD Pack 1
+ m_asic9->data_w<0>().set(m_ssd[0], FUNC(psion_ssd_device::data_w));
+ m_asic9->data_r<1>().set(m_ssd[1], FUNC(psion_ssd_device::data_r)); // SSD Pack 2
+ m_asic9->data_w<1>().set(m_ssd[1], FUNC(psion_ssd_device::data_w));
+ //m_asic9->data_r<4>().set(m_exp, FUNC(psion_exp_slot_device::data_r)); // Expansion port
+ //m_asic9->data_w<4>().set(m_exp, FUNC(psion_exp_slot_device::data_w));
+
+ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
+ screen.set_size(480, 160);
+ screen.set_visarea_full();
+ screen.set_refresh_hz(66);
+ screen.set_screen_update(m_asic9, FUNC(psion_asic9_device::screen_update));
+ screen.set_palette(m_palette);
+ PALETTE(config, "palette", FUNC(psion3a_state::palette_init), 3);
+
+ // TODO: M7542
+
+ SPEAKER(config, "mono").front_center();
+ SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00); // Piezo buzzer
+
+ RAM(config, m_ram).set_default_size("512K").set_extra_options("256K");
+
+ PSION_SSD(config, m_ssd[0]);
+ m_ssd[0]->door_cb().set(m_asic9, FUNC(psion_asic9_device::medchng_w));
+ PSION_SSD(config, m_ssd[1]);
+ m_ssd[1]->door_cb().set(m_asic9, FUNC(psion_asic9_device::medchng_w));
+
+ //PSION_EXP_SLOT(config, m_exp, psion_exp_devices, nullptr);
+
+ SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("S3,S3A");
+}
+
+void psion3a_state::psion3a2(machine_config &config)
+{
+ psion3a(config);
+
+ m_ram->set_default_size("2M").set_extra_options("1M");
+}
+
+void psion3a_state::psion3c(machine_config &config)
+{
+ psion3a(config);
+
+ m_ram->set_default_size("2M").set_extra_options("1M");
+
+ // TODO: M7702
+
+ // TODO: add Honda port
+}
+
+void psion3a_state::psion3mx(machine_config &config)
+{
+ psion3a(config);
+
+ m_asic9->set_clock(3.6864_MHz_XTAL * 15 / 2); // V30MX
+
+ m_ram->set_default_size("2M").set_extra_options("");
+
+ // TODO: M7702
+
+ // TODO: add Honda port
+}
+
+
+ROM_START(psion3a)
+ ROM_REGION16_LE(0x100000, "rom", 0)
+ ROM_SYSTEM_BIOS(0, "322f", "V3.22F/ENG")
+ ROMX_LOAD("s3a_v3.22f_eng.bin", 0x000000, 0x100000, CRC(fafa3820) SHA1(c1a320b43280cfdb74fc1cb1363fca88dd187487), ROM_BIOS(0))
+ROM_END
+
+ROM_START(psion3a2)
+ ROM_REGION16_LE(0x200000, "rom", 0)
+ ROM_SYSTEM_BIOS(0, "340f", "V3.40F/ENG")
+ ROMX_LOAD("s3a_v3.40f_eng.bin", 0x000000, 0x200000, CRC(f0adf12c) SHA1(3eb4e7f1fc5611a4d6e65d27d336969ebae94395), ROM_BIOS(0))
+ROM_END
+
+ROM_START(psion3a2_de)
+ ROM_REGION16_LE(0x200000, "rom", 0)
+ ROM_SYSTEM_BIOS(0, "341f", "V3.41F/DEU")
+ ROMX_LOAD("s3a_v3.41f_eng.bin", 0x000000, 0x200000, CRC(1f21cb0a) SHA1(fbb9c3356cf8b1d89b8cf50fc12175568c74ce3e), ROM_BIOS(0))
+ROM_END
+
+ROM_START(psion3c)
+ ROM_REGION16_LE(0x200000, "rom", 0)
+ ROM_SYSTEM_BIOS(0, "520f", "V5.20F/ENG")
+ ROMX_LOAD("oak_v5.20f_eng.bin", 0x000000, 0x200000, CRC(d8e672ca) SHA1(23e7570ddbecbfd50953ce6a6b7ead7128814402), ROM_BIOS(0))
+ROM_END
+
+ROM_START(psion3mx)
+ ROM_REGION16_LE(0x200000, "rom", 0)
+ ROM_SYSTEM_BIOS(0, "616f", "V6.16F/ENG")
+ ROMX_LOAD("maple_v6.16f_uk.bin", 0x000000, 0x200000, CRC(10011d9d) SHA1(8c657414513ed57ccf6beddc65dca1fe5ab600fb), ROM_BIOS(0))
+ROM_END
+
+ROM_START(pocketbk2)
+ ROM_REGION16_LE(0x200000, "rom", 0)
+ ROM_SYSTEM_BIOS(0, "130f", "V1.30F/ACN")
+ ROMX_LOAD("pb2_v1.30f_acn.bin", 0x000000, 0x200000, CRC(d7ba3a50) SHA1(2d29a7ac5ec4d6bf63bd7d93cc3763e0f9763136), ROM_BIOS(0))
+ROM_END
+
+} // anonymous namespace
+
+
+// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
+COMP( 1993, psion3a, 0, 0, psion3a, psion3a, psion3a_state, empty_init, "Psion", "Series 3a", MACHINE_NOT_WORKING )
+COMP( 1994, pocketbk2, psion3a, 0, psion3a, pocketbk2, psion3a_state, empty_init, "Acorn Computers", "Pocket Book II", MACHINE_NOT_WORKING )
+COMP( 1995, psion3a2, psion3a, 0, psion3a2, psion3a, psion3a_state, empty_init, "Psion", "Series 3a (2M)", MACHINE_NOT_WORKING )
+COMP( 1995, psion3a2_de, psion3a, 0, psion3a2, psion3a_de, psion3a_state, empty_init, "Psion", "Series 3a (2M) (German)", MACHINE_NOT_WORKING )
+COMP( 1996, psion3c, 0, 0, psion3c, psion3c, psion3a_state, empty_init, "Psion", "Series 3c", MACHINE_NOT_WORKING )
+COMP( 1998, psion3mx, 0, 0, psion3mx, psion3c, psion3a_state, empty_init, "Psion", "Series 3mx", MACHINE_NOT_WORKING )
diff --git a/src/mame/psion/psion_ssd.cpp b/src/mame/psion/psion_ssd.cpp
new file mode 100644
index 00000000000..ab59d210f5b
--- /dev/null
+++ b/src/mame/psion/psion_ssd.cpp
@@ -0,0 +1,284 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/****************************************************************************
+
+ Psion Solid State Disk emulation
+
+
+ Memory Type No. devices Memory Size
+ D7 D6 D5 D4 D3 D2 D1 D0
+ 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
+ 0 1 1 Type 3 Flash 1 1 4 devices 0 1 1 128K
+ 1 0 0 Type 4 Flash 1 0 0 256K
+ 1 0 1 Type 5 Flash 1 0 1 512K
+ 1 1 0 Read only SSD (ROM) 1 1 0 1M
+ 1 1 1 Hardware Write protected SSD 1 1 1 2M
+
+ Info Byte (* = confirmed)
+ Psion Solid State Disk 64K Flash 001 01 001 0x29
+ Psion Solid State Disk 128K Flash * 001 00 011 0x23
+ - MC200 System Disk * 111 00 011 0xE3
+ - MC400 System Disk * 111 00 011 0xE3
+ Psion Solid State Disk 256K Flash * 001 01 011 0x2B
+ - MC Word System Disk * 111 01 011 0xEB
+ Psion Solid State Disk 512K Flash 001 01 100 0x2C
+ Psion Solid State Disk 1MB Flash * 001 11 100 0x3C
+ Psion Solid State Disk 2MB Flash * 001 11 101 0x3D
+ Psion Solid State Disk 4MB Flash 001 11 110 0x3E
+ Psion Solid State Disk 8MB Flash * 001 11 111 0x3F
+
+ Psion Solid State Disk 64K RAM 000 01 001 0x09
+ Psion Solid State Disk 128K RAM 000 11 001 0x19
+ Psion Solid State Disk 512K RAM 000 11 011 0x1B
+ Psion Solid State Disk 1MB RAM 000 11 100 0x1C
+ Psion Solid State Disk 2MB RAM 000 11 101 0x1D
+
+****************************************************************************/
+
+#include "emu.h"
+#include "psion_ssd.h"
+
+#include "softlist_dev.h"
+
+
+DEFINE_DEVICE_TYPE(PSION_SSD, psion_ssd_device, "psion_ssd", "Psion Solid State Disk")
+
+
+//-------------------------------------------------
+// device_add_mconfig - add device configuration
+//-------------------------------------------------
+
+void psion_ssd_device::device_add_mconfig(machine_config &config)
+{
+ PSION_ASIC5_PACK(config, m_asic5);
+ m_asic5->readpa_handler().set([this]() { return m_ssd_data[latched_addr()]; });
+ m_asic5->writepa_handler().set([this](uint8_t data) { m_ssd_data[latched_addr()] = data; });
+ m_asic5->writepb_handler().set([this](uint8_t data) { m_addr_latch = (m_addr_latch & 0xffff00) | (data << 0); });
+ m_asic5->writepd_handler().set([this](uint8_t data) { m_addr_latch = (m_addr_latch & 0xff00ff) | (data << 8); });
+ m_asic5->writepc_handler().set([this](uint8_t data) { m_addr_latch = (m_addr_latch & 0x00ffff) | (data << 16); });
+}
+
+
+//-------------------------------------------------
+// psion_ssd_device - constructor
+//-------------------------------------------------
+
+psion_ssd_device::psion_ssd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, PSION_SSD, tag, owner, clock)
+ , device_memcard_image_interface(mconfig, *this)
+ , m_region(*this, DEVICE_SELF)
+ , m_asic5(*this, "asic5")
+ , m_door_cb(*this)
+ , m_door_timer(nullptr)
+ , m_info_byte(0)
+ , m_addr_latch(0)
+ , m_mem_width(0)
+{
+}
+
+
+//-------------------------------------------------
+// psion_ssd_device - destructor
+//-------------------------------------------------
+
+psion_ssd_device::~psion_ssd_device()
+{
+}
+
+
+//-------------------------------------------------
+// device_resolve_objects - resolve objects that
+// may be needed for other devices to set
+// initial conditions at start time
+//-------------------------------------------------
+
+void psion_ssd_device::device_resolve_objects()
+{
+ m_door_cb.resolve_safe();
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void psion_ssd_device::device_start()
+{
+ m_ssd_data = make_unique_clear<uint8_t[]>(0x800000);
+
+ // insert default System disk
+ if (m_region.found())
+ {
+ uint32_t size = m_region->bytes();
+ memcpy(&m_ssd_data[0], m_region->base(), size);
+ set_info_byte(size, SSD_FLASH1);
+ }
+
+ m_door_timer = timer_alloc(FUNC(psion_ssd_device::close_door), this);
+ m_door_timer->reset();
+
+ save_pointer(NAME(m_ssd_data), 0x800000);
+ save_item(NAME(m_info_byte));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void psion_ssd_device::device_reset()
+{
+ m_addr_latch = 0x00;
+
+ // open the door
+ m_door_cb(ASSERT_LINE);
+
+ // setup the timer to close the door
+ m_door_timer->adjust(attotime::from_msec(200));
+}
+
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void psion_ssd_device::device_config_complete()
+{
+ add_format("ssd", "Psion Solid State Disk image", "bin,rom", "");
+}
+
+
+//-------------------------------------------------
+// get_software_list_loader -
+//-------------------------------------------------
+
+const software_list_loader &psion_ssd_device::get_software_list_loader() const
+{
+ return image_software_list_loader::instance();
+}
+
+
+
+TIMER_CALLBACK_MEMBER(psion_ssd_device::close_door)
+{
+ // close the door
+ m_door_cb(CLEAR_LINE);
+}
+
+
+image_init_result psion_ssd_device::call_load()
+{
+ device_image_interface *image = nullptr;
+ interface(image);
+
+ uint32_t size = image->length();
+
+ if (size < 0x10000 || size > 0x800000 || (size & (size - 1)) != 0)
+ {
+ image->seterror(image_error::INVALIDIMAGE, "Invalid size, must be 64K, 128K, 256K, 512K, 1M, 2M, 4M, 8M");
+ return image_init_result::FAIL;
+ }
+
+ image->fread(m_ssd_data.get(), size);
+
+ // check for Flash header
+ if ((m_ssd_data[0] | (m_ssd_data[1] << 8)) == 0xf1a5) // Flash
+ set_info_byte(size, SSD_FLASH1);
+ else
+ set_info_byte(size, SSD_RAM);
+
+ // open the door
+ m_door_cb(ASSERT_LINE);
+
+ // setup the timer to close the door
+ m_door_timer->adjust(attotime::from_msec(200));
+
+ return image_init_result::PASS;
+}
+
+
+void psion_ssd_device::call_unload()
+{
+ memset(m_ssd_data.get(), 0, sizeof(m_ssd_data));
+ set_info_byte(0);
+}
+
+
+uint32_t psion_ssd_device::latched_addr()
+{
+ return (m_addr_latch & make_bitmask<uint32_t>(m_mem_width)) | (BIT(m_addr_latch, 22, 2) << m_mem_width);
+}
+
+
+void psion_ssd_device::set_info_byte(uint32_t size, uint8_t type)
+{
+ m_info_byte = 0x00;
+
+ // Type 1 Flash or RAM
+ if (type != SSD_RAM)
+ m_info_byte |= 0x20;
+
+ // No. devices and size
+ switch (size)
+ {
+ case 0x010000: // 64K (2 x 32K)
+ m_info_byte = 0x09;
+ m_mem_width = 15;
+ break;
+ case 0x020000: // 128K (1 x 128K)
+ m_info_byte = 0x03;
+ m_mem_width = 17;
+ break;
+ case 0x040000: // 256K (2 x 128K)
+ m_info_byte = 0x0b;
+ m_mem_width = 17;
+ break;
+ case 0x080000: // 512K (2 x 256K)
+ m_info_byte = 0x0c;
+ m_mem_width = 18;
+ break;
+ case 0x100000: // 1M (4 x 256K)
+ m_info_byte = 0x1c;
+ m_mem_width = 18;
+ break;
+ case 0x200000: // 2M (4 x 512K)
+ m_info_byte = 0x1d;
+ m_mem_width = 19;
+ break;
+ case 0x400000:// 4M (4 x 1M)
+ m_info_byte = 0x1e;
+ m_mem_width = 20;
+ break;
+ case 0x800000:// 8M (4 x 2M)
+ m_info_byte = 0x1f;
+ m_mem_width = 21;
+ break;
+ }
+
+ // write protected
+ m_info_byte |= 0xe0;
+
+ // set pull-ups on ASIC5
+ m_asic5->set_info_byte(m_info_byte);
+}
+
+
+uint8_t psion_ssd_device::data_r()
+{
+ if (m_info_byte)
+ {
+ return m_asic5->data_r();
+ }
+ return 0x00;
+}
+
+void psion_ssd_device::data_w(uint16_t data)
+{
+ if (m_info_byte)
+ {
+ m_asic5->data_w(data);
+ }
+}
diff --git a/src/mame/psion/psion_ssd.h b/src/mame/psion/psion_ssd.h
new file mode 100644
index 00000000000..c7df0b8f033
--- /dev/null
+++ b/src/mame/psion/psion_ssd.h
@@ -0,0 +1,84 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/****************************************************************************
+
+ Psion Solid State Disk emulation
+
+****************************************************************************/
+
+#ifndef MAME_PSION_PSION_SSD_H
+#define MAME_PSION_PSION_SSD_H
+
+#include "machine/psion_asic5.h"
+#include "imagedev/memcard.h"
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+// ======================> psion_ssd_device
+
+class psion_ssd_device : public device_t, public device_memcard_image_interface
+{
+public:
+ // construction/destruction
+ psion_ssd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+ virtual ~psion_ssd_device();
+
+ // callbacks
+ auto door_cb() { return m_door_cb.bind(); }
+
+ uint8_t data_r();
+ void data_w(uint16_t data);
+
+protected:
+ // device-level overrides
+ virtual void device_resolve_objects() override;
+ virtual void device_config_complete() override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+ // image-level overrides
+ virtual image_init_result call_load() override;
+ virtual void call_unload() override;
+
+ virtual bool is_reset_on_load() const noexcept override { return false; }
+ virtual const char *image_interface() const noexcept override { return "psion_ssd"; }
+ virtual const char *file_extensions() const noexcept override { return "rom,bin"; }
+ virtual const char *image_type_name() const noexcept override { return "ssd"; }
+ virtual const char *image_brief_type_name() const noexcept override { return "ssd"; }
+
+ // device_image_interface implementation
+ virtual const software_list_loader &get_software_list_loader() const override;
+
+private:
+ optional_memory_region m_region;
+ required_device<psion_asic5_device> m_asic5;
+
+ inline uint32_t mem_addr() const noexcept { return (m_addr_latch & make_bitmask<uint32_t>(m_mem_width)) | (BIT(m_addr_latch, 22, 2) << m_mem_width); }
+
+ TIMER_CALLBACK_MEMBER(close_door);
+
+ devcb_write_line m_door_cb;
+ emu_timer *m_door_timer;
+
+ std::unique_ptr<uint8_t[]> m_ssd_data;
+
+ void set_info_byte(uint32_t size, uint8_t type = 0);
+ uint32_t latched_addr();
+
+ uint8_t m_info_byte;
+ uint32_t m_addr_latch;
+ int m_mem_width;
+
+ static constexpr uint8_t SSD_RAM = 0;
+ static constexpr uint8_t SSD_FLASH1 = 1;
+};
+
+
+// device type definition
+DECLARE_DEVICE_TYPE(PSION_SSD, psion_ssd_device)
+
+#endif // MAME_PSION_PSION_SSD_H
diff --git a/src/mame/psion/siena.cpp b/src/mame/psion/siena.cpp
new file mode 100644
index 00000000000..c8631fd43cb
--- /dev/null
+++ b/src/mame/psion/siena.cpp
@@ -0,0 +1,226 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/******************************************************************************
+
+ Psion Siena
+
+ TODO:
+ - battery backed RAM
+
+******************************************************************************/
+
+#include "emu.h"
+#include "machine/psion_asic9.h"
+#include "machine/ram.h"
+#include "sound/spkrdev.h"
+
+#include "emupal.h"
+#include "screen.h"
+#include "softlist_dev.h"
+#include "speaker.h"
+#include "utf8.h"
+
+
+namespace {
+
+class siena_state : public driver_device
+{
+public:
+ siena_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_asic9(*this, "asic9")
+ , m_ram(*this, "ram")
+ , m_palette(*this, "palette")
+ , m_keyboard(*this, "COL%u", 0U)
+ , m_speaker(*this, "speaker")
+ { }
+
+ void siena(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+private:
+ required_device<psion_asic9_device> m_asic9;
+ required_device<ram_device> m_ram;
+ required_device<palette_device> m_palette;
+ required_ioport_array<8> m_keyboard;
+ required_device<speaker_sound_device> m_speaker;
+
+ void palette_init(palette_device &palette);
+
+ uint16_t kbd_r();
+
+ uint8_t m_key_col = 0;
+};
+
+
+void siena_state::machine_start()
+{
+}
+
+void siena_state::machine_reset()
+{
+}
+
+
+static INPUT_PORTS_START( siena )
+ PORT_START("COL0")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3') PORT_NAME("3 MC")
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2') PORT_NAME("2 MR")
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1') PORT_NAME("1 Min")
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("On/CE")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_UNKNOWN)
+
+ PORT_START("COL1")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PORT_CHAR('"')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') PORT_CHAR('/')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR('(')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("Fn")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) PORT_NAME("IR Send")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("Word")
+
+ PORT_START("COL2")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PORT_CHAR(0xa3)
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PORT_CHAR('~')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("Help")
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR(')')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT)) PORT_NAME("Psion")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("IR Receive")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("Agenda")
+
+ PORT_START("COL3")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('$')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PORT_CHAR('@')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(UTF8_LEFT" Home")
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_NAME("= %")
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (L)")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Off")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("Time")
+
+ PORT_START("COL4")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR('%')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR('{')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME(UTF8_DOWN" Pg Dn")
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME(UTF8_UP" Pg Up")
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter")
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+') PORT_NAME("+ M+")
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (R)")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("World")
+
+ PORT_START("COL5")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PORT_CHAR('^')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR('}')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME(UTF8_RIGHT" End")
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08) PORT_NAME("Del")
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR('*') PORT_NAME(u8"×")
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNKNOWN)
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("Sheet")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("Calc")
+
+ PORT_START("COL6")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PORT_CHAR('!')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PORT_CHAR('\\')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME("Caps")
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR(']')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PORT_CHAR('*')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR('/') PORT_NAME(u8"÷")
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('-') PORT_NAME("- M-")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Esc")
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("Data")
+
+ PORT_START("COL7")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(0x09) PORT_NAME("Tab")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PORT_CHAR('#')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("Menu Info")
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR('[')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PORT_CHAR('&')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR('.') PORT_NAME(". +/-")
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Control")
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("System")
+INPUT_PORTS_END
+
+
+uint16_t siena_state::kbd_r()
+{
+ uint16_t data = 0x00;
+
+ for (int i = 0; i < 8; i++)
+ {
+ if (BIT(m_key_col, i))
+ data |= m_keyboard[i]->read();
+ }
+
+ return data;
+}
+
+
+void siena_state::palette_init(palette_device &palette)
+{
+ palette.set_pen_color(0, rgb_t(190, 220, 190));
+ palette.set_pen_color(1, rgb_t(130, 130, 110));
+ palette.set_pen_color(2, rgb_t(190, 210, 180));
+}
+
+
+void siena_state::siena(machine_config &config)
+{
+ PSION_ASIC9(config, m_asic9, 7.68_MHz_XTAL);
+ m_asic9->set_ram_rom("ram", "rom");
+ m_asic9->port_ab_r().set(FUNC(siena_state::kbd_r));
+ m_asic9->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
+ m_asic9->col_cb().set([this](uint8_t data) { m_key_col = data; });
+
+ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
+ screen.set_size(240, 160);
+ screen.set_visarea_full();
+ screen.set_refresh_hz(66);
+ screen.set_screen_update(m_asic9, FUNC(psion_asic9_device::screen_update));
+ screen.set_palette(m_palette);
+ PALETTE(config, "palette", FUNC(siena_state::palette_init), 3);
+
+ SPEAKER(config, "mono").front_center();
+ SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00); // Piezo buzzer
+
+ RAM(config, m_ram).set_default_size("512K").set_extra_options("1M");
+
+ // TODO: add Honda port
+
+ SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("WA");
+}
+
+
+ROM_START(siena)
+ ROM_REGION16_LE(0x100000, "rom", 0)
+ ROM_SYSTEM_BIOS(0, "420f", "V4.20F")
+ ROMX_LOAD("vine_v4.20f.bin", 0x00000, 0x100000, CRC(641f8e7c) SHA1(fe0e46540e0aac5aabb2dd1b96689da41e8f55fb), ROM_BIOS(0))
+ROM_END
+
+} // anonymous namespace
+
+
+// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
+COMP( 1996, siena, 0, 0, siena, siena, siena_state, empty_init, "Psion", "Siena", MACHINE_NOT_WORKING )
diff --git a/src/mame/psion/workabout.cpp b/src/mame/psion/workabout.cpp
new file mode 100644
index 00000000000..685697e7d36
--- /dev/null
+++ b/src/mame/psion/workabout.cpp
@@ -0,0 +1,265 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/******************************************************************************
+
+ Psion Workabout
+
+ TODO:
+ - battery backed RAM
+
+******************************************************************************/
+
+#include "emu.h"
+#include "machine/psion_asic9.h"
+#include "machine/ram.h"
+#include "sound/spkrdev.h"
+//#include "bus/psion/exp/slot.h"
+#include "psion_ssd.h"
+
+#include "emupal.h"
+#include "screen.h"
+#include "softlist_dev.h"
+#include "speaker.h"
+#include "utf8.h"
+
+
+namespace {
+
+class workabout_state : public driver_device
+{
+public:
+ workabout_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_asic9(*this, "asic9")
+ , m_ram(*this, "ram")
+ , m_palette(*this, "palette")
+ , m_keyboard(*this, "COL%u", 0U)
+ , m_speaker(*this, "speaker")
+ , m_ssd(*this, "ssd%u", 1U)
+ //, m_exp(*this, "exp%u", 1U)
+ { }
+
+ void workabout(machine_config &config);
+ void psionwa(machine_config &config);
+ void psionwamx(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+private:
+ required_device<psion_asic9_device> m_asic9;
+ required_device<ram_device> m_ram;
+ required_device<palette_device> m_palette;
+ required_ioport_array<8> m_keyboard;
+ required_device<speaker_sound_device> m_speaker;
+ required_device_array<psion_ssd_device, 2> m_ssd;
+ //required_device_array<psion_exp_slot_device, 3> m_exp;
+
+ void palette_init(palette_device &palette);
+
+ uint16_t kbd_r();
+
+ uint8_t m_key_col = 0;
+};
+
+
+void workabout_state::machine_start()
+{
+}
+
+void workabout_state::machine_reset()
+{
+}
+
+
+static INPUT_PORTS_START( workabout )
+ PORT_START("COL0")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PORT_CHAR('}')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME(UTF8_DOWN)
+ PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_UNUSED)
+ PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("On/Esc")
+
+ PORT_START("COL1")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter")
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME(UTF8_RIGHT)
+ PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("COL2")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_NAME("1 ! @")
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_CHAR('\'')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08) PORT_NAME("Del")
+ PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("COL3")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("Psion")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') PORT_CHAR('#')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') PORT_CHAR('[')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(0x09) PORT_NAME("Tab")
+ PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("COL4")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(0xa3) PORT_CHAR('\\')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')') PORT_CHAR(']')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME(UTF8_UP)
+ PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("COL5")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('+') PORT_CHAR('=')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR('*') PORT_CHAR(':')
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("Menu")
+ PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("COL6")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Control")
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(';') PORT_CHAR('<')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR('~')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD)
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Contrast")
+ PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("COL7")
+ PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
+ PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
+ PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
+ PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(',')
+ PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('{')
+ PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(UTF8_LEFT)
+ PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backlight")
+ PORT_BIT(0x180, IP_ACTIVE_HIGH, IPT_UNUSED)
+INPUT_PORTS_END
+
+
+uint16_t workabout_state::kbd_r()
+{
+ uint16_t data = 0x00;
+
+ for (int i = 0; i < 8; i++)
+ {
+ if (BIT(m_key_col, i))
+ data |= m_keyboard[i]->read();
+ }
+
+ return data;
+}
+
+
+void workabout_state::palette_init(palette_device &palette)
+{
+ palette.set_pen_color(0, rgb_t(190, 220, 190));
+ palette.set_pen_color(1, rgb_t(130, 130, 110));
+ palette.set_pen_color(2, rgb_t(190, 210, 180));
+}
+
+
+void workabout_state::workabout(machine_config &config)
+{
+ PSION_ASIC9(config, m_asic9, 7.68_MHz_XTAL); // V30H
+ m_asic9->set_ram_rom("ram", "rom");
+ m_asic9->port_ab_r().set(FUNC(workabout_state::kbd_r));
+ m_asic9->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w));
+ m_asic9->col_cb().set([this](uint8_t data) { m_key_col = data; });
+ m_asic9->data_r<0>().set(m_ssd[0], FUNC(psion_ssd_device::data_r)); // SSD Pack 1
+ m_asic9->data_w<0>().set(m_ssd[0], FUNC(psion_ssd_device::data_w));
+ m_asic9->data_r<1>().set(m_ssd[1], FUNC(psion_ssd_device::data_r)); // SSD Pack 2
+ m_asic9->data_w<1>().set(m_ssd[1], FUNC(psion_ssd_device::data_w));
+ //m_asic9->data_r<2>().set(m_exp[0], FUNC(psion_exp_slot_device::data_r)); // Expansion port A
+ //m_asic9->data_w<2>().set(m_exp[0], FUNC(psion_exp_slot_device::data_w));
+ //m_asic9->data_r<3>().set(m_exp[1], FUNC(psion_exp_slot_device::data_r)); // Expansion port B
+ //m_asic9->data_w<3>().set(m_exp[1], FUNC(psion_exp_slot_device::data_w));
+ //m_asic9->data_r<4>().set(m_exp[2], FUNC(psion_exp_slot_device::data_r)); // Expansion port C
+ //m_asic9->data_w<4>().set(m_exp[2], FUNC(psion_exp_slot_device::data_w));
+
+ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
+ screen.set_size(240, 100);
+ screen.set_visarea_full();
+ screen.set_refresh_hz(66);
+ screen.set_screen_update(m_asic9, FUNC(psion_asic9_device::screen_update));
+ screen.set_palette(m_palette);
+ PALETTE(config, "palette", FUNC(workabout_state::palette_init), 3);
+
+ SPEAKER(config, "mono").front_center();
+ SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00); // Piezo buzzer
+
+ RAM(config, m_ram);
+
+ PSION_SSD(config, m_ssd[0]);
+ m_ssd[0]->door_cb().set(m_asic9, FUNC(psion_asic9_device::medchng_w));
+ PSION_SSD(config, m_ssd[1]);
+ m_ssd[1]->door_cb().set(m_asic9, FUNC(psion_asic9_device::medchng_w));
+
+ //PSION_EXP_SLOT(config, m_exp[0], psion_exp_devices, nullptr);
+ //PSION_EXP_SLOT(config, m_exp[1], psion_exp_devices, nullptr);
+ //PSION_EXP_SLOT(config, m_exp[2], psion_exp_devices, nullptr);
+
+ SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("WA");
+}
+
+
+void workabout_state::psionwa(machine_config &config)
+{
+ workabout(config);
+
+ m_ram->set_default_size("1M");
+}
+
+void workabout_state::psionwamx(machine_config &config)
+{
+ workabout(config);
+
+ m_asic9->set_clock(3.6864_MHz_XTAL * 15 / 2); // V30MX
+
+ m_ram->set_default_size("2M");
+}
+
+
+ROM_START(psionwa)
+ ROM_REGION16_LE(0x200000, "rom", 0)
+ ROM_SYSTEM_BIOS(0, "240f", "V2.40F 060897")
+ ROMX_LOAD("w1_v2.40f.bin", 0x00000, 0x200000, CRC(4ef1d380) SHA1(d155edf7995c2a799525b53079fff9fb68789f0f), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(1, "100f", "V1.00F 010195")
+ ROMX_LOAD("w1_v1.00f.bin", 0x00000, 0x100000, CRC(8fd7c127) SHA1(316fdc6c54629470f1af3295c46a2e7d58ebdca9), ROM_BIOS(1))
+ ROM_RELOAD(0x100000, 0x100000)
+ ROM_SYSTEM_BIOS(2, "024b", "V0.24B 120296")
+ ROMX_LOAD("w1_v0.24b.bin", 0x00000, 0x200000, CRC(6e7e3016) SHA1(5c35eea431f975cdefc770697a88fe62a1e7af7f), ROM_BIOS(2))
+ROM_END
+
+ROM_START(psionwamx)
+ ROM_REGION16_LE(0x200000, "rom", 0)
+ ROM_SYSTEM_BIOS(0, "720f", "V7.20F 230798")
+ ROMX_LOAD("w2mx_v7.20f.bin", 0x00000, 0x200000, CRC(63734683) SHA1(9d8aa1e45f52e7fcb52d6e81ac47f60d1104c35d), ROM_BIOS(0))
+ROM_END
+
+} // anonymous namespace
+
+
+// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
+COMP( 1995, psionwa, 0, 0, psionwa, workabout, workabout_state, empty_init, "Psion", "Workabout", MACHINE_NOT_WORKING )
+COMP( 1998, psionwamx, 0, 0, psionwamx, workabout, workabout_state, empty_init, "Psion", "Workabout mx", MACHINE_NOT_WORKING )