From f97a17275fa2e0f84ac5bd0480728cc38ca69253 Mon Sep 17 00:00:00 2001 From: Nigel Barnes Date: Wed, 17 May 2023 22:13:06 +0100 Subject: New systems marked not working ------------------------------ Series 3 [Nigel Barnes, The Last Psion] New clones marked not working ----------------------------- Pocket Book [Nigel Barnes, The Last Psion] Series 3s [Nigel Barnes, The Last Psion] --- scripts/src/machine.lua | 9 + src/devices/machine/psion_asic1.cpp | 499 ++++++++++++++++++++++++++++++++++++ src/devices/machine/psion_asic1.h | 101 ++++++++ src/devices/machine/psion_asic2.cpp | 377 +++++++++++++++++++++++++++ src/devices/machine/psion_asic2.h | 93 +++++++ src/devices/machine/psion_asic3.cpp | 188 ++++++++++++++ src/devices/machine/psion_asic3.h | 55 ++++ src/mame/mame.lst | 5 + src/mame/psion/psion3.cpp | 341 ++++++++++++++++++++++++ 9 files changed, 1668 insertions(+) create mode 100644 src/devices/machine/psion_asic1.cpp create mode 100644 src/devices/machine/psion_asic1.h create mode 100644 src/devices/machine/psion_asic2.cpp create mode 100644 src/devices/machine/psion_asic2.h create mode 100644 src/devices/machine/psion_asic3.cpp create mode 100644 src/devices/machine/psion_asic3.h create mode 100644 src/mame/psion/psion3.cpp diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index fb62db5756b..28173df5241 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -2897,6 +2897,9 @@ end --------------------------------------------------- -- +--@src/devices/machine/psion_asic1.h,MACHINES["PSION_ASIC"] = true +--@src/devices/machine/psion_asic2.h,MACHINES["PSION_ASIC"] = true +--@src/devices/machine/psion_asic3.h,MACHINES["PSION_ASIC"] = true --@src/devices/machine/psion_asic4.h,MACHINES["PSION_ASIC"] = true --@src/devices/machine/psion_asic5.h,MACHINES["PSION_ASIC"] = true --@src/devices/machine/psion_asic9.h,MACHINES["PSION_ASIC"] = true @@ -2904,6 +2907,12 @@ end if (MACHINES["PSION_ASIC"]~=null) then files { + MAME_DIR .. "src/devices/machine/psion_asic1.cpp", + MAME_DIR .. "src/devices/machine/psion_asic1.h", + MAME_DIR .. "src/devices/machine/psion_asic2.cpp", + MAME_DIR .. "src/devices/machine/psion_asic2.h", + MAME_DIR .. "src/devices/machine/psion_asic3.cpp", + MAME_DIR .. "src/devices/machine/psion_asic3.h", MAME_DIR .. "src/devices/machine/psion_asic4.cpp", MAME_DIR .. "src/devices/machine/psion_asic4.h", MAME_DIR .. "src/devices/machine/psion_asic5.cpp", diff --git a/src/devices/machine/psion_asic1.cpp b/src/devices/machine/psion_asic1.cpp new file mode 100644 index 00000000000..f04f8504af8 --- /dev/null +++ b/src/devices/machine/psion_asic1.cpp @@ -0,0 +1,499 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/****************************************************************************** + + Psion ASIC1 + + ASIC1 is the main system controller chip for the SIBO architecture. It connects + directly to the 8086-based processor (i.e. the V30H) controlling all bus cycles + to and from the processor. This configuration effectively forms a micro-controller + like device that executes 8086 instruction codes. ASIC 1 is made up of a number of + functional blocks including a bus controller, a programmable timer, an eight input + interrupt controller, an LCD controller and the memory decoding circuitry. + +******************************************************************************/ + +#include "emu.h" +#include "psion_asic1.h" +#include "screen.h" + + +#define VERBOSE 0 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(PSION_ASIC1, psion_asic1_device, "psion_asic1", "Psion ASIC1") + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +psion_asic1_device::psion_asic1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, PSION_ASIC1, tag, owner, clock) + , device_memory_interface(mconfig, *this) + , device_video_interface(mconfig, *this) + , m_space_config("program", ENDIANNESS_LITTLE, 16, 20, 0) + , m_tick_timer(nullptr) + , m_frc_timer(nullptr) + , m_watchdog_timer(nullptr) + , m_int_cb(*this) + , m_nmi_cb(*this) + , m_frcovl_cb(*this) + , m_laptop_mode(false) +{ +} + + +//------------------------------------------------- +// memory_space_config - return a description of +// any address spaces owned by this device +//------------------------------------------------- + +device_memory_interface::space_config_vector psion_asic1_device::memory_space_config() const +{ + return space_config_vector{ + std::make_pair(0, &m_space_config) + }; +} + + +//------------------------------------------------- +// device_resolve_objects - resolve objects that +// may be needed for other devices to set +// initial conditions at start time +//------------------------------------------------- + +void psion_asic1_device::device_resolve_objects() +{ + m_int_cb.resolve_safe(); + m_nmi_cb.resolve_safe(); + m_frcovl_cb.resolve_safe(); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void psion_asic1_device::device_start() +{ + m_space = &space(); + + m_tick_timer = timer_alloc(FUNC(psion_asic1_device::tick), this); + m_frc_timer = timer_alloc(FUNC(psion_asic1_device::frc), this); + m_watchdog_timer = timer_alloc(FUNC(psion_asic1_device::watchdog), this); + + m_a1_status = 0x00; + + save_item(NAME(m_a1_status)); + save_item(NAME(m_a1_lcd_size)); + save_item(NAME(m_a1_lcd_control)); + save_item(NAME(m_a1_interrupt_status)); + save_item(NAME(m_a1_interrupt_mask)); + save_item(NAME(m_a1_protection_mode)); + save_item(NAME(m_a1_protection_lower)); + save_item(NAME(m_a1_protection_upper)); + save_item(NAME(m_frc_count)); + save_item(NAME(m_frc_reload)); + save_item(NAME(m_watchdog_count)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void psion_asic1_device::device_reset() +{ + m_tick_timer->adjust(attotime::from_hz(4), 0, attotime::from_hz(4)); + m_frc_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_frc_count = 0; + m_frc_reload = 0; + m_frc_ovl = 0; + m_watchdog_count = 0; + + m_a1_interrupt_status = 0x00; + m_a1_interrupt_mask = 0x00; + + m_a1_protection_mode = false; + m_a1_protection_lower = 0x00; + m_a1_protection_upper = 0x00; +} + + +TIMER_CALLBACK_MEMBER(psion_asic1_device::tick) +{ + m_a1_interrupt_status |= 0x01; // Timer + update_interrupts(); +} + +TIMER_CALLBACK_MEMBER(psion_asic1_device::frc) +{ + switch (--m_frc_count) + { + case 0x0000: + m_frc_ovl ^= 1; + m_frcovl_cb(m_frc_ovl); + + m_a1_interrupt_status |= 0x20; // FrcExpired + update_interrupts(); + break; + + case 0xffff: + if (BIT(m_a1_status, 0)) // FrcMode + m_frc_count = m_frc_reload; + break; + } +} + +TIMER_CALLBACK_MEMBER(psion_asic1_device::watchdog) +{ + m_watchdog_count++; + m_watchdog_count &= 3; + + if (m_watchdog_count == 3) + { + m_a1_status |= 0x0100; // WatchDogNmi + update_interrupts(); + } +} + +WRITE_LINE_MEMBER(psion_asic1_device::eint1_w) +{ + if (state) + m_a1_interrupt_status |= 0x04; // ExpIntRightB + else + m_a1_interrupt_status &= ~0x04; + + update_interrupts(); +} + +WRITE_LINE_MEMBER(psion_asic1_device::eint2_w) +{ + if (state) + m_a1_interrupt_status |= 0x08; // ExpIntLeftA + else + m_a1_interrupt_status &= ~0x08; + + update_interrupts(); +} + +WRITE_LINE_MEMBER(psion_asic1_device::eint3_w) +{ + if (state) + m_a1_interrupt_status |= 0x10; // Asic2Int + else + m_a1_interrupt_status &= ~0x10; + + update_interrupts(); +} + +WRITE_LINE_MEMBER(psion_asic1_device::enmi_w) +{ + if (state) + m_a1_status |= 0x0200; // ExternalNmi + else + m_a1_status &= ~0x0200; + + update_interrupts(); +} + +void psion_asic1_device::update_interrupts(bool address_trap) +{ + bool irq = m_a1_interrupt_status & m_a1_interrupt_mask; + bool nmi = (m_a1_status & 0x0300) || address_trap; + + m_int_cb(irq ? ASSERT_LINE : CLEAR_LINE); + m_nmi_cb(nmi ? ASSERT_LINE : CLEAR_LINE); +} + + +IRQ_CALLBACK_MEMBER(psion_asic1_device::inta_cb) +{ + // IRQ Vector Name Description + // 0 0x78 TINT Tick interrupt at 2 or 32 Hz + // 1 0x79 EINT0 External interrupt usually connected to mains detect bit + // 2 0x7A EINT1 External interrupt from expansion port one + // 3 0x7B EINT2 External interrupt from expansion port two + // 4 0x7C EINT3 External interrupt from ASIC2 + // 5 0x7D OVINT Timer overflow interrupt + // 6 0x7E SRXI SLD sound receive interrupt + // 7 0x7F STXI SLD sound transmit interrupt + uint8_t vector = 0x78; + for (int irq = 0; irq < 8; irq++) + { + if (m_a1_interrupt_status & m_a1_interrupt_mask & (1 << irq)) + { + vector += irq; + break; + } + } + return vector; +} + + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +bool psion_asic1_device::is_protected(offs_t offset) +{ + if (m_a1_protection_mode && (offset <= m_a1_protection_lower || offset > m_a1_protection_upper)) + { + LOG("%s is_protected: %05x < %05x <= %05x\n", machine().describe_context(), m_a1_protection_lower, offset, m_a1_protection_upper); + update_interrupts(true); + return true; + } + return false; +} + + +uint16_t psion_asic1_device::mem_r(offs_t offset, uint16_t mem_mask) +{ + return m_space->read_word(offset << 1, mem_mask); +} + +void psion_asic1_device::mem_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + if (!is_protected(offset << 1)) + m_space->write_word(offset << 1, data, mem_mask); +} + + +uint16_t psion_asic1_device::io_r(offs_t offset, uint16_t mem_mask) +{ + uint16_t data = 0x00; + + switch (offset << 1) + { + case 0x02: // A1Status + // b0 FrcMode + // b1 TickRate + // b2 FrcSource + // b3 Ram128 + // b4 Ram512 + // b5 LcdEnable + // b6 A1SldEnable + // b7 SldTx + // b8 WatchDogNmi + // b9 ExternalNmi + // b10 Rtc32Hz + // b11 ComboBusy + // b12 SldMsw + // b13 Rtc4Hz + // b14-b15 LcdData - LCD id as follows: + // 0 = Lcd640X400 + // 1 = Lcd640X200Small + // 2 = Lcd640X200Big + // 3 = Lcd720X348 + // 4 = Lcd160X80 + data = m_a1_status; + data |= lcd_type() << 14; + LOG("%s io_r: A1Status => %04x\n", machine().describe_context(), data); + break; + + case 0x06: // A1InterruptStatus + data = m_a1_interrupt_status; + LOG("%s io_r: A1InterruptStatus => %02x\n", machine().describe_context(), data); + break; + + case 0x08: // A1InterruptMask + data = m_a1_interrupt_mask; + LOG("%s io_r: A1InterruptMask => %02x\n", machine().describe_context(), data); + break; + + case 0x12: // A1FrcControl + data = m_frc_count; + LOG("%s io_r: A1FrcControl => %04x\n", machine().describe_context(), data); + break; + + case 0x14: // A1ProtectionOff + if (!machine().side_effects_disabled()) + { + //LOG("%s io_r: A1ProtectionOff => %04x\n", machine().describe_context(), data); + m_a1_protection_mode = false; + } + break; + + default: + data = 0xffff; + LOG("%s io_r: Unhandled register %02x => %04x\n", machine().describe_context(), offset << 1, data); + break; + } + return data; +} + +void psion_asic1_device::io_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + switch (offset << 1) + { + case 0x02: // A1Control + // b0 FrcMode + // b1 TickRate + // b2 FrcSource + // b3 Ram128 + // b4 Ram512 + // b5 LcdEnable + // b6 A1SldEnable + // b7 SldTx + LOG("%s io_w: A1Control <= %04x\n", machine().describe_context(), data); + if (BIT(data, 1) != BIT(m_a1_status, 1)) + { + if (data & 0x02) // TickRate + m_tick_timer->adjust(attotime::zero, 0, attotime::from_hz(32.768)); // RTC from PS34 + else + m_tick_timer->adjust(attotime::zero, 0, attotime::from_hz(4)); + } + m_a1_status = (m_a1_status & 0xff00) | (data & 0xff); + break; + + case 0x04: // A1LcdSize + // b0-b9 LcdEndOfFrame - (Total pixels in display / 128) - 1 + // b10-b14 LcdNumberOfPixels - (No. pixels in line / 32) - 1 + // b15 LcdMLineEnable - 1 to enable the M line magic. + LOG("%s io_w: A1LcdSize <= %04x, Pixels in line %d, Total pixels in display %d\n", machine().describe_context(), data, (BIT(data, 10, 5) + 1) * 32, (BIT(data, 0, 10) + 1) * 128); + m_a1_lcd_size = data; + break; + + case 0x06: // A1LcdControl + // b0-b4 LcdRate - LCDCLK = SYSCLK / (2*(n+1)) + // b5-b9 LcdMLineRate - 13 + // b10-b11 LcdMode - 3 (Dual Screen mode) + LOG("%s io_w: A1LcdControl <= %04x, LCD clock %dHz, %s\n", machine().describe_context(), data, clock() * 4 / (screen().width() * screen().height() * 2 * (BIT(data, 0, 5) + 1)), BIT(data, 10, 2) == 3 ? "Dual screen" : "Single screen"); + m_a1_lcd_control = data; + break; + + case 0x08: // A1InterruptMask + // b0 Timer + // b1 Mains + // b2 ExpIntRightB + // b3 ExpIntLeftA + // b4 Asic2Int + // b5 FrcExpired + // b6 SldReceive + // b7 SldTransmit + LOG("%s io_w: A1InterruptMask <= %02x\n", machine().describe_context(), data); + m_a1_interrupt_mask = data & 0xff; + update_interrupts(); + break; + + case 0x0a: // A1NonSpecificEoi + LOG("%s io_w: A1NonSpecificEoi <= %04x\n", machine().describe_context(), data); + break; + + case 0x0c: // A1TimerEoi + LOG("%s io_w: A1TimerEoi <= %04x\n", machine().describe_context(), data); + m_a1_interrupt_status &= ~0x01; // Timer + update_interrupts(); + break; + + case 0x0e: // A1FrcEoi + LOG("%s io_w: A1FrcEoi <= %04x\n", machine().describe_context(), data); + m_a1_interrupt_status &= ~0x20; // FrcExpired + update_interrupts(); + break; + + case 0x10: // A1ResetWatchDog + //LOG("% io_w: A1ResetWatchDog <= %04x\n", machine().describe_context(), data); + m_watchdog_count = 0; + break; + + case 0x12: // A1FrcControl + LOG("%s io_w: A1FrcControl <= %04x\n", machine().describe_context(), data); + m_frc_reload = data; + m_frc_count = data; + break; + + case 0x14: // A1ProtectionOn + //LOG("%s io_w: A1ProtectionOn <= %04x\n", machine().describe_context(), data); + m_a1_protection_mode = true; + break; + + case 0x16: // A1ProtectionUpper + //LOG("%s io_w: A1ProtectionUpper <= %04x\n", machine().describe_context(), data); + m_a1_protection_upper = (data << 4) | 0x0f; + break; + + case 0x18: // A1ProtectionLower + //LOG("%s io_w: A1ProtectionLower <= %04x\n", machine().describe_context(), data); + m_a1_protection_lower = data << 4; + break; + + case 0x1a: // A1SoundLsw + LOG("%s io_w: A1SoundLsw <= %04x\n", machine().describe_context(), data); + break; + + case 0x1c: // A1SoundMsw + LOG("%s io_w: A1SoundMsw <= %04x\n", machine().describe_context(), data); + break; + + case 0x1e: // A1SoundControl + LOG("%s io_w: A1SoundControl <= %04x\n", machine().describe_context(), data); + break; + + default: + LOG("%s io_w: Unhandled register %02x <= %04x\n", machine().describe_context(), offset << 1, data); + break; + } +} + + +//------------------------------------------------- +// LCD Controller +//------------------------------------------------- + +uint8_t psion_asic1_device::lcd_type() +{ + if (m_laptop_mode) + { + switch (screen().height()) + { + case 400: return 0; + case 200: return 1; + } + } + return 0; +} + +uint32_t psion_asic1_device::screen_update_single(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + return screen_update(screen, bitmap, cliprect, 1); +} + +uint32_t psion_asic1_device::screen_update_dual(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + return screen_update(screen, bitmap, cliprect, 2); +} + +uint32_t psion_asic1_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int screens) +{ + if (m_a1_status & 0x0020) // LCD enable bit + { + pen_t const *const pens = screen.palette().pens(); + + offs_t videoram = m_laptop_mode ? 0xb8000 : 0x00400; + int const width = (BIT(m_a1_lcd_size, 10, 5) + 1) * 32; + int const height = screen.height() / screens; + + for (int vmap = 0; vmap < screens; vmap++) + { + for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y / screens; y++) + { + for (int x = screen.visible_area().min_x; x <= (screen.visible_area().max_x / 8); x++) + { + uint8_t const pixels = m_space->read_byte(videoram + (vmap << 14) + (y * (width / 8)) + x); + uint16_t *p = &bitmap.pix((vmap * height) + y, x << 3); + for (int i = 0; i < 8; i++) + *p++ = pens[BIT(pixels, i)]; + } + } + } + } + else + { + bitmap.fill(0, cliprect); + } + return 0; +} diff --git a/src/devices/machine/psion_asic1.h b/src/devices/machine/psion_asic1.h new file mode 100644 index 00000000000..6318a8f19f4 --- /dev/null +++ b/src/devices/machine/psion_asic1.h @@ -0,0 +1,101 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/****************************************************************************** + + Psion ASIC1 + +******************************************************************************/ + +#ifndef MAME_MACHINE_PSION_ASIC1_H +#define MAME_MACHINE_PSION_ASIC1_H + +#pragma once + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> psion_asic1_device + +class psion_asic1_device : public device_t, + public device_memory_interface, + public device_video_interface +{ +public: + // construction/destruction + psion_asic1_device(const machine_config &mconfig, const char* tag, device_t* owner, uint32_t clock); + + void set_laptop_mode(bool laptop) { m_laptop_mode = laptop; } + + // callbacks + auto int_cb() { return m_int_cb.bind(); } + auto nmi_cb() { return m_nmi_cb.bind(); } + auto frcovl_cb() { return m_frcovl_cb.bind(); } + + uint16_t mem_r(offs_t offset, uint16_t mem_mask); + void mem_w(offs_t offset, uint16_t data, uint16_t mem_mask); + uint16_t io_r(offs_t offset, uint16_t mem_mask); + void io_w(offs_t offset, uint16_t data, uint16_t mem_mask); + + IRQ_CALLBACK_MEMBER(inta_cb); + + DECLARE_WRITE_LINE_MEMBER(eint1_w); + DECLARE_WRITE_LINE_MEMBER(eint2_w); + DECLARE_WRITE_LINE_MEMBER(eint3_w); + DECLARE_WRITE_LINE_MEMBER(enmi_w); + + uint32_t screen_update_single(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); + uint32_t screen_update_dual(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); + +protected: + 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: + const address_space_config m_space_config; + address_space *m_space; + + emu_timer *m_tick_timer; + emu_timer *m_frc_timer; + emu_timer *m_watchdog_timer; + + TIMER_CALLBACK_MEMBER(tick); + TIMER_CALLBACK_MEMBER(frc); + TIMER_CALLBACK_MEMBER(watchdog); + + void update_interrupts(bool address_trap = false); + bool is_protected(offs_t offset); + + uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int plates); + + uint16_t m_a1_status; + uint16_t m_a1_lcd_size; + uint16_t m_a1_lcd_control; + uint16_t m_frc_count; + uint16_t m_frc_reload; + int m_frc_ovl; + uint8_t m_watchdog_count; + bool m_a1_protection_mode; + uint32_t m_a1_protection_upper; + uint32_t m_a1_protection_lower; + + uint8_t m_a1_interrupt_status; + uint8_t m_a1_interrupt_mask; + + devcb_write_line m_int_cb; + devcb_write_line m_nmi_cb; + devcb_write_line m_frcovl_cb; + + bool m_laptop_mode; + uint8_t lcd_type(); +}; + + +// device type definition +DECLARE_DEVICE_TYPE(PSION_ASIC1, psion_asic1_device) + +#endif // MAME_MACHINE_PSION_ASIC1_H diff --git a/src/devices/machine/psion_asic2.cpp b/src/devices/machine/psion_asic2.cpp new file mode 100644 index 00000000000..3fd8c4a9b6a --- /dev/null +++ b/src/devices/machine/psion_asic2.cpp @@ -0,0 +1,377 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/****************************************************************************** + + Psion ASIC2 + + ASIC2 is the peripheral controller chip for the SIBO architecture. It contains + the system clock oscillator and controls switching between the standby and + operating states. ASIC2 provides an interface to the power supply, keyboard, + buzzer and SSDs. ASIC 2 includes the eight-channel SIBO serial protocol + controller and provides interface circuitry to both the reduced external and + extended internal peripheral expansion ports. + +******************************************************************************/ + +#include "emu.h" +#include "psion_asic2.h" + +#define VERBOSE 0 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(PSION_ASIC2, psion_asic2_device, "psion_asic2", "Psion ASIC2") + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +psion_asic2_device::psion_asic2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, PSION_ASIC2, tag, owner, clock) + , m_int_cb(*this) + , m_nmi_cb(*this) + , m_cbusy_cb(*this) + , m_buz_cb(*this) + , m_buzvol_cb(*this) + , m_dr_cb(*this) + , m_col_cb(*this) + , m_data_r(*this) + , m_data_w(*this) +{ +} + + +//------------------------------------------------- +// device_resolve_objects - resolve objects that +// may be needed for other devices to set +// initial conditions at start time +//------------------------------------------------- + +void psion_asic2_device::device_resolve_objects() +{ + m_int_cb.resolve_safe(); + m_nmi_cb.resolve_safe(); + m_cbusy_cb.resolve_safe(); + m_buz_cb.resolve_safe(); + m_buzvol_cb.resolve_safe(); + m_dr_cb.resolve_safe(); + m_col_cb.resolve_safe(0xff); + m_data_r.resolve_all_safe(0x00); + m_data_w.resolve_all_safe(); +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void psion_asic2_device::device_start() +{ + m_a2_status = 0x01; + + m_busy_timer = timer_alloc(FUNC(psion_asic2_device::busy), this); + + save_item(NAME(m_a2_control1)); + save_item(NAME(m_a2_control2)); + save_item(NAME(m_a2_control3)); + save_item(NAME(m_a2_serial_data)); + save_item(NAME(m_a2_serial_control)); + save_item(NAME(m_a2_interrupt_status)); + save_item(NAME(m_a2_status)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void psion_asic2_device::device_reset() +{ + m_a2_control1 = 0x00; + m_a2_control2 = 0x00; + m_a2_control3 = 0x00; + m_a2_serial_data = 0x00; + m_a2_serial_control = 0x00; + m_a2_interrupt_status = 0x00; + m_a2_channel_control = 0x00; +} + + +WRITE_LINE_MEMBER(psion_asic2_device::on_clr_w) +{ + if (state) + m_a2_status |= 0x01; // A1OnKey + else + m_a2_status &= ~0x01; + + update_interrupts(); +} + +WRITE_LINE_MEMBER(psion_asic2_device::dnmi_w) +{ + if (state) + m_a2_interrupt_status |= 0x01; // DNMI + else + m_a2_interrupt_status &= ~0x01; + + update_interrupts(); +} + +WRITE_LINE_MEMBER(psion_asic2_device::frcovl_w) +{ + if (BIT(m_a2_control2, 4)) + { + m_buz_cb(state); + } +} + +WRITE_LINE_MEMBER(psion_asic2_device::reset_w) +{ + if (!state) + m_a2_status |= 0x04; // A1ResetFlag +} + +void psion_asic2_device::update_interrupts() +{ + int irq = m_a2_status & 0x01; + int nmi = BIT(m_a2_interrupt_status, 0, 2) & BIT(m_a2_control3, 4, 2); + + m_int_cb(irq ? ASSERT_LINE : CLEAR_LINE); + m_nmi_cb(nmi ? ASSERT_LINE : CLEAR_LINE); +} + + +TIMER_CALLBACK_MEMBER(psion_asic2_device::busy) +{ + m_a2_status &= ~0x10; // clear data serial controller is busy + m_cbusy_cb(1); +} + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +uint8_t psion_asic2_device::io_r(offs_t offset) +{ + uint8_t data = 0x00; + + switch (offset & 7) + { + case 0x02: // A2External - External status register + switch (m_a2_control1 & 0x0f) + { + case 0x0f: // all keyboard columns + for (int i = 0; i < 10; i++) + data |= m_col_cb(i); + break; + + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: + data = m_col_cb((m_a2_control1 & 0x0f) - 1); + break; + + default: + data = 0x00; + break; + } + LOG("%s io_r: A2External => %02x\n", machine().describe_context(), data); + break; + + case 0x03: // A2InterruptStatus - Interrupt status register + // b0 DoorInterrupt - Status of pack doors, 1 if open + // b1 ExpansionInterrupt - Status of expansion interface doors, 1 if open + // b2 SlaveDataValid - Slave data valid, 1 if valid frame arrived + // b3 SlaveDataControl - Slave control or data frame, 1 if control frame + // b4 SlaveDataOverrun - Slave data overrun, 1 if overrun + data = m_a2_interrupt_status; + LOG("%s io_r: A2InterruptStatus => %02x\n", machine().describe_context(), data); + break; + + case 0x04: // A2Status - Status register + // b0 A1OnKey - 1 if on key pressed + // b1 WakeUp - 1 if external wakeup valid + // b2 A1ResetFlag - 1 if system reset + // b3 SerialClockState - 1 if slave clock is high + // b4 SerialBusy - 1 while data serial controller is busy + // b5 A2Sdis - 1 if slave data signal is high + data = m_a2_status; + if (!machine().side_effects_disabled()) + { + m_a2_status &= ~0x01; // clear A1OnKey + m_a2_status &= ~0x04; // clear A1ResetFlag + } + LOG("%s io_r: A2Status => %02x\n", machine().describe_context(), data); + break; + + case 0x05: // A2SerialData - Serial channel read data register + data = m_a2_serial_data; + if ((m_a2_serial_control & 0x10) == 0x10) + m_a2_serial_data = receive_frame(); + LOG("%s io_r: A2SerialData => %02x\n", machine().describe_context(), data); + break; + + case 0x06: // A2KeyData - Keyboard poll register + data = m_col_cb(m_a2_control1 & 0x0f); + LOG("%s io_r: A2KeyData => %02x\n", machine().describe_context(), data); + break; + + case 0x07: // A2SlaveData - Slave data read register + data = 0x00; + LOG("%s io_r: A2SlaveData => %02x\n", machine().describe_context(), data); + break; + + default: + LOG("%s io_r: Unhandled register %02x => %02x\n", machine().describe_context(), offset, data); + break; + } + return data; +} + +void psion_asic2_device::io_w(offs_t offset, uint8_t data) +{ + switch (offset & 7) + { + case 0x02: // A2Control1 - Control register 1 + // b0-b3 KeyScan - Values 0-15 drive the keyboard poll columns + // b4-b5 SerialClockRate - Sets the clock frequency for channels 1-4 and 7 as below: + // 0 = ClockRateMedium 1.536 MHz + // 2 = ClockRateSlow 256 KHz + // 3 = ClockRateFast 3.84 MHz + LOG("%s io_w: A2Control1 <= %02x\n", machine().describe_context(), data); + m_a2_control1 = data; + //m_a2_external = m_col_cb(data & 0x0f); + break; + + case 0x03: // A2Control2 - Control register 2 + // b0 DigitizerEnable - 1 to enable digitizer drivers + // b1 XySwitch - 0 to drive digitizer X scan, 1 to drive Y + // b2 BuzzerToggle - Toggles the piezo speaker + // b3 BuzzerVolume - 1 if loud buzzer volume, 0 is soft + // b4 BuzzerMode - Piezo driving mode, 0 - toggled by BZTOG, 1 - driven by FRC + // b5 ClockEnable5 - Enable continuous clock on serial channel 5 + // b6 ClockEnable6 - Enable continuous clock on serial channel 6 + // b7 ClockEnable7 - Enable continuous clock on serial channel 7 + LOG("%s io_w: A2Control2 <= %02x\n", machine().describe_context(), data); + m_a2_control2 = data; + m_dr_cb(BIT(data, 1)); + if (!BIT(data, 4)) + { + m_buz_cb(BIT(data, 2)); + } + m_buzvol_cb(BIT(data, 3)); + break; + + case 0x04: // A2Control3 - Control register 3 + // b0 SerialNull - 1 to send a null frame + // b1 Ps34Acknowledge - 1 to reset PS34 on power up + // b2 A2SldEnable - 1 to enable SLD bus clock + // b3 SerialEnable - 1 to enable access to pack channels 1-4 + // b4 DoorEnable - 1 to enable pack door NMIs + // b5 ExpansionEnable - 1 to enable expansion door NMIs + // b6 VhControl - 1 to enable VPP to packs + // b7 ElEnable - 1 to enable LCD back light + LOG("%s io_w: A2Control3 <= %02x\n", machine().describe_context(), data); + m_a2_control3 = data; + if (BIT(m_a2_control3, 0)) + transmit_frame(NULL_FRAME); + break; + + case 0x05: // A2SerialData - Serial channel write data register + LOG("%s io_w: A2SerialData <= %02x\n", machine().describe_context(), data); + if ((m_a2_serial_control & 0xc0) == 0x80) + transmit_frame(DATA_FRAME | data); + break; + + case 0x06: // A2SerialControl - Serial channel write control register + // WriteSingle 10000000b; ReadSingle 11000000b + // WriteMulti 10010000b; ReadMulti 11010000b + // Reset 00000000b; Select 01000000b + // Asic2SlaveId 001h; Asic5PackId 002h + // Asic5NormalId 003h; Asic6Id 004h + // Asic8Id 005h; Asic4Id 006h + LOG("%s io_w: A2SerialControl <= %02x\n", machine().describe_context(), data); + m_a2_serial_control = data; + transmit_frame(CONTROL_FRAME | data); + + if ((m_a2_serial_control & 0x40) == 0x40) + m_a2_serial_data = receive_frame(); + break; + + case 0x07: // A2ChannelControl - 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-b6 ChannelSelect - 0-7 to select channel 0,5-8 as below: + // 4 = PK0, 5 = PK5, 6 = PK6, 7 = PK7 + // b7 MultiplexEnable - 1 to loop slave channel to pack channel + LOG("%s io_w: A2ChannelControl <= %02x Channels %c %c %c %c %c %c %c %c\n", machine().describe_context(), data, + BIT(data, 4, 3) == 4 ? '0' : ' ', + BIT(data, 0) ? '1' : ' ', + BIT(data, 1) ? '2' : ' ', + BIT(data, 2) ? '3' : ' ', + BIT(data, 3) ? '4' : ' ', + BIT(data, 4, 3) == 5 ? '5' : ' ', + BIT(data, 4, 3) == 6 ? '6' : ' ', + BIT(data, 4, 3) == 7 ? '7' : ' '); + m_a2_channel_control = data; + break; + + default: + LOG("%s io_w: Unhandled register %02x <= %02x\n", machine().describe_context(), offset, data); + break; + } +} + + +//------------------------------------------------- +// SIBO Serial Protocol Controller +//------------------------------------------------- + +bool psion_asic2_device::channel_active(int channel) +{ + switch (channel) + { + case 0: + return BIT(m_a2_channel_control, 4, 3) == 4; + case 1: case 2: case 3: case 4: + return BIT(m_a2_channel_control, channel - 1); + case 5: case 6: case 7: + return BIT(m_a2_channel_control, 4, 3) == channel; + } + return false; +} + +void psion_asic2_device::transmit_frame(uint16_t data) +{ + m_a2_status |= 0x10; // set data serial controller is busy + m_busy_timer->adjust(attotime::from_ticks(12, clock() / 2)); + m_cbusy_cb(0); + + 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_asic2_device::receive_frame() +{ + uint8_t data = 0x00; + + m_a2_status |= 0x10; // set data serial controller is busy + m_busy_timer->adjust(attotime::from_ticks(12, clock() / 2)); + m_cbusy_cb(0); + + 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; +} diff --git a/src/devices/machine/psion_asic2.h b/src/devices/machine/psion_asic2.h new file mode 100644 index 00000000000..7045d5b043d --- /dev/null +++ b/src/devices/machine/psion_asic2.h @@ -0,0 +1,93 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/****************************************************************************** + + Psion ASIC2 + +******************************************************************************/ + +#ifndef MAME_MACHINE_PSION_ASIC2_H +#define MAME_MACHINE_PSION_ASIC2_H + +#pragma once + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> psion_asic2_device + +class psion_asic2_device : public device_t +{ +public: + // construction/destruction + psion_asic2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // callbacks + auto int_cb() { return m_int_cb.bind(); } + auto nmi_cb() { return m_nmi_cb.bind(); } + auto cbusy_cb() { return m_cbusy_cb.bind(); } + auto buz_cb() { return m_buz_cb.bind(); } + auto buzvol_cb() { return m_buzvol_cb.bind(); } + auto dr_cb() { return m_dr_cb.bind(); } + auto col_cb() { return m_col_cb.bind(); } + + template auto data_r() { static_assert(N < 8); return m_data_r[N].bind(); } + template auto data_w() { static_assert(N < 8); return m_data_w[N].bind(); } + + uint8_t io_r(offs_t offset); + void io_w(offs_t offset, uint8_t data); + + DECLARE_WRITE_LINE_MEMBER(on_clr_w); + DECLARE_WRITE_LINE_MEMBER(dnmi_w); + DECLARE_WRITE_LINE_MEMBER(frcovl_w); + DECLARE_WRITE_LINE_MEMBER(reset_w); + +protected: + virtual void device_resolve_objects() override; + virtual void device_start() override; + virtual void device_reset() override; + +private: + void update_interrupts(); + + uint8_t m_a2_control1; + uint8_t m_a2_control2; + uint8_t m_a2_control3; + uint8_t m_a2_serial_data; + uint8_t m_a2_serial_control; + uint8_t m_a2_interrupt_status; + uint8_t m_a2_status; + uint8_t m_a2_channel_control; + //uint8_t m_a2_external; + + devcb_write_line m_int_cb; + devcb_write_line m_nmi_cb; + devcb_write_line m_cbusy_cb; + devcb_write_line m_buz_cb; + devcb_write_line m_buzvol_cb; + devcb_write_line m_dr_cb; + devcb_read8 m_col_cb; + + 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_ASIC2, psion_asic2_device) + +#endif // MAME_MACHINE_PSION_ASIC2_H diff --git a/src/devices/machine/psion_asic3.cpp b/src/devices/machine/psion_asic3.cpp new file mode 100644 index 00000000000..0585fd5e60b --- /dev/null +++ b/src/devices/machine/psion_asic3.cpp @@ -0,0 +1,188 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/****************************************************************************** + + Psion ASIC3 + + MC and HC power supplies are based on a full custom liner ASIC known as ASIC3. + This custom chip is manufactured by Maxim and has the Maxim part number MAX616. + + TODO: + - Battery status readings, maybe different analogue channel. + - Resolve ambiguity about status register, S3 and MC/HC machines expect different data. + +******************************************************************************/ + +#include "emu.h" +#include "psion_asic3.h" + +#define VERBOSE 0 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(PSION_ASIC3, psion_asic3_device, "psion_asic3", "Psion ASIC3") + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +psion_asic3_device::psion_asic3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, PSION_ASIC3, tag, owner, clock) + , m_adin_cb(*this) +{ +} + + +//------------------------------------------------- +// device_resolve_objects - resolve objects that +// may be needed for other devices to set +// initial conditions at start time +//------------------------------------------------- + +void psion_asic3_device::device_resolve_objects() +{ + m_adin_cb.resolve_safe(0); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void psion_asic3_device::device_start() +{ + save_item(NAME(m_a3_status)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void psion_asic3_device::device_reset() +{ + m_a3_status = 0x00; +} + + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +void psion_asic3_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 + break; + } + break; + + case DATA_FRAME: + data &= 0xff; + switch (m_sibo_control & 0x0f) + { + case 0x01: // A3Control1 + // b0-b4 DtoaBits + // b5 Vcc3Enable LCD + // b6 Vcc4Enable Sound subsystem + // b7 Vcc5Enable SSDs + LOG("%s data_w: A3Control1 register %02x\n", machine().describe_context(), data); + m_a3_control1 = data; + break; + + case 0x02: // A3Setup + LOG("%s data_w: A3Setup register %02x\n", machine().describe_context(), data); + break; + + case 0x03: // A3Control2 + // b0 Vee1Enable LCD + // b1 Vee2Enable Sound subsystem + // b2 VhEnable + // b3 Vee1SoftStart + // b4 Vee2SoftStart + // b5 VhSoftStart + // b6-b7 AnalogueMultiplex + LOG("%s data_w: A3Control2 register %02x\n", machine().describe_context(), data); + m_a3_control2 = data; + break; + + case 0x04: // A3Dummy1 + LOG("%s data_w: A3Dummy1 register %02x\n", machine().describe_context(), data); + break; + + case 0x07: // A3Control3 + // b0 xDummy1 + // b1 OffEnable + // b2 AdcReadHighEnable + LOG("%s data_w: A3Control3 register %02x\n", machine().describe_context(), data); + m_a3_control3 = data; + break; + + default: + LOG("%s data_w: Unhandled register %02x\n", machine().describe_context(), m_sibo_control & 0x0f); + break; + } + } +} + +uint8_t psion_asic3_device::data_r() +{ + uint8_t data = 0x00; + + switch (m_sibo_control & 0xc0) + { + case 0x40: // SerialSelect + if (m_sibo_control == 0x43) // A3SelectId + data = 0x80; // A3InfoByte + break; + + case 0xc0: // SerialRead + switch (m_sibo_control & 0x0f) + { + case 0x00: // A3Adc + // A3AdcLsbR + // b0-b6 OtherBits + // b7 InvertedBit + // A3AdcMsbR + // b0-b3 AdcBits + // b4 Overrange + // b5 Polarity + if (BIT(m_a3_control3, 2)) // AdcReadHighEnable + data = BIT(m_adin_cb(), 8, 4); + else + data = (m_adin_cb() & 0xff) ^ 0x80; + LOG("%s data_r: A3Adc register %02x\n", machine().describe_context(), data); + break; + + case 0x0d: // A3Status + // b0-b2 Admsb + // b3 Nc + // b4 Penup + // b5 Vhready + // b6 ColdStart + // b7 PowerFail + + // b0 ColdStart + // b1 PowerFail + data = 0x40; + LOG("%s data_r: A3Status register %02x\n", machine().describe_context(), data); + break; + + default: + LOG("%s data_r: Unhandled register %02x\n", machine().describe_context(), m_sibo_control & 0x0f); + break; + } + break; + } + + return data; +} diff --git a/src/devices/machine/psion_asic3.h b/src/devices/machine/psion_asic3.h new file mode 100644 index 00000000000..0279f4e8c5d --- /dev/null +++ b/src/devices/machine/psion_asic3.h @@ -0,0 +1,55 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/****************************************************************************** + + Psion ASIC3 + +******************************************************************************/ + +#ifndef MAME_MACHINE_PSION_ASIC3_H +#define MAME_MACHINE_PSION_ASIC3_H + +#pragma once + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> psion_asic3_device + +class psion_asic3_device : public device_t +{ +public: + psion_asic3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + // callbacks + auto adin_cb() { return m_adin_cb.bind(); } + + void data_w(uint16_t data); + uint8_t data_r(); + +protected: + virtual void device_resolve_objects() override; + 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_read16 m_adin_cb; + + uint8_t m_sibo_control; + uint8_t m_a3_control1; + uint8_t m_a3_control2; + uint8_t m_a3_control3; + uint8_t m_a3_status; +}; + + +// device type definition +DECLARE_DEVICE_TYPE(PSION_ASIC3, psion_asic3_device) + +#endif // MAME_MACHINE_PSION_ASIC3_H diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 5567f594b2b..10c47129bdc 100755 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -37721,6 +37721,11 @@ psionp350 // psionp464 // psionxp // +@source:psion/psion3.cpp +pocketbk // 1992 Acorn Pocket Book +psion3 // 1991 Psion Series 3 +psion3s // 1994 Psion Series 3s + @source:psion/psion3a.cpp pocketbk2 // 1994 Acorn Pocket Book II psion3a // 1993 Psion Series 3a diff --git a/src/mame/psion/psion3.cpp b/src/mame/psion/psion3.cpp new file mode 100644 index 00000000000..3451f2f7b2f --- /dev/null +++ b/src/mame/psion/psion3.cpp @@ -0,0 +1,341 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/****************************************************************************** + + Psion Series 3 + + TODO: + - battery backed RAM + - DTMF tone generator + +******************************************************************************/ + +#include "emu.h" +#include "cpu/nec/nec.h" +#include "machine/psion_asic1.h" +#include "machine/psion_asic2.h" +#include "machine/psion_ssd.h" +#include "machine/ram.h" +#include "sound/pcd3311.h" +#include "sound/spkrdev.h" +#include "bus/psion/sibo/slot.h" + +#include "emupal.h" +#include "screen.h" +#include "softlist_dev.h" +#include "speaker.h" +#include "utf8.h" + + +namespace { + +class psion3_state : public driver_device +{ +public: + psion3_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag) + , m_maincpu(*this, "maincpu") + , m_asic1(*this, "asic1") + , m_asic2(*this, "asic2") + , m_ram(*this, "ram") + , m_palette(*this, "palette") + , m_keyboard(*this, "COL%u", 0U) + , m_dtmf(*this, "dtmf") + , m_speaker(*this, "speaker") + , m_ssd(*this, "ssd%u", 1U) + , m_sibo(*this, "sibo") + { } + + void psion3(machine_config &config); + void psion3s(machine_config &config); + + DECLARE_INPUT_CHANGED_MEMBER(key_on); + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + +private: + required_device m_maincpu; + required_device m_asic1; + required_device m_asic2; + required_device m_ram; + required_device m_palette; + required_ioport_array<10> m_keyboard; + required_device m_dtmf; + required_device m_speaker; + required_device_array m_ssd; + required_device m_sibo; + + void palette_init(palette_device &palette); + + void mem_map(address_map &map); + void io_map(address_map &map); + void asic1_map(address_map &map); +}; + + +void psion3_state::machine_start() +{ + m_asic1->space(0).install_ram(0, m_ram->mask(), m_ram->pointer()); +} + +void psion3_state::machine_reset() +{ +} + + +void psion3_state::mem_map(address_map &map) +{ + map(0x00000, 0xfffff).rw(m_asic1, FUNC(psion_asic1_device::mem_r), FUNC(psion_asic1_device::mem_w)); +} + +void psion3_state::io_map(address_map &map) +{ + map(0x0000, 0x001f).rw(m_asic1, FUNC(psion_asic1_device::io_r), FUNC(psion_asic1_device::io_w)); + map(0x0080, 0x008f).rw(m_asic2, FUNC(psion_asic2_device::io_r), FUNC(psion_asic2_device::io_w)).umask16(0x00ff); + //map(0x0100, 0x01ff).lr8(NAME([]() { return 0xff; })); // w: enable Vcc lines? + map(0x0200, 0x02ff).w(m_dtmf, FUNC(pcd3311_device::write)); +} + +void psion3_state::asic1_map(address_map &map) +{ + map(0x80000, 0xfffff).rom().region("flash", 0); +} + + +static INPUT_PORTS_START( psion3 ) + PORT_START("COL0") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PORT_NAME("1 ! Off") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME(UTF8_RIGHT" End") + + PORT_START("COL1") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') PORT_CHAR('#') + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PORT_CHAR('~') + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("Help Dial") + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_') + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (R)") + + PORT_START("COL2") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PORT_CHAR('@') + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PORT_CHAR('\'') + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('?') PORT_CHAR('}') + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('+') PORT_CHAR('=') + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') + + PORT_START("COL3") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("Menu") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PORT_CHAR('[') + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') PORT_CHAR(']') + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME(UTF8_LEFT" Home") + + PORT_START("COL4") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME(UTF8_UP" Pg Up") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME("Caps Lock") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME(UTF8_DOWN" Pg Dn") + + PORT_START("COL5") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(0xa3) PORT_CHAR('\\') + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PORT_CHAR('{') + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR('*') PORT_CHAR(':') + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') + + PORT_START("COL6") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Control") + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (L)") + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("Psion") + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(0x08) PORT_NAME("Delete") + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Enter") + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') + + PORT_START("COL7") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("Data") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("Time") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("Program") + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(0x09) PORT_NAME("Tab") + + PORT_START("COL8") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("Word") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("World") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED) + + PORT_START("COL9") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("System") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("Agenda") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("Calc") + PORT_BIT(0xf8, IP_ACTIVE_HIGH, IPT_UNUSED) + + PORT_START("ESC") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Esc On") PORT_CHANGED_MEMBER(DEVICE_SELF, psion3_state, key_on, 0) +INPUT_PORTS_END + + +static INPUT_PORTS_START( psion3s ) + PORT_INCLUDE(psion3) + + PORT_MODIFY("COL7") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("Sheet") +INPUT_PORTS_END + + +static INPUT_PORTS_START( pocketbk ) + PORT_INCLUDE(psion3) + + PORT_MODIFY("COL1") + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("Help") + + PORT_MODIFY("COL6") + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_SHIFT_2) PORT_NAME("Acorn") + + PORT_MODIFY("COL7") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("Cards") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("Time") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("Key2") + + PORT_MODIFY("COL8") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("Write") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("Calc") + + PORT_MODIFY("COL9") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("Desktop") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("Abacus") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("Key1") +INPUT_PORTS_END + + +INPUT_CHANGED_MEMBER(psion3_state::key_on) +{ + if (newval) + { + m_asic2->on_clr_w(newval); + } +} + + +void psion3_state::palette_init(palette_device &palette) +{ + palette.set_pen_color(0, rgb_t(170, 180, 160)); + palette.set_pen_color(1, rgb_t(80, 75, 50)); +} + + +void psion3_state::psion3(machine_config &config) +{ + V30(config, m_maincpu, 7.68_MHz_XTAL / 2); + m_maincpu->set_addrmap(AS_PROGRAM, &psion3_state::mem_map); + m_maincpu->set_addrmap(AS_IO, &psion3_state::io_map); + m_maincpu->set_irq_acknowledge_callback(m_asic1, FUNC(psion_asic1_device::inta_cb)); + + screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD)); + screen.set_size(240, 80); + screen.set_visarea_full(); + screen.set_refresh_hz(66); + screen.set_screen_update(m_asic1, FUNC(psion_asic1_device::screen_update_single)); + screen.set_palette(m_palette); + PALETTE(config, "palette", FUNC(psion3_state::palette_init), 2); + + PSION_ASIC1(config, m_asic1, 7.68_MHz_XTAL); + m_asic1->set_laptop_mode(false); + m_asic1->set_addrmap(0, &psion3_state::asic1_map); + m_asic1->int_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ0); + m_asic1->nmi_cb().set_inputline(m_maincpu, INPUT_LINE_NMI); + m_asic1->frcovl_cb().set(m_asic2, FUNC(psion_asic2_device::frcovl_w)); + + PSION_ASIC2(config, m_asic2, 7.68_MHz_XTAL); + m_asic2->int_cb().set(m_asic1, FUNC(psion_asic1_device::eint3_w)); + m_asic2->nmi_cb().set(m_asic1, FUNC(psion_asic1_device::enmi_w)); + m_asic2->cbusy_cb().set_inputline(m_maincpu, NEC_INPUT_LINE_POLL); + m_asic2->buz_cb().set(m_speaker, FUNC(speaker_sound_device::level_w)); + m_asic2->buzvol_cb().set([this](int state) { m_speaker->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.25); }); + m_asic2->col_cb().set([this](uint8_t data) { return m_keyboard[data]->read(); }); + m_asic2->data_r<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_r)); // SSD Pack 1 + m_asic2->data_w<1>().set(m_ssd[0], FUNC(psion_ssd_device::data_w)); + m_asic2->data_r<2>().set(m_ssd[1], FUNC(psion_ssd_device::data_r)); // SSD Pack 2 + m_asic2->data_w<2>().set(m_ssd[1], FUNC(psion_ssd_device::data_w)); + m_asic2->data_r<7>().set(m_sibo, FUNC(psion_sibo_slot_device::data_r)); // SIBO expansion port + m_asic2->data_w<7>().set(m_sibo, FUNC(psion_sibo_slot_device::data_w)); + + RAM(config, m_ram).set_default_size("256K").set_extra_options("128K"); + + SPEAKER(config, "mono").front_center(); + SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00); // Piezo buzzer + + PCD3311(config, m_dtmf, 3'580'000).add_route(ALL_OUTPUTS, "mono", 0.25); // PCD3311CT + + PSION_SSD(config, m_ssd[0]); + m_ssd[0]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w)); + PSION_SSD(config, m_ssd[1]); + m_ssd[1]->door_cb().set(m_asic2, FUNC(psion_asic2_device::dnmi_w)); + + PSION_SIBO_SLOT(config, m_sibo, psion_sibo_devices, nullptr); + //m_sibo->int_cb().set(m_asic2, FUNC(psion_asic2_device::eint_w)); // TODO: unknown interrupt line + + SOFTWARE_LIST(config, "ssd_list").set_original("psion_ssd").set_filter("S3"); +} + +void psion3_state::psion3s(machine_config &config) +{ + psion3(config); + + m_ram->set_default_size("256K").set_extra_options(""); +} + + +ROM_START(psion3) + ROM_REGION16_LE(0x80000, "flash", ROMREGION_ERASEFF) + ROM_SYSTEM_BIOS(0, "177f", "V1.77F/ENG 221091") + ROMX_LOAD("3504-3002-01_19-11-91v1.77f_eng.bin", 0x00000, 0x20000, CRC(73ba99fa) SHA1(1b3f7b2da9cc2f189e88a9aa01fdb6fad7598925), ROM_BIOS(0)) + ROMX_LOAD("3504-3001-01_19-11-91v1.77f_eng.bin", 0x40000, 0x40000, CRC(e868c250) SHA1(48cce7dd219fb776bffe247c48ba070a89bff121), ROM_BIOS(0)) +ROM_END + +ROM_START(psion3s) + ROM_REGION16_LE(0x80000, "flash", ROMREGION_ERASEFF) + ROM_SYSTEM_BIOS(0, "191f", "V1.91F/ENG 140694") + ROMX_LOAD("s3_v1.91f_eng.bin", 0x00000, 0x80000, CRC(8b4cfa7a) SHA1(582ffba8ec81960b2db283ef0c280a6d8444414f), ROM_BIOS(0)) +ROM_END + +ROM_START(pocketbk) + ROM_REGION16_LE(0x80000, "flash", ROMREGION_ERASEFF) + ROM_SYSTEM_BIOS(0, "191f", "V1.91F/ACN 270892") + ROMX_LOAD("pb_v1.91f_acn.bin", 0x00000, 0x80000, CRC(875a804b) SHA1(9db07b3de9bcb9cc0c56c9a6fb35b9653eba68b3), ROM_BIOS(0)) +ROM_END + +} // anonymous namespace + + +// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS +COMP( 1991, psion3, 0, 0, psion3, psion3, psion3_state, empty_init, "Psion", "Series 3", MACHINE_NOT_WORKING ) +COMP( 1992, pocketbk, psion3, 0, psion3s, pocketbk, psion3_state, empty_init, "Acorn Computers", "Pocket Book", MACHINE_NOT_WORKING ) +COMP( 1994, psion3s, psion3, 0, psion3s, psion3s, psion3_state, empty_init, "Psion", "Series 3s", MACHINE_NOT_WORKING ) -- cgit v1.2.3