diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index db520225ce0..b2ebdd9f5bb 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1855,6 +1855,18 @@ if (MACHINES["LINFLASH"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/locomo.h,MACHINES["LOCOMO"] = true +--------------------------------------------------- + +if (MACHINES["LOCOMO"]~=null) then + files { + MAME_DIR .. "src/devices/machine/locomo.cpp", + MAME_DIR .. "src/devices/machine/locomo.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/m3002.h,MACHINES["M3002"] = true diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 424f6101943..28bf0d93e66 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -528,6 +528,7 @@ MACHINES["LDP1450"] = true MACHINES["LDVP931"] = true --MACHINES["LH5810"] = true MACHINES["LINFLASH"] = true +--MACHINES["LOCOMO"] = true MACHINES["LPCI"] = true --MACHINES["LSI53C810"] = true --MACHINES["M3002"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index ed649a5cb0c..50f9262c362 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -557,6 +557,7 @@ MACHINES["LDV1000"] = true MACHINES["LDVP931"] = true MACHINES["LH5810"] = true MACHINES["LINFLASH"] = true +MACHINES["LOCOMO"] = true MACHINES["LPCI"] = true MACHINES["LSI53C810"] = true MACHINES["M3002"] = true diff --git a/src/devices/machine/locomo.cpp b/src/devices/machine/locomo.cpp new file mode 100644 index 00000000000..f54f1bedce4 --- /dev/null +++ b/src/devices/machine/locomo.cpp @@ -0,0 +1,376 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/*************************************************************************** + + Sharp LoCoMo peripheral chip emulation + +***************************************************************************/ + +#include "emu.h" +#include "locomo.h" + +#define LOG_UNKNOWN (1 << 1) +#define LOG_READS (1 << 2) +#define LOG_WRITES (1 << 3) +#define LOG_ALL (LOG_UNKNOWN | LOG_READS | LOG_WRITES) + +#define VERBOSE (LOG_ALL) +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(LOCOMO, locomo_device, "locomo", "Sharp LoCoMo Peripheral") + +locomo_device::locomo_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, LOCOMO, tag, owner, clock) +{ +} + +void locomo_device::device_start() +{ + save_item(NAME(m_kbd_cmd)); + save_item(NAME(m_kbd_row)); + save_item(NAME(m_kbd_col)); + save_item(NAME(m_kbd_level)); +} + +void locomo_device::device_reset() +{ + m_kbd_cmd = 0; + m_kbd_row = 0; + m_kbd_col = 0; + m_kbd_level = 0; +} + +uint32_t locomo_device::read(offs_t offset, uint32_t mem_mask) +{ + switch (offset) + { + case 0x00/4: + LOGMASKED(LOG_READS, "%s: read: Version Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x04/4: + LOGMASKED(LOG_READS, "%s: read: Pin Status Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x08/4: + LOGMASKED(LOG_READS, "%s: read: C32K(?) Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x0c/4: + LOGMASKED(LOG_READS, "%s: read: Interrupt Control Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x10/4: + LOGMASKED(LOG_READS, "%s: read: Memory Chip Select 0 Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x14/4: + LOGMASKED(LOG_READS, "%s: read: Memory Chip Select 1 Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x18/4: + LOGMASKED(LOG_READS, "%s: read: Memory Chip Select 2 Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x1c/4: + LOGMASKED(LOG_READS, "%s: read: Memory Chip Select 3 Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x20/4: + LOGMASKED(LOG_READS, "%s: read: A/D Start Delay Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x24/4: + LOGMASKED(LOG_READS, "%s: read: HSYS Delay Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x28/4: + LOGMASKED(LOG_READS, "%s: read: HSYS Period Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x30/4: + LOGMASKED(LOG_READS, "%s: read: Tablet ADC Clock Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x38/4: + LOGMASKED(LOG_READS, "%s: read: TFT Backlight Control Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x3c/4: + LOGMASKED(LOG_READS, "%s: read: TFT CPS Delay Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x40/4: + LOGMASKED(LOG_READS, "%s: read: Keyboard Level Register: %08x & %08x\n", machine().describe_context(), m_kbd_level, mem_mask); + return m_kbd_level; + case 0x44/4: + LOGMASKED(LOG_READS, "%s: read: Keyboard Strobe Control Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x48/4: + LOGMASKED(LOG_READS, "%s: read: Keyboard Strobe Command Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x4c/4: + LOGMASKED(LOG_READS, "%s: read: Keyboard Interrupt Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x54/4: + LOGMASKED(LOG_READS, "%s: read: Audio Clock Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x60/4: + LOGMASKED(LOG_READS, "%s: read: SPI Mode Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x64/4: + LOGMASKED(LOG_READS, "%s: read: SPI Control Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x68/4: + LOGMASKED(LOG_READS, "%s: read: SPI Status Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x70/4: + LOGMASKED(LOG_READS, "%s: read: SPI Interrupt Status Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x74/4: + LOGMASKED(LOG_READS, "%s: read: SPI Interrupt Status Write-Enable Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x78/4: + LOGMASKED(LOG_READS, "%s: read: SPI Interrupt Enable Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x7c/4: + LOGMASKED(LOG_READS, "%s: read: SPI Interrupt Request Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x80/4: + LOGMASKED(LOG_READS, "%s: read: SPI Transmit Data Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x84/4: + LOGMASKED(LOG_READS, "%s: read: SPI Receive Data Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x88/4: + LOGMASKED(LOG_READS, "%s: read: SPI Transmit Shift Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x8c/4: + LOGMASKED(LOG_READS, "%s: read: SPI Receive Shift Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x90/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Direction Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x94/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Input Enable Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x98/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Level Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0x9c/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Output Latch Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xa0/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Rising-Edge Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xa4/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Falling-Edge Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xa8/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Edge-Detect Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xac/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Status Write-Enable Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xb0/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Interrupt Enable Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xb4/4: + LOGMASKED(LOG_READS, "%s: read: GPIO Interrupt Status Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xc8/4: + LOGMASKED(LOG_READS, "%s: read: Front Light Cycle Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xcc/4: + LOGMASKED(LOG_READS, "%s: read: Front Light Duty Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xd8/4: + LOGMASKED(LOG_READS, "%s: read: Long-Time Clock Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xdc/4: + LOGMASKED(LOG_READS, "%s: read: Long-Time Clock Interrupt Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xe0/4: + LOGMASKED(LOG_READS, "%s: read: DAC Control Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xe8/4: + LOGMASKED(LOG_READS, "%s: read: LED 0 Control Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case 0xec/4: + LOGMASKED(LOG_READS, "%s: read: LED 1 Control Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + default: + LOGMASKED(LOG_READS | LOG_UNKNOWN, "%s: read: Unknown Register: %08x & %08x\n", machine().describe_context(), offset << 2, mem_mask); + return 0; + } +} + +void locomo_device::write(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + switch (offset) + { + case 0x00/4: + LOGMASKED(LOG_WRITES, "%s: write: Version Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x04/4: + LOGMASKED(LOG_WRITES, "%s: write: Pin Status Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x08/4: + LOGMASKED(LOG_WRITES, "%s: write: C32K(?) Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x0c/4: + LOGMASKED(LOG_WRITES, "%s: write: Interrupt Control Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x10/4: + LOGMASKED(LOG_WRITES, "%s: write: Memory Chip Select 0 Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x14/4: + LOGMASKED(LOG_WRITES, "%s: write: Memory Chip Select 1 Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x18/4: + LOGMASKED(LOG_WRITES, "%s: write: Memory Chip Select 2 Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x1c/4: + LOGMASKED(LOG_WRITES, "%s: write: Memory Chip Select 3 Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x20/4: + LOGMASKED(LOG_WRITES, "%s: write: A/D Start Delay Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x24/4: + LOGMASKED(LOG_WRITES, "%s: write: HSYS Delay Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x28/4: + LOGMASKED(LOG_WRITES, "%s: write: HSYS Period Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x30/4: + LOGMASKED(LOG_WRITES, "%s: write: Tablet ADC Clock Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x38/4: + LOGMASKED(LOG_WRITES, "%s: write: TFT Backlight Control Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x3c/4: + LOGMASKED(LOG_WRITES, "%s: write: TFT CPS Delay Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x40/4: + LOGMASKED(LOG_WRITES, "%s: write: Keyboard Level Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x44/4: + LOGMASKED(LOG_WRITES, "%s: write: Keyboard Strobe Control Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + if (m_kbd_cmd == 1) + { + if (m_kbd_row == 0) + { + m_kbd_row = (uint16_t)data; + } + else if (m_kbd_col == 0) + { + m_kbd_col = (uint16_t)data; + if (m_kbd_col == 0xffff) + { + m_kbd_cmd = 0; + m_kbd_level = 0; + } + else + { + // HACK: Something about this value causes the Zaurus updater to begin booting. + // TODO: Work out the proper keyboard matrix. + m_kbd_level = 0xffff; + } + m_kbd_row = 0; + m_kbd_col = 0; + } + } + break; + case 0x48/4: + { + LOGMASKED(LOG_WRITES, "%s: write: Keyboard Strobe Command Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + const uint16_t old = m_kbd_cmd; + m_kbd_cmd = (uint16_t)data; + if (old == 0) + { + m_kbd_row = 0; + m_kbd_col = 0; + } + break; + } + case 0x4c/4: + LOGMASKED(LOG_WRITES, "%s: write: Keyboard Interrupt Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x54/4: + LOGMASKED(LOG_WRITES, "%s: write: Audio Clock Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x60/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Mode Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x64/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Control Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x68/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Status Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x70/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Interrupt Status Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x74/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Interrupt Status Write-Enable Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x78/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Interrupt Enable Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x7c/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Interrupt Request Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x80/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Transmit Data Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x84/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Receive Data Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x88/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Transmit Shift Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x8c/4: + LOGMASKED(LOG_WRITES, "%s: write: SPI Receive Shift Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x90/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Direction Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x94/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Input Enable Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x98/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Level Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0x9c/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Output Latch Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xa0/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Rising-Edge Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xa4/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Falling-Edge Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xa8/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Edge-Detect Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xac/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Status Write-Enable Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xb0/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Interrupt Enable Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xb4/4: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Interrupt Status Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xc8/4: + LOGMASKED(LOG_WRITES, "%s: write: Front Light Cycle Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xcc/4: + LOGMASKED(LOG_WRITES, "%s: write: Front Light Duty Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xd8/4: + LOGMASKED(LOG_WRITES, "%s: write: Long-Time Clock Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xdc/4: + LOGMASKED(LOG_WRITES, "%s: write: Long-Time Clock Interrupt Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xe0/4: + LOGMASKED(LOG_WRITES, "%s: write: DAC Control Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xe8/4: + LOGMASKED(LOG_WRITES, "%s: write: LED 0 Control Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case 0xec/4: + LOGMASKED(LOG_WRITES, "%s: write: LED 1 Control Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + default: + LOGMASKED(LOG_WRITES | LOG_UNKNOWN, "%s: write: Unknown Register: %08x = %08x & %08x\n", machine().describe_context(), offset << 2, data, mem_mask); + break; + } +} diff --git a/src/devices/machine/locomo.h b/src/devices/machine/locomo.h new file mode 100644 index 00000000000..86d6eeef8fe --- /dev/null +++ b/src/devices/machine/locomo.h @@ -0,0 +1,34 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/*************************************************************************** + + Sharp LoCoMo peripheral chip emulation + +***************************************************************************/ + +#ifndef MAME_MACHINE_LOCOMO +#define MAME_MACHINE_LOCOMO + +#pragma once + +class locomo_device : public device_t +{ +public: + locomo_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + uint32_t read(offs_t offset, uint32_t mem_mask = ~0U); + void write(offs_t offset, uint32_t data, uint32_t mem_mask = ~0U); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + + uint16_t m_kbd_cmd; + uint16_t m_kbd_row; + uint16_t m_kbd_col; + uint16_t m_kbd_level; +}; + +DECLARE_DEVICE_TYPE(LOCOMO, locomo_device) + +#endif // MAME_MACHINE_LOCOMO diff --git a/src/devices/machine/sa1110.cpp b/src/devices/machine/sa1110.cpp index 4a767de0d85..1c79d1a943d 100644 --- a/src/devices/machine/sa1110.cpp +++ b/src/devices/machine/sa1110.cpp @@ -1,18 +1,24 @@ // license:BSD-3-Clause // copyright-holders:Ryan Holtz -/************************************************************************** - * - * Intel XScale SA1110 peripheral emulation - * - **************************************************************************/ +/*************************************************************************** + + Intel XScale SA1110 peripheral emulation + +***************************************************************************/ #include "emu.h" #include "sa1110.h" #define LOG_UNKNOWN (1 << 1) -#define LOG_INTC (1 << 2) -#define LOG_POWER (1 << 3) -#define LOG_ALL (LOG_UNKNOWN | LOG_INTC | LOG_POWER) +#define LOG_UART (1 << 2) +#define LOG_UART_HF (1 << 3) +#define LOG_OSTIMER (1 << 4) +#define LOG_RTC (1 << 5) +#define LOG_POWER (1 << 6) +#define LOG_RESET (1 << 7) +#define LOG_GPIO (1 << 8) +#define LOG_INTC (1 << 9) +#define LOG_ALL (LOG_UNKNOWN | LOG_UART | LOG_OSTIMER | LOG_RTC | LOG_POWER | LOG_RESET | LOG_GPIO | LOG_INTC) #define VERBOSE (LOG_ALL) #include "logmacro.h" @@ -21,90 +27,571 @@ DEFINE_DEVICE_TYPE(SA1110_PERIPHERALS, sa1110_periphs_device, "sa1110_periphs", sa1110_periphs_device::sa1110_periphs_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, SA1110_PERIPHERALS, tag, owner, clock) + , device_serial_interface(mconfig, *this) , m_maincpu(*this, finder_base::DUMMY_TAG) + , m_uart3_irqs(*this, "uart3irq") + , m_gpio_out(*this) { } /* - Intel SA-1110 Interrupt Controller + Intel SA-1110 Serial Port 3 - UART - pg. 81 to 88 Intel StrongARM SA-1110 Microprocessor Developer's Manual + pg. 289 to 306 Intel StrongARM SA-1110 Microprocessor Developer's Manual */ -void sa1110_periphs_device::update_interrupts() +WRITE_LINE_MEMBER(sa1110_periphs_device::uart3_irq_callback) { - m_intc_regs.icfp = (m_intc_regs.icpr & m_intc_regs.icmr) & m_intc_regs.iclr; - m_intc_regs.icip = (m_intc_regs.icpr & m_intc_regs.icmr) & (~m_intc_regs.iclr); - m_maincpu->set_input_line(ARM7_FIRQ_LINE, m_intc_regs.icfp ? ASSERT_LINE : CLEAR_LINE); - m_maincpu->set_input_line(ARM7_IRQ_LINE, m_intc_regs.icip ? ASSERT_LINE : CLEAR_LINE); + set_irq_line(INT_UART3, state); } -void sa1110_periphs_device::set_irq_line(uint32_t line, int irq_state) +// Rx completed receiving byte +void sa1110_periphs_device::rcv_complete() { - m_intc_regs.icpr &= ~line; - m_intc_regs.icpr |= irq_state ? line : 0; - update_interrupts(); } -uint32_t sa1110_periphs_device::intc_r(offs_t offset, uint32_t mem_mask) +// Tx completed sending byte +void sa1110_periphs_device::tra_complete() { - switch (offset) + m_uart_regs.tx_fifo_count--; + m_uart_regs.tx_fifo_read_idx = (m_uart_regs.tx_fifo_read_idx + 1) % ARRAY_LENGTH(m_uart_regs.tx_fifo); + m_uart_regs.utsr1 |= (1 << UTSR1_TNF_BIT); + + if (m_uart_regs.tx_fifo_count) + transmit_register_setup(m_uart_regs.tx_fifo[m_uart_regs.tx_fifo_read_idx]); + else + m_uart_regs.utsr1 &= ~(1 << UTSR1_TBY_BIT); + + uart_check_tx_fifo_service(); +} + +// Tx send bit +void sa1110_periphs_device::tra_callback() +{ + transmit_register_get_data_bit(); +} + +void sa1110_periphs_device::uart_recalculate_divisor() +{ + // TODO: Handle external UART clocking + const int multiplier = (((m_uart_regs.utcr[1] & 0x0f) << 8) | (m_uart_regs.utcr[2] & 0xff)) + 1; + set_rcv_rate(INTERNAL_OSC, multiplier * 16); + set_tra_rate(INTERNAL_OSC, multiplier * 16); + + receive_register_reset(); + transmit_register_reset(); +} + +void sa1110_periphs_device::uart_update_eif_status() +{ + bool has_error = false; + for (int i = 0; i < 4 && i < m_uart_regs.rx_fifo_count; i++) { - case REG_ICIP: - LOGMASKED(LOG_INTC, "sa1110 intc_r: Interrupt Controller IRQ Pending Register: %08x & %08x\n", m_intc_regs.icip, mem_mask); - return m_intc_regs.icip; - case REG_ICMR: - LOGMASKED(LOG_INTC, "sa1110 intc_r: Interrupt Controller Mask Register: %08x & %08x\n", m_intc_regs.icmr, mem_mask); - return m_intc_regs.icmr; - case REG_ICLR: - LOGMASKED(LOG_INTC, "sa1110 intc_r: Interrupt Controller Level Register: %08x & %08x\n", m_intc_regs.iclr, mem_mask); - return m_intc_regs.iclr; - case REG_ICFP: - LOGMASKED(LOG_INTC, "sa1110 intc_r: Interrupt Controller FIQ Pending Register: %08x & %08x\n", m_intc_regs.icfp, mem_mask); - return m_intc_regs.icfp; - case REG_ICPR: - LOGMASKED(LOG_INTC, "sa1110 intc_r: Interrupt Controller Pending Register: %08x & %08x\n", m_intc_regs.icpr, mem_mask); - return m_intc_regs.icpr; - case REG_ICCR: - LOGMASKED(LOG_INTC, "sa1110 intc_r: Interrupt Controller Control Register: %08x & %08x\n", m_intc_regs.iccr, mem_mask); - return m_intc_regs.iccr; - default: - LOGMASKED(LOG_INTC | LOG_UNKNOWN, "sa1110 intc_r: Unknown address: %08x\n", INTC_BASE_ADDR | (offset << 2)); + const int read_idx = (m_uart_regs.rx_fifo_read_idx + i) % ARRAY_LENGTH(m_uart_regs.rx_fifo); + if (m_uart_regs.rx_fifo[read_idx] & 0x700) + { + has_error = true; break; + } + } + + if (has_error) + { + m_uart_regs.utsr0 |= (1 << UTSR0_EIF_BIT); + m_uart3_irqs->in_w(1); + } + else + { + m_uart_regs.utsr0 &= ~(1 << UTSR0_EIF_BIT); + m_uart3_irqs->in_w(0); } - return 0; } -void sa1110_periphs_device::intc_w(offs_t offset, uint32_t data, uint32_t mem_mask) +uint8_t sa1110_periphs_device::uart_read_receive_fifo() +{ + const uint8_t data = m_uart_regs.rx_fifo[m_uart_regs.rx_fifo_read_idx]; + if (m_uart_regs.rx_fifo_count) + { + m_uart_regs.rx_fifo_read_idx = (m_uart_regs.rx_fifo_read_idx + 1) % ARRAY_LENGTH(m_uart_regs.rx_fifo); + m_uart_regs.rx_fifo_count--; + if (m_uart_regs.rx_fifo_count) + { + const uint16_t fifo_bottom_flags = ((m_uart_regs.rx_fifo[m_uart_regs.rx_fifo_read_idx]) >> 8) & 7; + m_uart_regs.utsr1 &= ~((1 << UTSR1_PRE_BIT) | (1 << UTSR1_FRE_BIT) | (1 << UTSR1_ROR_BIT)); + m_uart_regs.utsr1 |= fifo_bottom_flags << UTSR1_PRE_BIT; + } + } + return data; +} + +void sa1110_periphs_device::uart_write_transmit_fifo(uint8_t data) +{ + if (m_uart_regs.tx_fifo_count >= ARRAY_LENGTH(m_uart_regs.tx_fifo)) + return; + if (!BIT(m_uart_regs.utcr[3], UTCR3_TXE_BIT)) + return; + + // immediately start transmitting if FIFO is empty + if (m_uart_regs.tx_fifo_count == 0) + { + m_uart_regs.utsr1 |= (1 << UTSR1_TBY_BIT); + transmit_register_setup(data); + } + + // fill FIFO entry + m_uart_regs.tx_fifo[m_uart_regs.tx_fifo_write_idx] = data; + m_uart_regs.tx_fifo_count++; + m_uart_regs.tx_fifo_write_idx = (m_uart_regs.tx_fifo_write_idx + 1) % ARRAY_LENGTH(m_uart_regs.tx_fifo); + + // update transmitter-not-full flag + if (m_uart_regs.tx_fifo_count == ARRAY_LENGTH(m_uart_regs.tx_fifo)) + m_uart_regs.utsr1 &= ~(1 << UTSR1_TNF_BIT); + else + m_uart_regs.utsr1 |= (1 << UTSR1_TNF_BIT); + + // update FIFO-service interrupt + uart_check_tx_fifo_service(); +} + +void sa1110_periphs_device::uart_check_tx_fifo_service() +{ + if (m_uart_regs.tx_fifo_count <= 4 && BIT(m_uart_regs.utcr[3], UTCR3_TXE_BIT)) + { + m_uart_regs.utsr0 |= (1 << UTSR0_TFS_BIT); + if (BIT(m_uart_regs.utcr[3], UTCR3_TIE_BIT)) + { + m_uart3_irqs->in_w(1); + } + } + else + { + m_uart_regs.utsr0 &= ~(1 << UTSR0_TFS_BIT); + m_uart3_irqs->in_w(0); + } +} + +void sa1110_periphs_device::uart_set_receiver_idle() +{ +} + +void sa1110_periphs_device::uart_begin_of_break() +{ +} + +void sa1110_periphs_device::uart_end_of_break() +{ +} + +void sa1110_periphs_device::uart_set_receiver_enabled(bool enabled) +{ +} + +void sa1110_periphs_device::uart_set_transmitter_enabled(bool enabled) +{ + if (enabled) + { + m_uart_regs.utsr0 |= (1 << UTSR0_TFS_BIT); + m_uart3_irqs->in_w(1); + + m_uart_regs.utsr1 |= (1 << UTSR1_TNF_BIT); + } + else + { + m_uart_regs.utsr0 &= ~(1 << UTSR0_TFS_BIT); + m_uart3_irqs->in_w(0); + + m_uart_regs.utsr1 &= ~(1 << UTSR1_TBY_BIT); + m_uart_regs.utsr1 &= ~(1 << UTSR1_TNF_BIT); + + m_uart_regs.tx_fifo_count = 0; + m_uart_regs.tx_fifo_read_idx = 0; + m_uart_regs.tx_fifo_write_idx = 0; + + transmit_register_reset(); + } +} + +void sa1110_periphs_device::uart_set_receive_irq_enabled(bool enabled) +{ +} + +void sa1110_periphs_device::uart_set_transmit_irq_enabled(bool enabled) +{ +} + +uint32_t sa1110_periphs_device::uart3_r(offs_t offset, uint32_t mem_mask) { switch (offset) { - case REG_ICIP: - LOGMASKED(LOG_INTC, "sa1110 intc_w: (Invalid Write) Interrupt Controller IRQ Pending Register: %08x & %08x\n", data, mem_mask); - break; - case REG_ICMR: - LOGMASKED(LOG_INTC, "sa1110 intc_w: Interrupt Controller Mask Register: %08x & %08x\n", data, mem_mask); - COMBINE_DATA(&m_intc_regs.icmr); - break; - case REG_ICLR: - LOGMASKED(LOG_INTC, "sa1110 intc_w: Interrupt Controller Level Register: %08x & %08x\n", data, mem_mask); - COMBINE_DATA(&m_intc_regs.iclr); - break; - case REG_ICFP: - LOGMASKED(LOG_INTC, "sa1110 intc_w: (Invalid Write) Interrupt Controller FIQ Pending Register: %08x & %08x\n", data, mem_mask); - break; - case REG_ICPR: - LOGMASKED(LOG_INTC, "sa1110_intc_w: (Invalid Write) Interrupt Controller Pending Register: %08x & %08x\n", data, mem_mask); - break; - case REG_ICCR: - LOGMASKED(LOG_INTC, "sa1110 intc_w: Interrupt Controller Control Register: %08x & %08x\n", data, mem_mask); - m_intc_regs.iccr = BIT(data, 0); - break; - default: - LOGMASKED(LOG_INTC | LOG_UNKNOWN, "sa1110 intc_w: Unknown address: %08x = %08x & %08x\n", INTC_BASE_ADDR | (offset << 2), data, mem_mask); - break; + case REG_UTCR0: + LOGMASKED(LOG_UART, "%s: uart3_r: UART Control Register 0: %08x & %08x\n", machine().describe_context(), m_uart_regs.utcr[0], mem_mask); + return m_uart_regs.utcr[0]; + case REG_UTCR1: + LOGMASKED(LOG_UART, "%s: uart3_r: UART Control Register 1: %08x & %08x\n", machine().describe_context(), m_uart_regs.utcr[1], mem_mask); + return m_uart_regs.utcr[1]; + case REG_UTCR2: + LOGMASKED(LOG_UART, "%s: uart3_r: UART Control Register 2: %08x & %08x\n", machine().describe_context(), m_uart_regs.utcr[2], mem_mask); + return m_uart_regs.utcr[2]; + case REG_UTCR3: + LOGMASKED(LOG_UART, "%s: uart3_r: UART Control Register 3: %08x & %08x\n", machine().describe_context(), m_uart_regs.utcr[3], mem_mask); + return m_uart_regs.utcr[3]; + case REG_UTDR: + { + const uint8_t data = uart_read_receive_fifo(); + LOGMASKED(LOG_UART, "%s: uart3_r: UART Data Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + return data; + } + case REG_UTSR0: + LOGMASKED(LOG_UART_HF, "%s: uart3_r: UART Status Register 0: %08x & %08x\n", machine().describe_context(), m_uart_regs.utsr0, mem_mask); + return m_uart_regs.utsr0; + case REG_UTSR1: + LOGMASKED(LOG_UART, "%s: uart3_r: UART Status Register 1: %08x & %08x\n", machine().describe_context(), m_uart_regs.utsr1, mem_mask); + return m_uart_regs.utsr1; + default: + LOGMASKED(LOG_UART | LOG_UNKNOWN, "%s: uart3_r: Unknown address: %08x & %08x\n", machine().describe_context(), UART_BASE_ADDR | (offset << 2), mem_mask); + return 0; + } +} + +void sa1110_periphs_device::uart3_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + switch (offset) + { + case REG_UTCR0: + { + LOGMASKED(LOG_UART, "%s: uart3_w: UART Control Register 0: %08x & %08x\n", machine().describe_context(), data, mem_mask); + LOGMASKED(LOG_UART, "%s: Parity Enable: %d\n", machine().describe_context(), BIT(data, 0)); + LOGMASKED(LOG_UART, "%s: Parity Mode: %s\n", machine().describe_context(), BIT(data, 1) ? "Even" : "Odd"); + LOGMASKED(LOG_UART, "%s: Stop Bits: %d\n", machine().describe_context(), BIT(data, 2) + 1); + LOGMASKED(LOG_UART, "%s: Data Size: %d\n", machine().describe_context(), BIT(data, 3) ? 8 : 7); + LOGMASKED(LOG_UART, "%s: Sample Clock: %s\n", machine().describe_context(), BIT(data, 4) ? "External" : "Internal"); + LOGMASKED(LOG_UART, "%s: Receive Edge: %s\n", machine().describe_context(), BIT(data, 5) ? "Falling" : "Rising"); + LOGMASKED(LOG_UART, "%s: Transmit Edge: %s\n", machine().describe_context(), BIT(data, 6) ? "Falling" : "Rising"); + + stop_bits_t stop_bits = (BIT(data, 2) ? STOP_BITS_2 : STOP_BITS_1); + + parity_t parity = PARITY_NONE; + if (BIT(data, 0)) + { + parity = (BIT(data, 1) ? PARITY_EVEN : PARITY_ODD); + } + + set_data_frame(1, BIT(data, 3) ? 8 : 7, parity, stop_bits); + receive_register_reset(); + transmit_register_reset(); + + COMBINE_DATA(&m_uart_regs.utcr[0]); + break; + } + case REG_UTCR1: + { + LOGMASKED(LOG_UART, "%s: uart3_w: UART Control Register 1: %08x & %08x\n", machine().describe_context(), data, mem_mask); + LOGMASKED(LOG_UART, "%s: Baud Rate Divisor MSB: %02x\n", machine().describe_context(), data & 0x0f); + const uint8_t old = m_uart_regs.utcr[1] & 0x0f; + COMBINE_DATA(&m_uart_regs.utcr[1]); + if ((m_uart_regs.utcr[1] & 0x0f) != old) + uart_recalculate_divisor(); + break; + } + case REG_UTCR2: + { + LOGMASKED(LOG_UART, "%s: uart3_w: UART Control Register 2: %08x & %08x\n", machine().describe_context(), data, mem_mask); + LOGMASKED(LOG_UART, "%s: Baud Rate Divisor LSB: %02x\n", machine().describe_context(), (uint8_t)data); + const uint8_t old = m_uart_regs.utcr[2] & 0xff; + COMBINE_DATA(&m_uart_regs.utcr[2]); + if ((m_uart_regs.utcr[2] & 0xff) != old) + uart_recalculate_divisor(); + break; + } + case REG_UTCR3: + { + LOGMASKED(LOG_UART, "%s: uart3_w: UART Control Register 3: %08x & %08x\n", machine().describe_context(), data, mem_mask); + LOGMASKED(LOG_UART, "%s: Receive Enable: %d\n", machine().describe_context(), BIT(data, 0)); + LOGMASKED(LOG_UART, "%s: Transmit Enable: %d\n", machine().describe_context(), BIT(data, 1)); + LOGMASKED(LOG_UART, "%s: Send Break: %d\n", machine().describe_context(), BIT(data, 2)); + LOGMASKED(LOG_UART, "%s: Receive FIFO IRQ Enable: %d\n", machine().describe_context(), BIT(data, 3)); + LOGMASKED(LOG_UART, "%s: Transmit FIFO IRQ Enable: %d\n", machine().describe_context(), BIT(data, 4)); + LOGMASKED(LOG_UART, "%s: Loopback Enable: %d\n", machine().describe_context(), BIT(data, 5)); + const uint32_t old = m_uart_regs.utcr[3]; + COMBINE_DATA(&m_uart_regs.utcr[3]); + const uint32_t changed = old ^ m_uart_regs.utcr[3]; + if (BIT(changed, 0)) + uart_set_receiver_enabled(BIT(data, 0)); + if (BIT(changed, 1)) + uart_set_transmitter_enabled(BIT(data, 1)); + if (BIT(changed, 3)) + uart_set_receive_irq_enabled(BIT(data, 3)); + if (BIT(changed, 4)) + uart_set_transmit_irq_enabled(BIT(data, 4)); + break; + } + case REG_UTDR: + LOGMASKED(LOG_UART, "%s: uart3_w: UART Data Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + if (data == 0x0d || data == 0x0a || (data >= 0x20 && data < 0x7f)) + { + printf("%c", (char)data); + } + uart_write_transmit_fifo((uint8_t)data); + break; + case REG_UTSR0: + LOGMASKED(LOG_UART, "%s: uart3_w: UART Status Register 0: %08x & %08x\n", machine().describe_context(), data, mem_mask); + LOGMASKED(LOG_UART, "%s: Receiver Idle Status: %d\n", machine().describe_context(), BIT(data, 2)); + LOGMASKED(LOG_UART, "%s: Receiver Begin of Break Status: %d\n", machine().describe_context(), BIT(data, 3)); + LOGMASKED(LOG_UART, "%s: Receiver End of Break Status: %d\n", machine().describe_context(), BIT(data, 4)); + if (BIT(data, 2)) + uart_set_receiver_idle(); + if (BIT(data, 3)) + uart_begin_of_break(); + if (BIT(data, 4)) + uart_end_of_break(); + break; + default: + LOGMASKED(LOG_UART | LOG_UNKNOWN, "%s: uart3_w: Unknown address: %08x = %08x & %08x\n", machine().describe_context(), UART_BASE_ADDR | (offset << 2), data, mem_mask); + break; + } +} + +/* + + Intel SA-1110 Operating System Timer + + pg. 92 to 96 Intel StrongARM SA-1110 Microprocessor Developer's Manual + +*/ + +TIMER_CALLBACK_MEMBER(sa1110_periphs_device::ostimer_tick_cb) +{ + const int channel = param; + if (BIT(m_ostmr_regs.oier, channel)) + { + m_ostmr_regs.ossr |= (1 << channel); + set_irq_line(INT_OSTIMER0 + channel, 1); + // TODO: Handle Channel 3, watchdog timer mode + } +} + +void sa1110_periphs_device::ostimer_update_count() +{ + const attotime time_delta = machine().time() - m_ostmr_regs.last_count_sync; + const uint64_t ticks_elapsed = time_delta.as_ticks(INTERNAL_OSC); + const uint32_t wrapped_ticks = (uint32_t)ticks_elapsed; + m_ostmr_regs.oscr += wrapped_ticks; + m_ostmr_regs.last_count_sync = machine().time(); +} + +void sa1110_periphs_device::ostimer_update_match_timer(int channel) +{ + uint64_t ticks_remaining = m_ostmr_regs.osmr[channel] - m_ostmr_regs.oscr; + if (m_ostmr_regs.oscr >= m_ostmr_regs.osmr[channel]) + ticks_remaining += 0x100000000ULL; + m_ostmr_regs.timer[channel]->adjust(attotime::from_ticks(ticks_remaining, INTERNAL_OSC), channel); +} + +uint32_t sa1110_periphs_device::ostimer_r(offs_t offset, uint32_t mem_mask) +{ + ostimer_update_count(); + + switch (offset) + { + case REG_OSMR0: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_r: OS Timer Match Register 0: %08x & %08x\n", machine().describe_context(), m_ostmr_regs.osmr[0], mem_mask); + return m_ostmr_regs.osmr[0]; + case REG_OSMR1: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_r: OS Timer Match Register 1: %08x & %08x\n", machine().describe_context(), m_ostmr_regs.osmr[1], mem_mask); + return m_ostmr_regs.osmr[1]; + case REG_OSMR2: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_r: OS Timer Match Register 2: %08x & %08x\n", machine().describe_context(), m_ostmr_regs.osmr[2], mem_mask); + return m_ostmr_regs.osmr[2]; + case REG_OSMR3: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_r: OS Timer Match Register 3: %08x & %08x\n", machine().describe_context(), m_ostmr_regs.osmr[3], mem_mask); + return m_ostmr_regs.osmr[3]; + case REG_OSCR: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_r: OS Timer Counter Register: %08x & %08x\n", machine().describe_context(), m_ostmr_regs.oscr, mem_mask); + return m_ostmr_regs.oscr; + case REG_OSSR: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_r: OS Timer Status Register: %08x & %08x\n", machine().describe_context(), m_ostmr_regs.ossr, mem_mask); + return m_ostmr_regs.ossr; + case REG_OWER: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_r: OS Timer Watchdog Enable Register: %08x & %08x\n", machine().describe_context(), m_ostmr_regs.ower, mem_mask); + return m_ostmr_regs.ower; + case REG_OIER: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_r: OS Timer Interrupt Enable Register: %08x & %08x\n", machine().describe_context(), m_ostmr_regs.oier, mem_mask); + return m_ostmr_regs.oier; + default: + LOGMASKED(LOG_OSTIMER | LOG_UNKNOWN, "%s: ostimer_r: Unknown address: %08x & %08x\n", machine().describe_context(), OSTMR_BASE_ADDR | (offset << 2), mem_mask); + return 0; + } +} + +void sa1110_periphs_device::ostimer_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + ostimer_update_count(); + + switch (offset) + { + case REG_OSMR0: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_w: OS Timer Match Register 0 = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_ostmr_regs.osmr[0]); + ostimer_update_match_timer(0); + break; + case REG_OSMR1: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_w: OS Timer Match Register 1 = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_ostmr_regs.osmr[1]); + ostimer_update_match_timer(1); + break; + case REG_OSMR2: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_w: OS Timer Match Register 2 = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_ostmr_regs.osmr[2]); + ostimer_update_match_timer(2); + break; + case REG_OSMR3: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_w: OS Timer Match Register 3 = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_ostmr_regs.osmr[3]); + ostimer_update_match_timer(3); + break; + case REG_OSCR: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_w: OS Timer Counter Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_ostmr_regs.oscr); + m_ostmr_regs.last_count_sync = machine().time(); + for (int channel = 0; channel < 4; channel++) + { + if (m_ostmr_regs.oscr == m_ostmr_regs.osmr[channel] && BIT(m_ostmr_regs.oier, channel)) + { + if (!BIT(m_ostmr_regs.ossr, channel)) + { + m_ostmr_regs.ossr |= (1 << channel); + set_irq_line(INT_OSTIMER0 + channel, 1); + } + } + else + { + ostimer_update_match_timer(channel); + } + } + break; + case REG_OSSR: + { + LOGMASKED(LOG_OSTIMER, "%s: ostimer_w: OS Timer Status Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + const uint32_t old = m_ostmr_regs.ossr; + m_ostmr_regs.ossr &= ~(data & mem_mask); + if (old != m_ostmr_regs.ossr) + { + for (int channel = 0; channel < 4; channel++) + { + if (BIT(old, channel)) + set_irq_line(INT_OSTIMER0 + channel, 0); + } + } + break; + } + case REG_OWER: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_w: OS Timer Watchdog Enable Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + if (!m_ostmr_regs.ower) + { + m_ostmr_regs.ower = data & mem_mask & 1; + } + break; + case REG_OIER: + LOGMASKED(LOG_OSTIMER, "%s: ostimer_w: OS Timer Interrupt Enable Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_ostmr_regs.oier); + break; + default: + LOGMASKED(LOG_OSTIMER | LOG_UNKNOWN, "%s: ostimer_w: Unknown address: %08x = %08x & %08x\n", machine().describe_context(), OSTMR_BASE_ADDR | (offset << 2), + data, mem_mask); + break; + } +} + +/* + + Intel SA-1110 Real-Time Clock + + pg. 88 to 92 Intel StrongARM SA-1110 Microprocessor Developer's Manual + +*/ + +TIMER_CALLBACK_MEMBER(sa1110_periphs_device::rtc_tick_cb) +{ + m_rtc_regs.rcnr++; + m_rtc_regs.rtsr |= (1 << RTSR_HZ_BIT); + + if (m_rtc_regs.rcnr == m_rtc_regs.rtar) + { + m_rtc_regs.rtsr |= (1 << RTSR_AL_BIT); + if (BIT(m_rtc_regs.rtsr, RTSR_ALE_BIT)) + set_irq_line(INT_RTC_ALARM, 1); + } + + if (BIT(m_rtc_regs.rtsr, RTSR_HZE_BIT)) + set_irq_line(INT_RTC_TICK, 1); +} + +uint32_t sa1110_periphs_device::rtc_r(offs_t offset, uint32_t mem_mask) +{ + switch (offset) + { + case REG_RTAR: + LOGMASKED(LOG_RTC, "%s: rtc_r: RTC Alarm Register: %08x & %08x\n", machine().describe_context(), m_rtc_regs.rtar, mem_mask); + return m_rtc_regs.rtar; + case REG_RCNR: + LOGMASKED(LOG_RTC, "%s: rtc_r: RTC Count Register: %08x & %08x\n", machine().describe_context(), m_rtc_regs.rcnr, mem_mask); + return m_rtc_regs.rcnr; + case REG_RTTR: + LOGMASKED(LOG_RTC, "%s: rtc_r: RTC Timer Trim Register: %08x & %08x\n", machine().describe_context(), m_rtc_regs.rttr, mem_mask); + return m_rtc_regs.rttr; + case REG_RTSR: + LOGMASKED(LOG_RTC, "%s: rtc_r: RTC Status Register: %08x & %08x\n", machine().describe_context(), m_rtc_regs.rtsr, mem_mask); + return m_rtc_regs.rtsr; + default: + LOGMASKED(LOG_RTC | LOG_UNKNOWN, "%s: reset_r: Unknown address: %08x & %08x\n", machine().describe_context(), RTC_BASE_ADDR | (offset << 2), mem_mask); + return 0; + } +} + +void sa1110_periphs_device::rtc_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + switch (offset) + { + case REG_RTAR: + LOGMASKED(LOG_RTC, "%s: rtc_w: RTC Alarm Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_rtc_regs.rtar); + break; + case REG_RCNR: + LOGMASKED(LOG_RTC, "%s: rtc_w: RTC Count Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_rtc_regs.rcnr); + break; + case REG_RTTR: + LOGMASKED(LOG_RTC, "%s: rtc_w: RTC Timer Trim Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_rtc_regs.rttr); + // TODO: Implement timer trimming + break; + case REG_RTSR: + { + LOGMASKED(LOG_RTC, "%s: rtc_w: RTC Count Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + + const uint32_t old = m_rtc_regs.rtsr; + const bool old_alarm_int = BIT(old, RTSR_AL_MASK) && BIT(m_rtc_regs.rtsr, RTSR_ALE_MASK); + const bool old_tick_int = BIT(old, RTSR_HZ_MASK) && BIT(m_rtc_regs.rtsr, RTSR_HZE_MASK); + + m_rtc_regs.rtsr &= ~(data & (RTSR_AL_MASK | RTSR_HZ_MASK) & mem_mask); + m_rtc_regs.rtsr &= ~(RTSR_ALE_MASK | RTSR_HZE_MASK); + m_rtc_regs.rtsr |= (data & (RTSR_ALE_MASK | RTSR_HZE_MASK) & mem_mask); + + const bool new_alarm_int = BIT(m_rtc_regs.rtsr, RTSR_AL_MASK) && BIT(m_rtc_regs.rtsr, RTSR_ALE_MASK); + const bool new_tick_int = BIT(m_rtc_regs.rtsr, RTSR_HZ_MASK) && BIT(m_rtc_regs.rtsr, RTSR_HZE_MASK); + + if (old_alarm_int != new_alarm_int) + set_irq_line(INT_RTC_ALARM, (int)new_alarm_int); + if (old_tick_int != new_tick_int) + set_irq_line(INT_RTC_TICK, (int)new_tick_int); + break; + } + default: + LOGMASKED(LOG_RTC | LOG_UNKNOWN, "%s: reset_w: Unknown address: %08x = %08x & %08x\n", machine().describe_context(), RTC_BASE_ADDR | (offset << 2), + data, mem_mask); + break; } } @@ -120,108 +607,531 @@ uint32_t sa1110_periphs_device::power_r(offs_t offset, uint32_t mem_mask) { switch (offset) { - case REG_PMCR: - LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Control Register: %08x\n", machine().describe_context(), m_power_regs.pmcr); - return m_power_regs.pmcr; - case REG_PSSR: - LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Sleep Status Register: %08x\n", machine().describe_context(), m_power_regs.pssr); - return m_power_regs.pssr; - case REG_PSPR: - LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Scratch Pad Register: %08x\n", machine().describe_context(), m_power_regs.pspr); - return m_power_regs.pspr; - case REG_PWER: - LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Wake-up Enable Register: %08x\n", machine().describe_context(), m_power_regs.pwer); - return m_power_regs.pwer; - case REG_PCFR: - LOGMASKED(LOG_POWER, "%s: power_r: Power Manager General Configuration Register: %08x\n", machine().describe_context(), m_power_regs.pcfr); - return m_power_regs.pcfr; - case REG_PPCR: - LOGMASKED(LOG_POWER, "%s: power_r: Power Manager PLL Configuration Register: %08x\n", machine().describe_context(), m_power_regs.ppcr); - return m_power_regs.ppcr; - case REG_PGSR: - LOGMASKED(LOG_POWER, "%s: power_r: Power Manager GPIO Sleep State Register: %08x\n", machine().describe_context(), m_power_regs.pgsr); - return m_power_regs.pgsr; - case REG_POSR: - LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Oscillator Status Register: %08x\n", machine().describe_context(), m_power_regs.posr); - return m_power_regs.posr; - default: - LOGMASKED(LOG_POWER | LOG_UNKNOWN, "%s: power_r: Unknown address: %08x\n", machine().describe_context(), POWER_BASE_ADDR | (offset << 2)); - break; + case REG_PMCR: + LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Control Register: %08x & %08x\n", machine().describe_context(), m_power_regs.pmcr, mem_mask); + return m_power_regs.pmcr; + case REG_PSSR: + LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Sleep Status Register: %08x & %08x\n", machine().describe_context(), m_power_regs.pssr, mem_mask); + return m_power_regs.pssr; + case REG_PSPR: + LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Scratch Pad Register: %08x & %08x\n", machine().describe_context(), m_power_regs.pspr, mem_mask); + return m_power_regs.pspr; + case REG_PWER: + LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Wake-up Enable Register: %08x & %08x\n", machine().describe_context(), m_power_regs.pwer, mem_mask); + return m_power_regs.pwer; + case REG_PCFR: + LOGMASKED(LOG_POWER, "%s: power_r: Power Manager General Configuration Register: %08x & %08x\n", machine().describe_context(), m_power_regs.pcfr, mem_mask); + return m_power_regs.pcfr; + case REG_PPCR: + LOGMASKED(LOG_POWER, "%s: power_r: Power Manager PLL Configuration Register: %08x & %08x\n", machine().describe_context(), m_power_regs.ppcr, mem_mask); + return m_power_regs.ppcr; + case REG_PGSR: + LOGMASKED(LOG_POWER, "%s: power_r: Power Manager GPIO Sleep State Register: %08x & %08x\n", machine().describe_context(), m_power_regs.pgsr, mem_mask); + return m_power_regs.pgsr; + case REG_POSR: + LOGMASKED(LOG_POWER, "%s: power_r: Power Manager Oscillator Status Register: %08x & %08x\n", machine().describe_context(), m_power_regs.posr, mem_mask); + return m_power_regs.posr; + default: + LOGMASKED(LOG_POWER | LOG_UNKNOWN, "%s: power_r: Unknown address: %08x & %08x\n", machine().describe_context(), POWER_BASE_ADDR | (offset << 2), mem_mask); + return 0; } - return 0; } void sa1110_periphs_device::power_w(offs_t offset, uint32_t data, uint32_t mem_mask) { switch (offset) { - case REG_PMCR: - LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Control Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); - COMBINE_DATA(&m_power_regs.pmcr); - break; - case REG_PSSR: - LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Sleep Status Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); - m_power_regs.pssr &= ~(data & 0x0000001f); - break; - case REG_PSPR: - LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Scratch Pad Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); - COMBINE_DATA(&m_power_regs.pspr); - break; - case REG_PWER: - LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Wake-Up Enable Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); - COMBINE_DATA(&m_power_regs.pwer); - break; - case REG_PCFR: - LOGMASKED(LOG_POWER, "%s: power_w: Power Manager General Configuration Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); - COMBINE_DATA(&m_power_regs.pcfr); - break; - case REG_PPCR: - LOGMASKED(LOG_POWER, "%s: power_w: Power Manager PLL Configuration Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); - COMBINE_DATA(&m_power_regs.ppcr); - break; - case REG_PGSR: - LOGMASKED(LOG_POWER, "%s: power_w: Power Manager GPIO Sleep State Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); - COMBINE_DATA(&m_power_regs.pgsr); - break; - case REG_POSR: - LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Oscillator Status Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); - break; - default: - LOGMASKED(LOG_POWER | LOG_UNKNOWN, "%s: power_w: Unknown address: %08x = %08x & %08x\n", machine().describe_context(), POWER_BASE_ADDR | (offset << 2), - data, mem_mask); - break; + case REG_PMCR: + LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Control Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_power_regs.pmcr); + break; + case REG_PSSR: + LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Sleep Status Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + m_power_regs.pssr &= ~(data & 0x0000001f); + break; + case REG_PSPR: + LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Scratch Pad Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_power_regs.pspr); + break; + case REG_PWER: + LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Wake-Up Enable Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_power_regs.pwer); + break; + case REG_PCFR: + LOGMASKED(LOG_POWER, "%s: power_w: Power Manager General Configuration Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_power_regs.pcfr); + break; + case REG_PPCR: + LOGMASKED(LOG_POWER, "%s: power_w: Power Manager PLL Configuration Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_power_regs.ppcr); + break; + case REG_PGSR: + LOGMASKED(LOG_POWER, "%s: power_w: Power Manager GPIO Sleep State Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_power_regs.pgsr); + break; + case REG_POSR: + LOGMASKED(LOG_POWER, "%s: power_w: Power Manager Oscillator Status Register (ignored) = %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + default: + LOGMASKED(LOG_POWER | LOG_UNKNOWN, "%s: power_w: Unknown address: %08x = %08x & %08x\n", machine().describe_context(), POWER_BASE_ADDR | (offset << 2), + data, mem_mask); + break; + } +} + +/* + + Intel SA-1110 Reset Controller + + pg. 112 to 114 Intel StrongARM SA-1110 Microprocessor Developer's Manual + +*/ + +uint32_t sa1110_periphs_device::reset_r(offs_t offset, uint32_t mem_mask) +{ + switch (offset) + { + case REG_RSRR: + LOGMASKED(LOG_RESET, "%s: reset_r: Reset Controller Software Reset Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case REG_RCSR: + LOGMASKED(LOG_RESET, "%s: reset_r: Reset Controller Status Register: %08x & %08x\n", machine().describe_context(), m_rcsr, mem_mask); + return m_rcsr; + default: + LOGMASKED(LOG_RESET | LOG_UNKNOWN, "%s: reset_r: Unknown address: %08x & %08x\n", machine().describe_context(), RESET_BASE_ADDR | (offset << 2), mem_mask); + return 0; + } +} + +void sa1110_periphs_device::reset_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + switch (offset) + { + case REG_RSRR: + LOGMASKED(LOG_RESET, "%s: reset_w: Reset Controller Software Reset Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case REG_RCSR: + LOGMASKED(LOG_RESET, "%s: reset_w: Reset Controller Status Register = %08x & %08x\n", machine().describe_context(), data, mem_mask); + m_rcsr &= ~(data & mem_mask); + break; + default: + LOGMASKED(LOG_RESET | LOG_UNKNOWN, "%s: reset_w: Unknown address: %08x = %08x & %08x\n", machine().describe_context(), RESET_BASE_ADDR | (offset << 2), + data, mem_mask); + break; + } +} + +/* + + Intel SA-1110 GPIO Controller + + pg. 71 to 80 Intel StrongARM SA-1110 Microprocessor Developer's Manual + +*/ + +void sa1110_periphs_device::gpio_in(const uint32_t line, const int state) +{ + const uint32_t mask = (1 << line); + const uint32_t old_latch = m_gpio_regs.input_latch; + m_gpio_regs.input_latch &= ~mask; + m_gpio_regs.input_latch |= (state << line); + + if (old_latch != m_gpio_regs.input_latch && !BIT(m_gpio_regs.gafr, line)) + { + // TODO: The manual is unclear if edge detection functions on both inputs and outputs. + // If it can also function on outputs, remove the GPDR check below. + if (!BIT(m_gpio_regs.gpdr, line) && BIT(m_gpio_regs.any_edge_mask, line)) + { + const uint32_t old_edge = m_gpio_regs.gedr; + if (state && BIT(m_gpio_regs.grer, line)) + m_gpio_regs.gedr |= mask; + if (!state && BIT(m_gpio_regs.gfer, line)) + m_gpio_regs.gedr |= mask; + if (old_edge != m_gpio_regs.gedr) + gpio_update_interrupts(mask); + } + + m_gpio_regs.gplr = (m_gpio_regs.input_latch & ~m_gpio_regs.gafr) | (m_gpio_regs.alt_input_latch & m_gpio_regs.gafr); + } +} + +void sa1110_periphs_device::gpio_update_interrupts(const uint32_t changed_mask) +{ + uint32_t remaining_mask = changed_mask; + for (uint32_t line = 0; line < 11; line++) + { + if (!BIT(remaining_mask, line)) + continue; + + set_irq_line(INT_GPIO0 + line, BIT(m_gpio_regs.gedr, line)); + remaining_mask &= ~(1 << line); + } + + if (!remaining_mask) + return; + + set_irq_line(INT_GPIOHI, (m_gpio_regs.gedr & 0x0ffff800) ? 1 : 0); +} + +void sa1110_periphs_device::gpio_update_direction(const uint32_t old_gpdr) +{ + const uint32_t new_outputs = ~old_gpdr & m_gpio_regs.gpdr & ~m_gpio_regs.gafr; + if (new_outputs) + { + for (uint32_t line = 0; line < 28; line++) + { + if (BIT(new_outputs, line)) + { + m_gpio_out[line](BIT(m_gpio_regs.gplr, line)); + } + } + } + + // TODO: Do we need to check rising/falling edges based on the transition from output to input? +} + +void sa1110_periphs_device::gpio_update_outputs(const uint32_t old_latch, const uint32_t changed) +{ + uint32_t remaining_changed = changed; + + for (uint32_t line = 0; line < 28 && remaining_changed != 0; line++) + { + if (BIT(remaining_changed, line)) + { + m_gpio_out[line](BIT(m_gpio_regs.output_latch, line)); + remaining_changed &= ~(1 << line); + } + } +} + +void sa1110_periphs_device::gpio_update_alternate_pins(const uint32_t changed_mask) +{ + // TODO +} + +uint32_t sa1110_periphs_device::gpio_r(offs_t offset, uint32_t mem_mask) +{ + switch (offset) + { + case REG_GPLR: + LOGMASKED(LOG_GPIO, "%s: gpio_r: GPIO Pin-Level Register: %08x & %08x\n", machine().describe_context(), m_gpio_regs.gplr, mem_mask); + return m_gpio_regs.gplr; + case REG_GPDR: + LOGMASKED(LOG_GPIO, "%s: gpio_r: GPIO Pin Direction Register: %08x & %08x\n", machine().describe_context(), m_gpio_regs.gpdr, mem_mask); + return m_gpio_regs.gpdr; + case REG_GPSR: + LOGMASKED(LOG_GPIO, "%s: gpio_r: GPIO Pin Output Set Register: %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case REG_GPCR: + LOGMASKED(LOG_GPIO, "%s: gpio_r: GPIO Pin Output Clear Register (ignored): %08x & %08x\n", machine().describe_context(), 0, mem_mask); + return 0; + case REG_GRER: + LOGMASKED(LOG_GPIO, "%s: gpio_r: GPIO Rising-Edge Detect Register: %08x & %08x\n", machine().describe_context(), m_gpio_regs.grer, mem_mask); + return m_gpio_regs.grer; + case REG_GFER: + LOGMASKED(LOG_GPIO, "%s: gpio_r: GPIO Falling-Edge Detect Register: %08x & %08x\n", machine().describe_context(), m_gpio_regs.gfer, mem_mask); + return m_gpio_regs.gfer; + case REG_GEDR: + LOGMASKED(LOG_GPIO, "%s: gpio_r: GPIO Edge Detect Status Register: %08x & %08x\n", machine().describe_context(), m_gpio_regs.gedr, mem_mask); + return m_gpio_regs.gedr; + case REG_GAFR: + LOGMASKED(LOG_GPIO, "%s: gpio_r: GPIO Alternate Function Register: %08x & %08x\n", machine().describe_context(), m_gpio_regs.gafr, mem_mask); + return m_gpio_regs.gafr; + default: + LOGMASKED(LOG_GPIO | LOG_UNKNOWN, "%s: gpio_r: Unknown address: %08x & %08x\n", machine().describe_context(), GPIO_BASE_ADDR | (offset << 2), mem_mask); + return 0; + } +} + +void sa1110_periphs_device::gpio_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + switch (offset) + { + case REG_GPLR: + LOGMASKED(LOG_GPIO, "%s: gpio_w: GPIO Pin-Level Register (ignored): %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case REG_GPDR: + { + LOGMASKED(LOG_GPIO, "%s: gpio_w: GPIO Pin Direction Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + const uint32_t old = m_gpio_regs.gpdr; + COMBINE_DATA(&m_gpio_regs.gpdr); + if (old != m_gpio_regs.gpdr) + gpio_update_direction(old); + break; + } + case REG_GPSR: + { + LOGMASKED(LOG_GPIO, "%s: gpio_w: GPIO Pin Output Set Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + const uint32_t old = m_gpio_regs.output_latch; + m_gpio_regs.output_latch |= (data & mem_mask); + const uint32_t changed = ((old ^ m_gpio_regs.output_latch) & m_gpio_regs.gpdr) & ~m_gpio_regs.gafr; + if (changed) + gpio_update_outputs(old, changed); + break; + } + case REG_GPCR: + { + LOGMASKED(LOG_GPIO, "%s: gpio_w: GPIO Pin Output Clear Register (ignored): %08x & %08x\n", machine().describe_context(), data, mem_mask); + const uint32_t old = m_gpio_regs.output_latch; + m_gpio_regs.output_latch &= ~(data & mem_mask); + const uint32_t changed = ((old ^ m_gpio_regs.output_latch) & m_gpio_regs.gpdr) & ~m_gpio_regs.gafr; + if (changed) + gpio_update_outputs(old, changed); + break; + } + case REG_GRER: + LOGMASKED(LOG_GPIO, "%s: gpio_w: GPIO Rising-Edge Detect Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_gpio_regs.grer); + m_gpio_regs.any_edge_mask = m_gpio_regs.grer | m_gpio_regs.gfer; + break; + case REG_GFER: + LOGMASKED(LOG_GPIO, "%s: gpio_w: GPIO Falling-Edge Detect Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + COMBINE_DATA(&m_gpio_regs.gfer); + m_gpio_regs.any_edge_mask = m_gpio_regs.grer | m_gpio_regs.gfer; + break; + case REG_GEDR: + { + LOGMASKED(LOG_GPIO, "%s: gpio_w: GPIO Edge Detect Status Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + const uint32_t old = m_gpio_regs.gedr; + m_gpio_regs.gedr &= ~(data & mem_mask); + if (old != m_gpio_regs.gedr) + gpio_update_interrupts(old); + break; + } + case REG_GAFR: + { + LOGMASKED(LOG_GPIO, "%s: gpio_w: GPIO Alternate Function Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + const uint32_t old = m_gpio_regs.gafr; + COMBINE_DATA(&m_gpio_regs.gafr); + if (old != m_gpio_regs.gafr) + gpio_update_alternate_pins(old); + break; + } + default: + LOGMASKED(LOG_GPIO | LOG_UNKNOWN, "%s: gpio_w: Unknown address: %08x = %08x & %08x\n", machine().describe_context(), GPIO_BASE_ADDR | (offset << 2), data, mem_mask); + break; + } +} + +/* + + Intel SA-1110 Interrupt Controller + + pg. 81 to 88 Intel StrongARM SA-1110 Microprocessor Developer's Manual + +*/ + +void sa1110_periphs_device::set_irq_line(uint32_t line, int irq_state) +{ + const uint32_t line_mask = (1 << line); + const uint32_t old_status = m_intc_regs.icpr; + m_intc_regs.icpr &= ~line_mask; + m_intc_regs.icpr |= irq_state ? line_mask : 0; + + if (m_intc_regs.icpr == old_status) + return; + + update_interrupts(); +} + +void sa1110_periphs_device::update_interrupts() +{ + const uint32_t old_fiq = m_intc_regs.icfp; + m_intc_regs.icfp = (m_intc_regs.icpr & m_intc_regs.icmr) & m_intc_regs.iclr; + if (old_fiq != m_intc_regs.icfp) + { + m_maincpu->set_input_line(ARM7_FIRQ_LINE, m_intc_regs.icfp ? ASSERT_LINE : CLEAR_LINE); + } + + const uint32_t old_irq = m_intc_regs.icip; + m_intc_regs.icip = (m_intc_regs.icpr & m_intc_regs.icmr) & (~m_intc_regs.iclr); + if (old_irq != m_intc_regs.icip) + { + m_maincpu->set_input_line(ARM7_IRQ_LINE, m_intc_regs.icip ? ASSERT_LINE : CLEAR_LINE); + } +} + +uint32_t sa1110_periphs_device::intc_r(offs_t offset, uint32_t mem_mask) +{ + switch (offset) + { + case REG_ICIP: + LOGMASKED(LOG_INTC, "%s: intc_r: Interrupt Controller IRQ Pending Register: %08x & %08x\n", machine().describe_context(), m_intc_regs.icip, mem_mask); + return m_intc_regs.icip; + case REG_ICMR: + LOGMASKED(LOG_INTC, "%s: intc_r: Interrupt Controller Mask Register: %08x & %08x\n", machine().describe_context(), m_intc_regs.icmr, mem_mask); + return m_intc_regs.icmr; + case REG_ICLR: + LOGMASKED(LOG_INTC, "%s: intc_r: Interrupt Controller Level Register: %08x & %08x\n", machine().describe_context(), m_intc_regs.iclr, mem_mask); + return m_intc_regs.iclr; + case REG_ICFP: + LOGMASKED(LOG_INTC, "%s: intc_r: Interrupt Controller FIQ Pending Register: %08x & %08x\n", machine().describe_context(), m_intc_regs.icfp, mem_mask); + return m_intc_regs.icfp; + case REG_ICPR: + LOGMASKED(LOG_INTC, "%s: intc_r: Interrupt Controller Pending Register: %08x & %08x\n", machine().describe_context(), m_intc_regs.icpr, mem_mask); + return m_intc_regs.icpr; + case REG_ICCR: + LOGMASKED(LOG_INTC, "%s: intc_r: Interrupt Controller Control Register: %08x & %08x\n", machine().describe_context(), m_intc_regs.iccr, mem_mask); + return m_intc_regs.iccr; + default: + LOGMASKED(LOG_INTC | LOG_UNKNOWN, "%s: intc_r: Unknown address: %08x & %08x\n", machine().describe_context(), INTC_BASE_ADDR | (offset << 2), mem_mask); + return 0; + } +} + +void sa1110_periphs_device::intc_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + switch (offset) + { + case REG_ICIP: + LOGMASKED(LOG_INTC, "%s: intc_w: (Invalid Write) Interrupt Controller IRQ Pending Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case REG_ICMR: + { + LOGMASKED(LOG_INTC, "%s: intc_w: Interrupt Controller Mask Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + const uint32_t old = m_intc_regs.icmr; + COMBINE_DATA(&m_intc_regs.icmr); + if (old != m_intc_regs.icmr) + update_interrupts(); + break; + } + case REG_ICLR: + { + LOGMASKED(LOG_INTC, "%s: intc_w: Interrupt Controller Level Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + const uint32_t old = m_intc_regs.iclr; + COMBINE_DATA(&m_intc_regs.iclr); + if (old != m_intc_regs.iclr) + update_interrupts(); + break; + } + case REG_ICFP: + LOGMASKED(LOG_INTC, "%s: intc_w: (Invalid Write) Interrupt Controller FIQ Pending Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case REG_ICPR: + LOGMASKED(LOG_INTC, "%s: intc_w: (Invalid Write) Interrupt Controller Pending Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + break; + case REG_ICCR: + LOGMASKED(LOG_INTC, "%s: intc_w: Interrupt Controller Control Register: %08x & %08x\n", machine().describe_context(), data, mem_mask); + m_intc_regs.iccr = BIT(data, 0); + break; + default: + LOGMASKED(LOG_INTC | LOG_UNKNOWN, "%s: intc_w: Unknown address: %08x = %08x & %08x\n", machine().describe_context(), INTC_BASE_ADDR | (offset << 2), data, mem_mask); + break; } } void sa1110_periphs_device::device_start() { - save_item(NAME(m_intc_regs.icip), m_intc_regs.icip); - save_item(NAME(m_intc_regs.icmr), m_intc_regs.icmr); - save_item(NAME(m_intc_regs.iclr), m_intc_regs.iclr); - save_item(NAME(m_intc_regs.iccr), m_intc_regs.iccr); - save_item(NAME(m_intc_regs.icfp), m_intc_regs.icfp); - save_item(NAME(m_intc_regs.icpr), m_intc_regs.icpr); + save_item(NAME(m_uart_regs.utcr)); + save_item(NAME(m_uart_regs.utsr0)); + save_item(NAME(m_uart_regs.utsr1)); + save_item(NAME(m_uart_regs.rx_fifo)); + save_item(NAME(m_uart_regs.rx_fifo_read_idx)); + save_item(NAME(m_uart_regs.rx_fifo_write_idx)); + save_item(NAME(m_uart_regs.rx_fifo_count)); + save_item(NAME(m_uart_regs.tx_fifo)); + save_item(NAME(m_uart_regs.tx_fifo_read_idx)); + save_item(NAME(m_uart_regs.tx_fifo_write_idx)); + save_item(NAME(m_uart_regs.tx_fifo_count)); + save_item(NAME(m_uart_regs.rx_break_interlock)); - save_item(NAME(m_power_regs.pmcr), m_power_regs.pmcr); - save_item(NAME(m_power_regs.pssr), m_power_regs.pssr); - save_item(NAME(m_power_regs.pspr), m_power_regs.pspr); - save_item(NAME(m_power_regs.pwer), m_power_regs.pwer); - save_item(NAME(m_power_regs.pcfr), m_power_regs.pcfr); - save_item(NAME(m_power_regs.ppcr), m_power_regs.ppcr); - save_item(NAME(m_power_regs.pgsr), m_power_regs.pgsr); - save_item(NAME(m_power_regs.posr), m_power_regs.posr); + save_item(NAME(m_ostmr_regs.osmr)); + save_item(NAME(m_ostmr_regs.oscr)); + save_item(NAME(m_ostmr_regs.ossr)); + save_item(NAME(m_ostmr_regs.ower)); + save_item(NAME(m_ostmr_regs.oier)); + for (int i = 0; i < 4; i++) + { + m_ostmr_regs.timer[i] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sa1110_periphs_device::ostimer_tick_cb), this)); + } + + save_item(NAME(m_rtc_regs.rtar)); + save_item(NAME(m_rtc_regs.rcnr)); + save_item(NAME(m_rtc_regs.rttr)); + save_item(NAME(m_rtc_regs.rtsr)); + m_rtc_regs.tick_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sa1110_periphs_device::rtc_tick_cb), this)); + + save_item(NAME(m_power_regs.pmcr)); + save_item(NAME(m_power_regs.pssr)); + save_item(NAME(m_power_regs.pspr)); + save_item(NAME(m_power_regs.pwer)); + save_item(NAME(m_power_regs.pcfr)); + save_item(NAME(m_power_regs.ppcr)); + save_item(NAME(m_power_regs.pgsr)); + save_item(NAME(m_power_regs.posr)); + + save_item(NAME(m_rcsr)); + + save_item(NAME(m_gpio_regs.gplr)); + save_item(NAME(m_gpio_regs.gpdr)); + save_item(NAME(m_gpio_regs.grer)); + save_item(NAME(m_gpio_regs.gfer)); + save_item(NAME(m_gpio_regs.gedr)); + save_item(NAME(m_gpio_regs.gafr)); + save_item(NAME(m_gpio_regs.any_edge_mask)); + save_item(NAME(m_gpio_regs.output_latch)); + save_item(NAME(m_gpio_regs.input_latch)); + save_item(NAME(m_gpio_regs.alt_output_latch)); + save_item(NAME(m_gpio_regs.alt_input_latch)); + + save_item(NAME(m_intc_regs.icip)); + save_item(NAME(m_intc_regs.icmr)); + save_item(NAME(m_intc_regs.iclr)); + save_item(NAME(m_intc_regs.iccr)); + save_item(NAME(m_intc_regs.icfp)); + save_item(NAME(m_intc_regs.icpr)); + + m_gpio_out.resolve_all_safe(); } void sa1110_periphs_device::device_reset() { - memset(&m_intc_regs, 0, sizeof(m_intc_regs)); - memset(&m_power_regs, 0, sizeof(m_power_regs)); -} + // init UART3 + memset(m_uart_regs.utcr, 0, sizeof(uint32_t) * 4); + m_uart_regs.utsr0 = 0; + m_uart_regs.utsr1 = 0; + memset(m_uart_regs.rx_fifo, 0, sizeof(uint16_t) * 12); + m_uart_regs.rx_fifo_read_idx = 0; + m_uart_regs.rx_fifo_write_idx = 0; + m_uart_regs.rx_fifo_count = 0; + memset(m_uart_regs.tx_fifo, 0, 8); + m_uart_regs.tx_fifo_read_idx = 0; + m_uart_regs.tx_fifo_write_idx = 0; + m_uart_regs.tx_fifo_count = 0; + m_uart_regs.rx_break_interlock = false; -void sa1110_periphs_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ + transmit_register_reset(); + receive_register_reset(); + + // init OS timers + memset(m_ostmr_regs.osmr, 0, sizeof(uint32_t) * 4); + m_ostmr_regs.ower = 0; + m_ostmr_regs.ossr = 0; + m_ostmr_regs.oier = 0; + for (int i = 0; i < 4; i++) + { + m_ostmr_regs.timer[i]->adjust(attotime::never); + } + m_ostmr_regs.last_count_sync = attotime::zero; + + // init RTC + m_rtc_regs.rtar = 0; + m_rtc_regs.rcnr = 0; + m_rtc_regs.rttr = 0; + m_rtc_regs.rtsr = 0; + m_rtc_regs.tick_timer->adjust(attotime::from_seconds(1), 0, attotime::from_seconds(1)); + + // init power regs + memset(&m_power_regs, 0, sizeof(m_power_regs)); + m_power_regs.posr = 1; // flag oscillator OK + + // bulk-init other registers + m_rcsr = 0x00000001; // indicate hardware reset + memset(&m_gpio_regs, 0, sizeof(m_gpio_regs)); + memset(&m_intc_regs, 0, sizeof(m_intc_regs)); } void sa1110_periphs_device::device_add_mconfig(machine_config &config) { + INPUT_MERGER_ANY_HIGH(config, m_uart3_irqs).output_handler().set(FUNC(sa1110_periphs_device::uart3_irq_callback)); } diff --git a/src/devices/machine/sa1110.h b/src/devices/machine/sa1110.h index 87ea94ffbf0..115285dbf7b 100644 --- a/src/devices/machine/sa1110.h +++ b/src/devices/machine/sa1110.h @@ -1,10 +1,10 @@ // license:BSD-3-Clause // copyright-holders:Ryan Holtz -/************************************************************************** - * - * Intel XScale SA1110 peripheral emulation - * - **************************************************************************/ +/*************************************************************************** + + Intel XScale SA1110 peripheral emulation + +***************************************************************************/ #ifndef MAME_MACHINE_SA1110 #define MAME_MACHINE_SA1110 @@ -13,10 +13,12 @@ #include "cpu/arm7/arm7.h" #include "cpu/arm7/arm7core.h" -#include "sound/dmadac.h" -#include "emupal.h" -class sa1110_periphs_device : public device_t +#include "machine/input_merger.h" + +#include "diserial.h" + +class sa1110_periphs_device : public device_t, public device_serial_interface { public: template @@ -28,29 +30,90 @@ public: sa1110_periphs_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - uint32_t intc_r(offs_t offset, uint32_t mem_mask = ~0); - void intc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + // device_serial overrides + virtual void rcv_complete() override; // Rx completed receiving byte + virtual void tra_complete() override; // Tx completed sending byte + virtual void tra_callback() override; // Tx send bit + + void gpio_in(const uint32_t line, const int state); + template auto gpio_out() { return m_gpio_out[Line].bind(); } + + uint32_t uart3_r(offs_t offset, uint32_t mem_mask = ~0); + void uart3_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + uint32_t ostimer_r(offs_t offset, uint32_t mem_mask = ~0); + void ostimer_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + uint32_t rtc_r(offs_t offset, uint32_t mem_mask = ~0); + void rtc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); uint32_t power_r(offs_t offset, uint32_t mem_mask = ~0); void power_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + uint32_t reset_r(offs_t offset, uint32_t mem_mask = ~0); + void reset_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + uint32_t gpio_r(offs_t offset, uint32_t mem_mask = ~0); + void gpio_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); + uint32_t intc_r(offs_t offset, uint32_t mem_mask = ~0); + void intc_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0); protected: virtual void device_add_mconfig(machine_config &config) override; virtual void device_start() override; virtual void device_reset() override; - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - void update_interrupts(); + static constexpr uint32_t INTERNAL_OSC = 3686400; + DECLARE_WRITE_LINE_MEMBER(uart3_irq_callback); + void uart_recalculate_divisor(); + void uart_update_eif_status(); + uint8_t uart_read_receive_fifo(); + void uart_write_transmit_fifo(uint8_t data); + void uart_check_tx_fifo_service(); + void uart_set_receiver_idle(); + void uart_begin_of_break(); + void uart_end_of_break(); + void uart_set_receiver_enabled(bool enabled); + void uart_set_transmitter_enabled(bool enabled); + void uart_set_receive_irq_enabled(bool enabled); + void uart_set_transmit_irq_enabled(bool enabled); + + TIMER_CALLBACK_MEMBER(ostimer_tick_cb); + void ostimer_update_count(); + void ostimer_update_match_timer(int channel); + + TIMER_CALLBACK_MEMBER(rtc_tick_cb); + + void gpio_update_interrupts(const uint32_t changed_mask); + void gpio_update_direction(const uint32_t old_gpdr); + void gpio_update_outputs(const uint32_t old_latch, const uint32_t changed); + void gpio_update_alternate_pins(const uint32_t changed_mask); + void set_irq_line(uint32_t line, int state); + void update_interrupts(); + // register offsets enum { - INTC_BASE_ADDR = 0x90050000, - REG_ICIP = (0x00000000 >> 2), - REG_ICMR = (0x00000004 >> 2), - REG_ICLR = (0x00000008 >> 2), - REG_ICCR = (0x0000000c >> 2), - REG_ICFP = (0x00000010 >> 2), - REG_ICPR = (0x00000020 >> 2), + UART_BASE_ADDR = 0x80050000, + REG_UTCR0 = (0x00000000 >> 2), + REG_UTCR1 = (0x00000004 >> 2), + REG_UTCR2 = (0x00000008 >> 2), + REG_UTCR3 = (0x0000000c >> 2), + REG_UTDR = (0x00000014 >> 2), + REG_UTSR0 = (0x0000001c >> 2), + REG_UTSR1 = (0x00000020 >> 2), + + OSTMR_BASE_ADDR = 0x90000000, + REG_OSMR0 = (0x00000000 >> 2), + REG_OSMR1 = (0x00000004 >> 2), + REG_OSMR2 = (0x00000008 >> 2), + REG_OSMR3 = (0x0000000c >> 2), + REG_OSCR = (0x00000010 >> 2), + REG_OSSR = (0x00000014 >> 2), + REG_OWER = (0x00000018 >> 2), + REG_OIER = (0x0000001c >> 2), + + RTC_BASE_ADDR = 0x90010000, + REG_RTAR = (0x00000000 >> 2), + REG_RCNR = (0x00000004 >> 2), + REG_RTTR = (0x00000008 >> 2), + REG_RTSR = (0x00000010 >> 2), POWER_BASE_ADDR = 0x90020000, REG_PMCR = (0x00000000 >> 2), @@ -60,17 +123,155 @@ protected: REG_PCFR = (0x00000010 >> 2), REG_PPCR = (0x00000014 >> 2), REG_PGSR = (0x00000018 >> 2), - REG_POSR = (0x0000001c >> 2) + REG_POSR = (0x0000001c >> 2), + + RESET_BASE_ADDR = 0x90030000, + REG_RSRR = (0x00000000 >> 2), + REG_RCSR = (0x00000004 >> 2), + + GPIO_BASE_ADDR = 0x90040000, + REG_GPLR = (0x00000000 >> 2), + REG_GPDR = (0x00000004 >> 2), + REG_GPSR = (0x00000008 >> 2), + REG_GPCR = (0x0000000c >> 2), + REG_GRER = (0x00000010 >> 2), + REG_GFER = (0x00000014 >> 2), + REG_GEDR = (0x00000018 >> 2), + REG_GAFR = (0x0000001c >> 2), + + INTC_BASE_ADDR = 0x90050000, + REG_ICIP = (0x00000000 >> 2), + REG_ICMR = (0x00000004 >> 2), + REG_ICLR = (0x00000008 >> 2), + REG_ICCR = (0x0000000c >> 2), + REG_ICFP = (0x00000010 >> 2), + REG_ICPR = (0x00000020 >> 2) }; - struct intc_regs + // register contents + enum : uint32_t { - uint32_t icip; - uint32_t icmr; - uint32_t iclr; - uint32_t iccr; - uint32_t icfp; - uint32_t icpr; + UART3_FIFO_PRE = 8, + UART3_FIFO_FRE = 9, + UART3_FIFO_ROR = 10, + + UTCR3_RXE_BIT = 0, + UTCR3_TXE_BIT = 1, + UTCR3_BRK_BIT = 2, + UTCR3_RIE_BIT = 3, + UTCR3_TIE_BIT = 4, + UTCR3_LBM_BIT = 5, + + UTSR0_TFS_BIT = 0, + UTSR0_RFS_BIT = 1, + UTSR0_RID_BIT = 2, + UTSR0_RBB_BIT = 3, + UTSR0_REB_BIT = 4, + UTSR0_EIF_BIT = 5, + + UTSR1_TBY_BIT = 0, + UTSR1_RNE_BIT = 1, + UTSR1_TNF_BIT = 2, + UTSR1_PRE_BIT = 3, + UTSR1_FRE_BIT = 4, + UTSR1_ROR_BIT = 5, + + RTSR_AL_BIT = 0, + RTSR_AL_MASK = (1 << RTSR_AL_BIT), + RTSR_HZ_BIT = 1, + RTSR_HZ_MASK = (1 << RTSR_HZ_BIT), + RTSR_ALE_BIT = 2, + RTSR_ALE_MASK = (1 << RTSR_ALE_BIT), + RTSR_HZE_BIT = 3, + RTSR_HZE_MASK = (1 << RTSR_HZE_BIT) + }; + + // interrupt bits + enum : uint32_t + { + INT_GPIO0 = 0, + INT_GPIO1 = 1, + INT_GPIO2 = 2, + INT_GPIO3 = 3, + INT_GPIO4 = 4, + INT_GPIO5 = 5, + INT_GPIO6 = 6, + INT_GPIO7 = 7, + INT_GPIO8 = 8, + INT_GPIO9 = 9, + INT_GPIO10 = 10, + INT_GPIOHI = 11, + INT_LCD = 12, + INT_UDC = 13, + INT_UART1 = 15, + INT_UART2 = 16, + INT_UART3 = 17, + INT_MCP = 18, + INT_SSP = 19, + INT_DMA0 = 20, + INT_DMA1 = 21, + INT_DMA2 = 22, + INT_DMA3 = 23, + INT_DMA4 = 24, + INT_DMA5 = 25, + INT_OSTIMER0 = 26, + INT_OSTIMER1 = 27, + INT_OSTIMER2 = 28, + INT_OSTIMER3 = 29, + INT_RTC_TICK = 30, + INT_RTC_ALARM = 31 + }; + + // UART3 interrupt sources + enum : unsigned + { + UART3_TFS = 0, + UART3_RFS = 1, + UART3_RID = 2, + UART3_RBB = 3, + UART3_REB = 4, + UART3_EIF = 5, + }; + + struct uart_regs + { + uint32_t utcr[4]; + uint32_t utsr0; + uint32_t utsr1; + + uint16_t rx_fifo[12]; + int rx_fifo_read_idx; + int rx_fifo_write_idx; + int rx_fifo_count; + + uint8_t tx_fifo[8]; + int tx_fifo_read_idx; + int tx_fifo_write_idx; + int tx_fifo_count; + + bool rx_break_interlock; + }; + + struct ostimer_regs + { + uint32_t osmr[4]; + uint32_t oscr; + uint32_t ossr; + uint32_t ower; + uint32_t oier; + + emu_timer *timer[4]; + attotime last_count_sync; + }; + + struct rtc_regs + { + uint32_t rtar; + uint32_t rcnr; + uint32_t rttr; + uint32_t rtsr; + + emu_timer *tick_timer; }; struct power_regs @@ -85,10 +286,45 @@ protected: uint32_t posr; }; - intc_regs m_intc_regs; - power_regs m_power_regs; + struct gpio_regs + { + uint32_t gplr; + uint32_t gpdr; + uint32_t grer; + uint32_t gfer; + uint32_t gedr; + uint32_t gafr; + + uint32_t any_edge_mask; + + uint32_t output_latch; + uint32_t input_latch; + uint32_t alt_output_latch; + uint32_t alt_input_latch; + }; + + struct intc_regs + { + uint32_t icip; + uint32_t icmr; + uint32_t iclr; + uint32_t iccr; + uint32_t icfp; + uint32_t icpr; + }; + + uart_regs m_uart_regs; + ostimer_regs m_ostmr_regs; + rtc_regs m_rtc_regs; + power_regs m_power_regs; + uint32_t m_rcsr; + gpio_regs m_gpio_regs; + intc_regs m_intc_regs; required_device m_maincpu; + required_device m_uart3_irqs; + + devcb_write_line::array<28> m_gpio_out; }; DECLARE_DEVICE_TYPE(SA1110_PERIPHERALS, sa1110_periphs_device) diff --git a/src/mame/drivers/zaurus.cpp b/src/mame/drivers/zaurus.cpp index 80ea72420c6..26ac249277d 100644 --- a/src/mame/drivers/zaurus.cpp +++ b/src/mame/drivers/zaurus.cpp @@ -1403,6 +1403,7 @@ Note: #include "emu.h" #include "cpu/arm7/arm7.h" #include "cpu/arm7/arm7core.h" +#include "machine/locomo.h" #include "machine/pxa255.h" #include "machine/sa1110.h" #include "machine/timer.h" @@ -1410,12 +1411,14 @@ Note: #include "screen.h" #include "speaker.h" - #define SA1110_CLOCK 206000000 #define PXA250_CLOCK 400000000 #define PXA255_CLOCK 400000000 #define PXA270_CLOCK 416000000 +namespace +{ + class zaurus_state : public driver_device { public: @@ -1441,14 +1444,18 @@ public: zaurus_sa_state(const machine_config &mconfig, device_type type, const char *tag) : zaurus_state(mconfig, type, tag) , m_sa_periphs(*this, "sa_periphs") + , m_locomo(*this, "locomo") { } void zaurus_sa1110(machine_config &config); private: + virtual void device_reset_after_children() override; + void main_map(address_map &map); required_device m_sa_periphs; + required_device m_locomo; }; class zaurus_pxa_state : public zaurus_state @@ -1477,9 +1484,15 @@ private: void zaurus_sa_state::main_map(address_map &map) { map(0x00000000, 0x00ffffff).rom().region("firmware", 0); + map(0x40000000, 0x40001fff).rw(m_locomo, FUNC(locomo_device::read), FUNC(locomo_device::write)); + map(0x80050000, 0x80050023).rw(m_sa_periphs, FUNC(sa1110_periphs_device::uart3_r), FUNC(sa1110_periphs_device::uart3_w)); + map(0x90000000, 0x9000001f).rw(m_sa_periphs, FUNC(sa1110_periphs_device::ostimer_r), FUNC(sa1110_periphs_device::ostimer_w)); + map(0x90010000, 0x9001000f).rw(m_sa_periphs, FUNC(sa1110_periphs_device::rtc_r), FUNC(sa1110_periphs_device::rtc_w)); map(0x90020000, 0x9002001f).rw(m_sa_periphs, FUNC(sa1110_periphs_device::power_r), FUNC(sa1110_periphs_device::power_w)); + map(0x90030000, 0x90030007).rw(m_sa_periphs, FUNC(sa1110_periphs_device::reset_r), FUNC(sa1110_periphs_device::reset_w)); + map(0x90040000, 0x90040023).rw(m_sa_periphs, FUNC(sa1110_periphs_device::gpio_r), FUNC(sa1110_periphs_device::gpio_w)); map(0x90050000, 0x90050023).rw(m_sa_periphs, FUNC(sa1110_periphs_device::intc_r), FUNC(sa1110_periphs_device::intc_w)); - map(0xc0000000, 0xc07fffff).ram().share("ram"); + map(0xc0000000, 0xc3ffffff).ram().share("ram"); } void zaurus_pxa_state::main_map(address_map &map) @@ -1497,6 +1510,11 @@ void zaurus_pxa_state::main_map(address_map &map) map(0xa0000000, 0xa07fffff).ram().share("ram"); } +void zaurus_sa_state::device_reset_after_children() +{ + m_sa_periphs->gpio_in(1, 1); +} + INPUT_CHANGED_MEMBER( zaurus_pxa_state::system_start ) { m_pxa_periphs->gpio_bit_w(10, m_power->read()); @@ -1524,6 +1542,8 @@ void zaurus_sa_state::zaurus_sa1110(machine_config &config) m_maincpu->set_addrmap(AS_PROGRAM, &zaurus_sa_state::main_map); SA1110_PERIPHERALS(config, m_sa_periphs, SA1110_CLOCK, m_maincpu); + + LOCOMO(config, m_locomo); } void zaurus_pxa_state::zaurus_pxa250(machine_config &config) @@ -1595,6 +1615,8 @@ ROM_START( zslc1000 ) ROM_LOAD( "openzaurus 3.5.3 - zimage-sharp sl-c1000-20050427214434.bin", 0x000000, 0x128980, BAD_DUMP CRC(1e1a9279) SHA1(909ac3f00385eced55822d6a155b79d9d25f43b3) ) ROM_END +} // anonymous namespace + COMP( 2002, zsl5500, 0, 0, zaurus_sa1110, zaurus_sa, zaurus_sa_state, empty_init, "Sharp", "Zaurus SL-5500 \"Collie\"", MACHINE_IS_SKELETON ) COMP( 2002, zslc500, 0, 0, zaurus_pxa250, zaurus_pxa, zaurus_pxa_state, empty_init, "Sharp", "Zaurus SL-C500", MACHINE_IS_SKELETON ) COMP( 2002, zsl5600, 0, 0, zaurus_pxa250, zaurus_pxa, zaurus_pxa_state, empty_init, "Sharp", "Zaurus SL-5600 / SL-B500 \"Poodle\"", MACHINE_IS_SKELETON )