summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/psion_asic9.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/psion_asic9.cpp')
-rw-r--r--src/devices/machine/psion_asic9.cpp1239
1 files changed, 1239 insertions, 0 deletions
diff --git a/src/devices/machine/psion_asic9.cpp b/src/devices/machine/psion_asic9.cpp
new file mode 100644
index 00000000000..fc7d677bfab
--- /dev/null
+++ b/src/devices/machine/psion_asic9.cpp
@@ -0,0 +1,1239 @@
+// 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:
+ - improve RAM configuration for mx machines
+ - set RTC timer
+ - ASIC9MX implements V30MX, and likely the Temic (Condor) device found in 3c/Siena
+
+******************************************************************************/
+
+#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 V30H")
+DEFINE_DEVICE_TYPE(PSION_ASIC9MX, psion_asic9mx_device, "psion_asic9mx", "Psion ASIC9 V30MX")
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+psion_asic9_device::psion_asic9_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_memory_interface(mconfig, *this)
+ , device_video_interface(mconfig, *this)
+ , m_v30(*this, "v30")
+ , 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_snd_timer(nullptr)
+ , m_buz_cb(*this)
+ , m_col_cb(*this)
+ , m_port_ab_r(*this, 0)
+ , m_port_ab_w(*this)
+ , m_pcm_in(*this, 0)
+ , m_pcm_out(*this)
+ , m_data_r(*this, 0x00)
+ , m_data_w(*this)
+{
+}
+
+psion_asic9_device::psion_asic9_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : psion_asic9_device(mconfig, PSION_ASIC9, tag, owner, clock)
+{
+}
+
+psion_asic9mx_device::psion_asic9mx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : psion_asic9_device(mconfig, PSION_ASIC9MX, tag, owner, clock)
+{
+}
+
+
+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_v30, DERIVED_CLOCK(1, 1));
+ m_v30->set_addrmap(AS_PROGRAM, &psion_asic9_device::mem_map);
+ m_v30->set_addrmap(AS_IO, &psion_asic9_device::io_map);
+ m_v30->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_start - device-specific startup
+//-------------------------------------------------
+
+void psion_asic9_device::device_start()
+{
+ if (!m_ram->started())
+ throw device_missing_dependencies();
+
+ m_ram_space = &space(AS_A9_RAM);
+ m_rom_space = &space(AS_A9_ROM);
+
+ m_ram_type = get_ram_type(m_ram->size());
+
+ 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_snd_timer = timer_alloc(FUNC(psion_asic9_device::snd), 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));
+}
+
+uint8_t psion_asic9_device::get_ram_type(uint32_t ram_size)
+{
+ switch (ram_size)
+ {
+ case 0x010000: case 0x020000:
+ return 0;
+
+ case 0x040000: case 0x080000:
+ return 1;
+
+ case 0x100000: case 0x200000:
+ return 2;
+
+ case 0x400000: case 0x800000:
+ return 3;
+ }
+ return 0;
+}
+
+uint8_t psion_asic9mx_device::get_ram_type(uint32_t ram_size)
+{
+ switch (ram_size)
+ {
+ case 0x0080000: case 0x0100000:
+ return 0;
+
+ case 0x0200000: case 0x0400000:
+ return 1;
+
+ case 0x0800000: case 0x1000000:
+ return 2;
+
+ case 0x2000000: case 0x4000000:
+ return 3;
+ }
+ return 0;
+}
+
+//-------------------------------------------------
+// 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_snd_timer->adjust(attotime::from_hz(8000), 0, attotime::from_hz(8000)); // unknown data rate
+
+ 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_buz_toggle = 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 = 0;
+
+ m_a9_status |= 0x0020; // A9MMainsPresent
+ 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:
+ if (BIT(m_a9_control_extra, 5)) // A9MBuzzFromFrc1OrTog
+ {
+ m_buz_cb(m_buz_toggle ^= 1);
+ }
+ 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++;
+}
+
+TIMER_CALLBACK_MEMBER(psion_asic9_device::snd)
+{
+ if (BIT(m_a9_control, 11)) // A9MSoundEnable
+ {
+ switch (BIT(m_a9_control_extra, 7)) // A9MSoundDir
+ {
+ case 0:
+ if (!m_snd_fifo.full())
+ m_snd_fifo.enqueue(m_pcm_in());
+ if (m_snd_fifo.full())
+ m_a9_status |= 0x0800;
+ break;
+
+ case 1:
+ if (!m_snd_fifo.empty())
+ m_pcm_out(m_snd_fifo.dequeue());
+ if (!m_snd_fifo.full())
+ m_a9_status &= ~0x0800;
+ break;
+ }
+ m_a9_interrupt_status |= 0x01; // Sound
+ update_interrupts();
+ }
+}
+
+void psion_asic9_device::sds_int_w(int state)
+{
+ if (state)
+ m_a9_interrupt_status |= 0x04; // A9MSlave
+ else
+ m_a9_interrupt_status &= ~0x04;
+
+ update_interrupts();
+}
+
+void psion_asic9_device::eint0_w(int state)
+{
+ if (state)
+ m_a9_interrupt_status |= 0x08; // A9MExpIntC
+ else
+ m_a9_interrupt_status &= ~0x08;
+
+ update_interrupts();
+}
+
+void psion_asic9_device::eint1_w(int state)
+{
+ if (state)
+ m_a9_interrupt_status |= 0x10; // A9MExpIntA
+ else
+ m_a9_interrupt_status &= ~0x10;
+
+ update_interrupts();
+}
+
+void psion_asic9_device::eint2_w(int state)
+{
+ if (state)
+ m_a9_interrupt_status |= 0x20; // A9MExpIntB
+ else
+ m_a9_interrupt_status &= ~0x20;
+
+ update_interrupts();
+}
+
+
+void psion_asic9_device::medchng_w(int state)
+{
+ 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_v30->set_input_line(INPUT_LINE_IRQ0, irq ? ASSERT_LINE : CLEAR_LINE);
+ m_v30->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_v30->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;
+
+ // RAM is installed in pairs, ie. 256KBits = 2 x 32K
+ 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;
+}
+
+uint32_t psion_asic9mx_device::ram_device_size(uint8_t device_type)
+{
+ uint32_t size = 0;
+
+ // TODO: unknown for A9MX but allow EPOC16 to correctly identify 2MB fitted to all MX machines.
+ switch (device_type & 3)
+ {
+ case 0: size = 0x0040000; break;
+ case 1: size = 0x0100000; break;
+ case 2: size = 0x0400000; break;
+ case 3: size = 0x1000000; break;
+ }
+ 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;
+}
+
+offs_t psion_asic9_device::translate_address(offs_t offset)
+{
+ switch (offset & 0xf0000)
+ {
+ case 0x00000: case 0x10000: case 0x20000: case 0x30000: case 0x40000: case 0x50000:
+ break;
+
+ case 0x60000:
+ offset = (m_a9_psel_6000 << 16) | (offset & 0xffff);
+ break;
+
+ case 0x70000:
+ offset = (m_a9_psel_7000 << 16) | (offset & 0xffff);
+ break;
+
+ case 0x80000:
+ offset = (m_a9_psel_8000 << 16) | (offset & 0xffff);
+ break;
+
+ case 0x90000:
+ offset = (m_a9_psel_9000 << 16) | (offset & 0xffff);
+ break;
+
+ case 0xa0000: case 0xb0000: case 0xc0000: case 0xd0000: case 0xe0000: case 0xf0000:
+ offset = 0xf00000 | offset;
+ break;
+ }
+ return offset;
+}
+
+uint16_t psion_asic9_device::mem_r(offs_t offset, uint16_t mem_mask)
+{
+ uint16_t data = 0xffff;
+
+ offset <<= 1;
+
+ offs_t addr = translate_address(offset);
+
+ switch (offset & 0x80000)
+ {
+ case 0x00000:
+ data = m_ram_space->read_word(addr, mem_mask);
+ break;
+
+ case 0x80000:
+ data = m_rom_space->read_word(addr, mem_mask);
+ break;
+ }
+
+ return data;
+}
+
+void psion_asic9_device::mem_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ offset <<= 1;
+
+ offs_t addr = translate_address(offset);
+
+ if (!is_protected(addr))
+ {
+ switch (offset & 0x80000)
+ {
+ case 0x00000:
+ m_ram_space->write_word(addr, 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 & m_a9_interrupt_mask;
+ LOG("%s io_r: A9InterruptStatus => %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)
+ {
+ data = m_snd_fifo.dequeue();
+ if (!m_snd_fifo.full())
+ m_a9_status &= ~0x0800;
+ 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);
+ m_snd_fifo.enqueue(data & 0xff);
+ if (m_snd_fifo.full())
+ m_a9_status |= 0x0800;
+ }
+ 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
+ // Asic2SlaveId 001h; Asic5PackId 002h
+ // Asic5NormalId 003h; Asic6Id 004h
+ // Asic8Id 005h; Asic4Id 006h
+ 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_v30->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_v30->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;
+}