From aeaf19f2640046d96b11991fc7ee9e389cb2ed3f Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Fri, 4 Oct 2024 20:58:33 +1000 Subject: -homebrew/linux4004.cpp: Emulated Linux/4004 board. * VFD is not emulated, but all other features are present. -machine/sc16is741.cpp: Emulated basic SC16IS741A UART functionality in SPI interface mode. -macine/spi_sdcard.cpp: Improved interface logic: * Start in unprotected mode as specified by the standard. * Ignore stop bits in unprotected mode - apparenty real cards do this. * Set protected or unprotected mode in response to CMD59. * Reset SPI logic when initially selected. -machine/spi_psram.cpp: Started adding PSRAM QPI functionality. -emu/diserial.cpp: Got rid of the per-instance parity lookup table. New working systems ------------------- Dmitry Grinberg Linux/4004 New working software list items (lnux4004.xml) ---------------------------------------------- uMIPS Linux 4.4.292+ --- hash/lnux4004.xml | 20 + scripts/src/machine.lua | 11 + src/devices/machine/sc16is741.cpp | 1238 ++++++++++++++++++++++++++++++++++++ src/devices/machine/sc16is741.h | 132 ++++ src/devices/machine/spi_psram.cpp | 237 +++++-- src/devices/machine/spi_psram.h | 46 +- src/devices/machine/spi_sdcard.cpp | 30 +- src/devices/machine/spi_sdcard.h | 5 +- src/emu/diserial.cpp | 62 +- src/emu/diserial.h | 4 - src/mame/homebrew/linux4004.cpp | 278 ++++++++ src/mame/layout/lnux4004.lay | 79 +++ src/mame/mame.lst | 3 + 13 files changed, 2027 insertions(+), 118 deletions(-) create mode 100644 hash/lnux4004.xml create mode 100644 src/devices/machine/sc16is741.cpp create mode 100644 src/devices/machine/sc16is741.h create mode 100644 src/mame/homebrew/linux4004.cpp create mode 100644 src/mame/layout/lnux4004.lay diff --git a/hash/lnux4004.xml b/hash/lnux4004.xml new file mode 100644 index 00000000000..1bf677eb7b5 --- /dev/null +++ b/hash/lnux4004.xml @@ -0,0 +1,20 @@ + + + + + + + + uMIPS Linux 4.4.292+ + 2024 + Dmitry Grinberg + + + + + + + + diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 19ed1d347b5..053caa56129 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -3320,6 +3320,17 @@ if (MACHINES["SAA5070"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/sc16is741.h,MACHINES["SC16IS741"] = true +--------------------------------------------------- +if (MACHINES["SC16IS741"]~=null) then + files { + MAME_DIR .. "src/devices/machine/sc16is741.cpp", + MAME_DIR .. "src/devices/machine/sc16is741.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/scc66470.h,MACHINES["SCC66470"] = true diff --git a/src/devices/machine/sc16is741.cpp b/src/devices/machine/sc16is741.cpp new file mode 100644 index 00000000000..c0a92d88943 --- /dev/null +++ b/src/devices/machine/sc16is741.cpp @@ -0,0 +1,1238 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/* + I²C/SPI UART with 64-byte transmit and receive FIFOs + + _______ + VDD 1 |* | 16 XTAL1 + A0 _CS 2 | | 15 XTAL2 + A1 SI 3 | | 14 _RESET + n.c. SO 4 | | 13 RX + SCL SCLK 5 | | 12 TX + SDA VSS 6 | | 11 _CTS + _IRQ 7 | | 10 _RTS + I2C _SPI 8 |_______| 9 VSS + + Partially software-compatible with the ubiquitous 16C450. + + TODO: + * When are registers considered "read" for side effects? + * The rest of the registers + * The rest of the interrupts + * 9-bit mode + * Xon/Xoff handshaking + * Special character detect + * Loopback + * Break detection + * IrDA mode + * I²C interface + * Sleep mode + * SC16IS741 differences + */ +#include "emu.h" +#include "sc16is741.h" + +//#define VERBOSE 1 +#include "logmacro.h" + + +namespace { + +#define IER_CTS_INT() (BIT(m_ier, 7)) +#define IER_RTS_INT() (BIT(m_ier, 6)) +#define IER_XOFF_INT() (BIT(m_ier, 5)) +#define IER_SLEEP_MODE() (BIT(m_ier, 4)) +#define IER_MODEM_STATUS_INT() (BIT(m_ier, 3)) +#define IER_LINE_STATUS_INT() (BIT(m_ier, 2)) +#define IER_THR_INT() (BIT(m_ier, 1)) +#define IER_RHR_INT() (BIT(m_ier, 0)) + +#define FCR_RX_TRIGGER() (BIT(m_fcr, 6, 2)) +#define FCR_TX_TRIGGER() (BIT(m_fcr, 4, 2)) +#define FCR_FIFO_ENABLE() (BIT(m_fcr, 0)) + +#define LCR_DL_ENABLE() (BIT(m_lcr, 7)) +#define LCR_BREAK() (BIT(m_lcr, 6)) +#define LCR_SET_PARITY() (BIT(m_lcr, 5)) +#define LCR_EVEN_PARITY() (BIT(m_lcr, 4)) +#define LCR_PARITY_ENABLE() (BIT(m_lcr, 3)) +#define LCR_STOP_BIT() (BIT(m_lcr, 2)) + +#define MCR_CLOCK_DIV4() (BIT(m_mcr, 7)) +#define MCR_TCR_TLR_ENABLE() (BIT(m_mcr, 2)) + +#define TCR_LEVEL_RESUME() (BIT(m_tcr, 4, 4)) +#define TCR_LEVEL_HALT() (BIT(m_tcr, 0, 4)) + +#define EFR_AUTO_CTS() (BIT(m_efr, 7)) +#define EFR_AUTO_RTS() (BIT(m_efr, 6)) +#define EFR_ENHANCED() (BIT(m_efr, 4)) + + +constexpr u8 RX_TRIGGER_LEVELS[4] = { 8, 16, 56, 60 }; +constexpr u8 TX_TRIGGER_LEVELS[4] = { 8, 16, 32, 56 }; + +char const *const SOFT_FLOW_CONTROL_DESC[16] = { + "no soft transmit flow control, no soft receive flow control", + "no soft transmit flow control, receiver compares Xon2, Xoff2", + "no soft transmit flow control, receiver compares Xon1, Xoff1", + "no soft transmit flow control, receiver compares Xon1 and Xon2, Xoff1 and Xoff2", + "transmit Xon2, Xoff2, no soft receive flow control", + "transmit Xon2, Xoff2, receiver compares Xon2, Xoff2", + "transmit Xon2, Xoff2, receiver compares Xon1, Xoff1", + "transmit Xon2, Xoff2, receiver compares Xon1 or Xon2, Xoff1 or Xoff2", + "transmit Xon1, Xoff1, no soft receive flow control", + "transmit Xon1, Xoff1, receiver compares Xon2, Xoff2", + "transmit Xon1, Xoff1, receiver compares Xon1, Xoff1", + "transmit Xon1, Xoff1, receiver compares Xon1 or Xon2, Xoff1 or Xoff2", + "transmit Xon1 and Xon2, Xoff1 and Xoff2, no soft receive flow control", + "transmit Xon1 and Xon2, Xoff1 and Xoff2, receiver compares Xon2, Xoff2", + "transmit Xon1 and Xon2, Xoff1 and Xoff2, receiver compares Xon1, Xoff1", + "transmit Xon1 and Xon2, Xoff1 and Xoff2, receiver compares Xon1 and Xon2, Xoff1 and Xoff2" }; + +} // anonymous namespace + + +DEFINE_DEVICE_TYPE(SC16IS741A, sc16is741a_device, "sc16is741a", "NXP SC16IS741A UART") + + +ALLOW_SAVE_TYPE(sc16is741a_device::phase); + +enum class sc16is741a_device::phase : u8 +{ + IDLE, + COMMAND, + WRITE, + READ +}; + + +enum class sc16is741a_device::parity : u8 +{ + NONE, + ODD, + EVEN, + MARK, + SPACE +}; + + +enum sc16is741a_device::interrupt : u8 +{ + INTERRUPT_LINE_STATUS = 0x80, + INTERRUPT_RX_TIMEOUT = 0x40, + INTERRUPT_RHR = 0x20, + INTERRUPT_THR = 0x10, + INTERRUPT_MODEM_STATUS = 0x08, + INTERRUPT_XOFF = 0x04, + INTERRUPT_SPECIAL_CHAR = 0x02, + INTERRUPT_RTS_CTS = 0x01 +}; + + +sc16is741a_device::sc16is741a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : + device_t(mconfig, SC16IS741A, tag, owner, clock), + m_so_cb(*this), + m_irq_cb(*this), + m_tx_cb(*this), + m_rts_cb(*this), + m_shift_timer{ nullptr, nullptr }, + m_rx_timeout_timer(nullptr) +{ +} + +sc16is741a_device::~sc16is741a_device() +{ +} + + +void sc16is741a_device::sclk_w(int state) +{ + if ((phase::COMMAND == m_phase) || (phase::WRITE == m_phase)) + { + if (state && !m_sclk) + { + m_buffer = (m_buffer << 1) | m_si; + if (!--m_bits) + { + if (phase::COMMAND == m_phase) + { + m_command = m_buffer; + if (BIT(m_buffer, 7)) + { + m_phase = phase::READ; + reg_r(true); + } + else + { + m_phase = phase::WRITE; + } + } + else + { + reg_w(); + } + m_bits = 8; + } + } + } + else if (phase::READ == m_phase) + { + if (!state && m_sclk) + { + m_so_cb(BIT(m_buffer, 7)); + } + else if (state && !m_sclk) + { + m_buffer = (m_buffer << 1) | (m_buffer >> 7); + --m_bits; + if (!m_bits) + { + reg_r(false); + m_bits = 8; + } + else if (7 == m_bits) + { + if ((BIT(m_command, 3, 4) == 0x00) && ((0xbf == m_lcr) || !LCR_DL_ENABLE())) + pop_rx_fifo(); + } + } + } + m_sclk = state ? 1 : 0; +} + +void sc16is741a_device::cs_w(int state) +{ + if (state) + { + m_phase = phase::IDLE; + m_so_cb(1); + } + else if (m_cs) + { + m_phase = phase::COMMAND; + m_bits = 8; + } + m_cs = state ? 1 : 0; +} + +void sc16is741a_device::si_w(int state) +{ + m_si = state ? 1 : 0; +} + +void sc16is741a_device::rx_w(int state) +{ + if (m_divisor) // FIXME: check EFCR[1] + { + if (!m_rx_remain) + { + if (m_rx && !state) + { + // start bit + m_rx_remain = m_rx_intervals; + m_rx_count = 0; + m_shift_timer[0]->adjust(attotime::from_ticks(m_divisor * 16 / 2, clock())); + } + } + else if (!m_rx_count) + { + if (state) + { + // false start + m_rx_remain = 0; + m_shift_timer[0]->reset(); + } + } + } + m_rx = state ? 1 : 0; +} + +void sc16is741a_device::cts_w(int state) +{ + bool const asserted(EFR_AUTO_CTS() && !state && m_cts); + if (bool(state) != bool(m_cts)) + { + m_interrupts |= INTERRUPT_MODEM_STATUS; + update_irq(); + } + m_cts = state ? 1 : 0; + if (asserted) + check_tx(); +} + + +void sc16is741a_device::device_resolve_objects() +{ + m_tx = 1; + m_rx = 1; + m_cts = 0; + m_sclk = 0; + m_cs = 1; + m_si = 1; + m_bits = 0; + m_buffer = 0; +} + +void sc16is741a_device::device_start() +{ + m_shift_timer[0] = timer_alloc(FUNC(sc16is741a_device::rx_shift), this); + m_shift_timer[1] = timer_alloc(FUNC(sc16is741a_device::tx_shift), this); + m_rx_timeout_timer = timer_alloc(FUNC(sc16is741a_device::rx_timeout), this); + + m_spr = 0x00; + m_dl = 0x0000; + std::fill(std::begin(m_xon_xoff), std::end(m_xon_xoff), 0); + m_tx_count = 0; + m_rx_count = 0; + for (auto &data : m_fifo_data) + std::fill(std::begin(data), std::end(data), 0); + m_divisor = 0; + + save_item(NAME(m_irq)); + save_item(NAME(m_tx)); + save_item(NAME(m_rts)); + save_item(NAME(m_rx)); + save_item(NAME(m_cts)); + save_item(NAME(m_sclk)); + save_item(NAME(m_cs)); + save_item(NAME(m_si)); + save_item(NAME(m_phase)); + save_item(NAME(m_bits)); + save_item(NAME(m_buffer)); + save_item(NAME(m_command)); + save_item(NAME(m_ier)); + save_item(NAME(m_fcr)); + save_item(NAME(m_lcr)); + save_item(NAME(m_mcr)); + save_item(NAME(m_spr)); + save_item(NAME(m_tcr)); + save_item(NAME(m_tlr)); + save_item(NAME(m_dl)); + save_item(NAME(m_efr)); + save_item(NAME(m_xon_xoff)); + save_item(NAME(m_shift_reg)); + save_item(NAME(m_rx_remain)); + save_item(NAME(m_rx_count)); + save_item(NAME(m_tx_remain)); + save_item(NAME(m_tx_count)); + save_item(NAME(m_fifo_head)); + save_item(NAME(m_fifo_tail)); + save_item(NAME(m_fifo_empty)); + save_item(NAME(m_fifo_data)); + save_item(NAME(m_fifo_errors)); + save_item(NAME(m_interrupts)); +} + +void sc16is741a_device::device_reset() +{ + m_shift_timer[0]->reset(); + m_shift_timer[1]->reset(); + m_rx_timeout_timer->reset(); + + m_phase = phase::IDLE; + + m_ier = 0x00; + m_fcr = 0x00; + m_lcr = 0x1d; + m_mcr = 0x00; + m_tcr = 0x00; + m_tlr = 0x00; + m_efr = 0x00; + + std::fill(std::begin(m_shift_reg), std::end(m_shift_reg), 0xffff); + m_rx_remain = 0; + m_tx_remain = 0; + + std::fill(std::begin(m_fifo_tail), std::end(m_fifo_tail), 0); + fifo_reset(0); + fifo_reset(1); + m_fifo_errors = 0; + + m_interrupts = 0x00; + + update_trigger_levels(); + update_data_frame(); + update_divisor(); + + m_irq_cb(m_irq = CLEAR_LINE); + m_tx_cb(m_tx = 1); + m_rts_cb(m_rts = 1); +} + +void sc16is741a_device::device_post_load() +{ + update_trigger_levels(); + update_data_frame(); + update_divisor(); +} + + +inline void sc16is741a_device::update_irq() +{ + bool const pending( + (IER_LINE_STATUS_INT() && (m_interrupts & INTERRUPT_LINE_STATUS)) || + (IER_MODEM_STATUS_INT() && (m_interrupts & INTERRUPT_MODEM_STATUS)) || + (IER_RHR_INT() && (m_interrupts & (INTERRUPT_RX_TIMEOUT | INTERRUPT_RHR)))); + if (pending != (ASSERT_LINE == m_irq)) + { + LOG(pending ? "asserting IRQ\n" : "deasserting IRQ\n"); + m_irq_cb(m_irq = (pending ? ASSERT_LINE : CLEAR_LINE)); + } +} + +inline void sc16is741a_device::update_tx() +{ + u8 const state(LCR_BREAK() ? 0 : BIT(m_shift_reg[1], 0)); + if (state != m_tx) + m_tx_cb(m_tx = state); +} + +inline void sc16is741a_device::set_rts(u8 state) +{ + if (state != m_rts) + m_rts_cb(m_rts = state); +} + + +inline void sc16is741a_device::reg_r(bool first) +{ + u8 const ch(BIT(m_command, 1, 2)); + u8 const addr(BIT(m_command, 3, 4)); + + // must be zero + if (0 != ch) + { + if (first) + logerror("read from unsupported ch %1$u register address 0x%2$02x\n", ch, addr); + m_buffer = 0xff; + return; + } + + switch (addr) + { + case 0x00: + if ((0xbf != m_lcr) && LCR_DL_ENABLE()) + m_buffer = BIT(m_dl, 0, 8); + else + m_buffer = m_fifo_data[0][m_fifo_tail[0]]; + return; + case 0x01: + if ((0xbf != m_lcr) && LCR_DL_ENABLE()) + m_buffer = BIT(m_dl, 8, 8); + else + m_buffer = m_ier; + return; + case 0x02: + if (0xbf == m_lcr) + m_buffer = m_efr; + else + iir_r(first); + return; + case 0x03: + m_buffer = m_lcr; + return; + case 0x04: + if (0xbf == m_lcr) + xon_xoff_r(first); + else + m_buffer = m_mcr; + return; + case 0x05: + if (0xbf == m_lcr) + xon_xoff_r(first); + else + lsr_r(first); + return; + case 0x06: + if (0xbf == m_lcr) + xon_xoff_r(first); + else if (MCR_TCR_TLR_ENABLE() && EFR_ENHANCED()) + m_buffer = m_tcr; + else + msr_r(first); + return; + case 0x07: + if (0xbf == m_lcr) + xon_xoff_r(first); + else if (MCR_TCR_TLR_ENABLE() && EFR_ENHANCED()) + m_buffer = m_tcr; + else + m_buffer = m_spr; + return; + case 0x08: + txlvl_r(first); + return; + case 0x09: + rxlvl_r(first); + return; + } + + if (first) + logerror("read from unimplemented register address 0x%1$02x\n", addr); + m_buffer = 0xff; +} + +inline void sc16is741a_device::reg_w() +{ + u8 const ch(BIT(m_command, 1, 2)); + u8 const addr(BIT(m_command, 3, 4)); + + // must be zero + if (0 != ch) + { + logerror("write to unsupported ch %1$u register address 0x%2$02x = 0x%3$02x\n", ch, addr, m_buffer); + return; + } + + switch (addr) + { + case 0x00: + if ((0xbf != m_lcr) && LCR_DL_ENABLE()) + dl_w(); + else + thr_w(); + return; + case 0x01: + if ((0xbf != m_lcr) && LCR_DL_ENABLE()) + dl_w(); + else + ier_w(); + return; + case 0x02: + if (0xbf == m_lcr) + efr_w(); + else + fcr_w(); + return; + case 0x03: + lcr_w(); + return; + case 0x04: + if (0xbf == m_lcr) + xon_xoff_w(); + else + mcr_w(); + return; + case 0x05: + if (0xbf == m_lcr) + xon_xoff_w(); + else + break; // LSR is read-only + return; + case 0x06: + if (0xbf == m_lcr) + xon_xoff_w(); + else if (MCR_TCR_TLR_ENABLE() && EFR_ENHANCED()) + tcr_w(); + else + break; // MSR is read-only + return; + case 0x07: + if (0xbf == m_lcr) + xon_xoff_w(); + else if (MCR_TCR_TLR_ENABLE() && EFR_ENHANCED()) + tlr_w(); + else + m_spr = m_buffer; + return; + case 0x0d: + reserved_w(); + return; + case 0x0e: + uart_reset_w(); + return; + } + + logerror("write to unimplemented register address 0x%1$02x = 0x%2$02x\n", addr, m_buffer); +} + + +inline void sc16is741a_device::iir_r(bool first) +{ + if (first) + { + m_buffer = BIT(m_fcr, 0) ? 0xc0 : 0x00; + if (!m_irq) + { + m_buffer |= 0x01; + } + else if (IER_LINE_STATUS_INT() && (m_interrupts & INTERRUPT_LINE_STATUS)) + { + m_buffer |= 0x06; + } + else if (IER_RHR_INT() && (m_interrupts & INTERRUPT_RX_TIMEOUT)) + { + m_buffer |= 0x0c; + } + else if (IER_RHR_INT() && (m_interrupts & INTERRUPT_RHR)) + { + m_buffer |= 0x04; + } + else if (IER_THR_INT() && (m_interrupts & INTERRUPT_THR)) + { + m_buffer |= 0x02; + LOG("clearing THR interrupt\n"); + m_interrupts &= ~INTERRUPT_THR; + } + else if (IER_MODEM_STATUS_INT() && (m_interrupts & INTERRUPT_MODEM_STATUS)) + { + m_buffer |= 0x00; + } + + LOG("read IIR (0x%1$02x)\n", m_buffer); + } +} + +inline void sc16is741a_device::lsr_r(bool first) +{ + m_buffer = + (m_fifo_errors ? 0x80 : 0x00) | + ((m_fifo_empty[1] && !m_tx_remain) ? 0x40 : 0x00) | + (m_fifo_empty[1] ? 0x20 : 0x00) | + (!m_fifo_empty[0] ? 0x01 : 0x00); + if (!m_fifo_empty[0]) + m_buffer |= m_fifo_data[1][m_fifo_tail[0]]; + + if (first) + LOG("read LSR (0x%1$02x)\n", m_buffer); +} + +inline void sc16is741a_device::msr_r(bool first) +{ + if (first) + { + m_buffer = + (!m_cts ? 0x10 : 0x00) | + ((m_interrupts & INTERRUPT_MODEM_STATUS) ? 0x01 : 0x00); + m_interrupts &= ~INTERRUPT_MODEM_STATUS; + + LOG("read MSR (0x%1$02x)\n", m_buffer); + } +} + +inline void sc16is741a_device::txlvl_r(bool first) +{ + m_buffer = fifo_spaces(1); + + if (first) + LOG("read TXLVL (0x%1$02x)\n", m_buffer); +} + +inline void sc16is741a_device::rxlvl_r(bool first) +{ + m_buffer = fifo_fill_level(0); + + if (first) + LOG("read RXLVL (0x%1$02x)\n", m_buffer); +} + +inline void sc16is741a_device::xon_xoff_r(bool first) +{ + m_buffer = m_xon_xoff[BIT(m_command, 3, 2)]; + + if (first) + LOG("read %1$s%2$u (0x%3$02x)\n", BIT(m_command, 4) ? "XOFF" : "XON", BIT(m_command, 3) + 1, m_buffer); +} + + +inline void sc16is741a_device::thr_w() +{ + m_fifo_data[2][fifo_push(1)] = m_buffer; + + if (m_interrupts & INTERRUPT_THR) + { + LOG("THR written, clearing THR interrupt\n"); + m_interrupts &= ~INTERRUPT_THR; + } + + check_tx(); + update_irq(); // doing this here avoids a glitch if the FIFO is immediately popped +} + +inline void sc16is741a_device::ier_w() +{ + LOG(EFR_ENHANCED() + ? "IER = 0x%1$02x (CTS interrupt %2$s, RTS interrupt %3$s, Xoff interrupt %4$s, sleep mode %5$s, modem status interrupt %6$s, RX status interrupt %7$s, THR interrupt %8$s, RHR interrupt %9$s)\n" + : "IER = 0x%1$02x (modem status interrupt %6$s, RX status interrupt %7$s, THR interrupt %8$s, RHR interrupt %9$s)\n", + m_buffer & (EFR_ENHANCED() ? 0xff : 0x0f), + BIT(m_buffer, 7) ? "enabled" : "disabled", + BIT(m_buffer, 6) ? "enabled" : "disabled", + BIT(m_buffer, 5) ? "enabled" : "disabled", + BIT(m_buffer, 4) ? "enabled" : "disabled", + BIT(m_buffer, 3) ? "enabled" : "disabled", + BIT(m_buffer, 2) ? "enabled" : "disabled", + BIT(m_buffer, 1) ? "enabled" : "disabled", + BIT(m_buffer, 0) ? "enabled" : "disabled"); + + if (EFR_ENHANCED()) + m_ier = m_buffer; + else + m_ier = (m_ier & 0xf0) | (m_buffer & 0x0f); + update_irq(); +} + +inline void sc16is741a_device::fcr_w() +{ + LOG(EFR_ENHANCED() + ? "FCR = 0x%1$02x (RX trigger %2$u, TX trigger %3$u, reserved %4$u, %5$sTX FIFO reset, %6$sRX FIFO reset, FIFO %7$s)\n" + : "FCR = 0x%1$02x (RX trigger %2$u, reserved %4$u, %5$sTX FIFO reset, %6$sRX FIFO reset, FIFO %7$s)\n", + m_buffer & (EFR_ENHANCED() ? 0xff : 0xcf), + RX_TRIGGER_LEVELS[BIT(m_buffer, 6, 2)], + TX_TRIGGER_LEVELS[BIT(m_buffer, 4, 2)], + BIT(m_buffer, 3), + BIT(m_buffer, 2) ? "" : "no ", + BIT(m_buffer, 1) ? "" : "no ", + BIT(m_buffer, 0) ? "enabled" : "disabled"); + + if (BIT(m_buffer, 3)) + logerror("reserved bit FCR[3] is set\n"); + + if (BIT(m_buffer, 2)) + fifo_reset(1); + + if (BIT(m_buffer, 1)) + { + fifo_reset(0); + m_fifo_errors = 0; + m_interrupts &= ~(INTERRUPT_LINE_STATUS | INTERRUPT_RX_TIMEOUT | INTERRUPT_RHR); + if (EFR_AUTO_RTS() && m_rts) // FIXME: check EFCR[4] + { + LOG("RX FIFO reset, asserting RTS\n"); + set_rts(0); + } + update_irq(); + } + + if (EFR_ENHANCED()) + m_fcr = m_buffer & 0xf9; + else + m_fcr = (m_fcr & 0x30) | (m_buffer & 0xc9); + update_trigger_levels(); +} + +inline void sc16is741a_device::lcr_w() +{ + LOG("LCR = 0x%1$02x (divisor latch %2$s, %3$sbreak, %4$s parity %5$s, %6$s stop bits, word length %7$u)\n", + m_buffer, + BIT(m_buffer, 7) ? "enabled" : "disabled", + BIT(m_buffer, 6) ? "" : "no ", + BIT(m_buffer, 5) ? (BIT(m_buffer, 4) ? "0" : "1") : (BIT(m_buffer, 4) ? "even" : "odd"), + BIT(m_buffer, 3) ? "on" : "off", + !BIT(m_buffer, 2) ? "1" : !BIT(m_buffer, 0, 2) ? "1.5" : "2", + BIT(m_buffer, 0, 2) + 5); + + m_lcr = m_buffer; + update_tx(); + update_data_frame(); +} + +inline void sc16is741a_device::mcr_w() +{ + LOG(EFR_ENHANCED() + ? "MCR = 0x%1$02x (divide-by-%2$u, %3$s mode, Xon Any %4$s, loopback %5$s, reserved %6$u, TCR and TLR %7$s, RTS %8$s, reserved %9$u)\n" + : "MCR = 0x%1$02x (loopback %5$s, reserved %6$u, TCR and TLR %7$s, RTS %8$s, reserved %9$u)\n", + m_buffer & (EFR_ENHANCED() ? 0xff : 0x1f), + BIT(m_buffer, 7) ? 4 : 1, + BIT(m_buffer, 6) ? "IrDA" : "normal UART", + BIT(m_buffer, 5) ? "enabled" : "disabled", + BIT(m_buffer, 4) ? "enabled" : "disabled", + BIT(m_buffer, 3), + BIT(m_buffer, 2) ? "enabled" : "disabled", + BIT(m_buffer, 1) ? "active" : "inactive", + BIT(m_buffer, 0)); + + if (BIT(m_buffer, 3)) + logerror("reserved bit MCR[3] is set\n"); + if (BIT(m_buffer, 0)) + logerror("reserved bit MCR[0] is set\n"); + + if (!EFR_AUTO_RTS()) // FIXME: check EFCR[4] + set_rts(BIT(~m_buffer, 1)); + + if (EFR_ENHANCED()) + { + m_mcr = m_buffer; + update_divisor(); + } + else + { + m_mcr = (m_mcr & 0xe0) | (m_buffer & 0x1f); + } +} + +inline void sc16is741a_device::tcr_w() +{ + LOG("TCR = 0x%1$02x (resume transmission at %2$u*4 characters, halt transmission at %3$u*4 characters)\n", + m_buffer, + BIT(m_buffer, 4, 4), + BIT(m_buffer, 0, 4)); + + m_tcr = m_buffer; +} + +inline void sc16is741a_device::tlr_w() +{ + LOG("TLR = 0x%1$02x (RX FIFO trigger level %2$u*4 characters%3$s, TX FIFO trigger level %4$u*4 spaces%5$s)\n", + m_buffer, + BIT(m_buffer, 4, 4), + BIT(m_buffer, 4, 4) ? "" : " - use FCR[7:6]", + BIT(m_buffer, 0, 4), + BIT(m_buffer, 0, 4) ? "" : " - use FCR[5:4]"); + + m_tlr = m_buffer; + update_trigger_levels(); +} + +inline void sc16is741a_device::reserved_w() +{ + logerror("reserved register address 0x%1$02x = 0x%2$02x\n", BIT(m_command, 3, 4), m_buffer); +} + +inline void sc16is741a_device::uart_reset_w() +{ + LOG("UART reset = 0x%1$02x (reserved %2$u, reserved %3$u, reserved %4$u, reserved %5$u, %6$ssoftware reset, reserved %7$u, reserved %8$u, reserved %9$u)\n", + m_buffer, + BIT(m_buffer, 7), + BIT(m_buffer, 6), + BIT(m_buffer, 5), + BIT(m_buffer, 4), + BIT(m_buffer, 3) ? "" : "no ", + BIT(m_buffer, 2), + BIT(m_buffer, 1), + BIT(m_buffer, 0)); + + if (BIT(m_buffer, 7)) + logerror("reserved bit UART reset[7] is set\n"); + if (BIT(m_buffer, 6)) + logerror("reserved bit UART reset[6] is set\n"); + if (BIT(m_buffer, 5)) + logerror("reserved bit UART reset[5] is set\n"); + if (BIT(m_buffer, 4)) + logerror("reserved bit UART reset[4] is set\n"); + if (BIT(m_buffer, 2)) + logerror("reserved bit UART reset[2] is set\n"); + if (BIT(m_buffer, 1)) + logerror("reserved bit UART reset[1] is set\n"); + if (BIT(m_buffer, 0)) + logerror("reserved bit UART reset[0] is set\n"); + + // TODO: is this instantaneous reset, or is the reset condition held until the bit is cleared? + if (BIT(m_buffer, 3)) + device_reset(); +} + +inline void sc16is741a_device::dl_w() +{ + LOG("DL%1$c = 0x%2$02x\n", BIT(m_command, 3) ? 'H' : 'L', m_buffer); + + m_dl = (m_dl & (BIT(m_command, 3) ? 0x00ff : 0xff00)) | (u16(m_buffer) << (BIT(m_command, 3) ? 8 : 0)); + update_divisor(); +} + +inline void sc16is741a_device::efr_w() +{ + LOG("EFR = 0x%1$02x (CTS flow control %2$s, RTS flow control %3$s, special character detect %4$s, enhanced functions %5$s, %6$s)\n", + m_buffer, + BIT(m_buffer, 7) ? "enabled" : "disabled", + BIT(m_buffer, 6) ? "enabled" : "disabled", + BIT(m_buffer, 5) ? "enabled" : "disabled", + BIT(m_buffer, 4) ? "enabled" : "disabled", + SOFT_FLOW_CONTROL_DESC[BIT(m_buffer, 0, 4)]); + + if (!BIT(m_buffer, 6)) // FIXME: check EFCR[4] + { + // auto RTS off, ensure RTS output is up-to-date + set_rts(BIT(~m_mcr, 1)); + } + else if (!EFR_AUTO_RTS()) + { + // enabling auto RTS + if (FCR_FIFO_ENABLE()) + { + u8 const level(fifo_fill_level(0)); + set_rts(((level <= (TCR_LEVEL_RESUME() * 4)) || (level < (TCR_LEVEL_HALT() * 4))) ? 0 : 1); + } + else + { + set_rts(m_fifo_empty[0] ? 0 : 1); + } + } + + m_efr = m_buffer; + check_tx(); +} + +inline void sc16is741a_device::xon_xoff_w() +{ + LOG("%1$s%2$u = 0x%3$02x\n", BIT(m_command, 4) ? "XOFF" : "XON", BIT(m_command, 3) + 1, m_buffer); + + m_xon_xoff[BIT(m_command, 3, 2)] = m_buffer; +} + + +inline void sc16is741a_device::pop_rx_fifo() +{ + assert(!m_fifo_empty[0] || !m_fifo_errors); + + if (!m_fifo_empty[0] && m_fifo_data[1][m_fifo_tail[0]]) + { + assert(m_fifo_errors); + assert(m_interrupts & INTERRUPT_LINE_STATUS); + if (!--m_fifo_errors) + { + LOG("read last data error, clearing line status interrupt\n"); + m_interrupts &= ~INTERRUPT_LINE_STATUS; + update_irq(); + } + } + + fifo_pop(0); + u8 const level(fifo_fill_level(0)); + if (m_fifo_empty[0]) + m_rx_timeout_timer->reset(); + else + m_rx_timeout_timer->adjust(attotime::from_ticks(m_divisor * 16 / 2 * 4 * m_rx_intervals, clock())); + + if (m_interrupts & INTERRUPT_RX_TIMEOUT) + { + LOG("clearing RX timeout interrupt\n"); + m_interrupts &= ~INTERRUPT_RX_TIMEOUT; + update_irq(); + } + + if (m_interrupts & INTERRUPT_RHR) + { + if (FCR_FIFO_ENABLE()) + { + if (level < m_rx_trigger) + { + LOG("RX FIFO level %1$u within %2$u, clearing RHR interrupt\n", level, m_rx_trigger); + m_interrupts &= ~INTERRUPT_RHR; + update_irq(); + } + } + else if (m_fifo_empty[0]) + { + LOG("RHR empty, clearing RHR interrupt\n"); + m_interrupts &= ~INTERRUPT_RHR; + update_irq(); + } + } + + if (EFR_AUTO_RTS() && m_rts) // FIXME: check EFCR[4] + { + if (FCR_FIFO_ENABLE()) + { + u8 const trigger(TCR_LEVEL_RESUME()); + if (level <= (trigger * 4)) + { + LOG("RX FIFO level %1$u within %2$u*4, asserting RTS\n", level, trigger); + set_rts(0); + } + } + else + { + LOG("RHR empty, asserting RTS\n"); + set_rts(0); + } + } +} + +inline bool sc16is741a_device::check_tx() +{ + if (m_tx_remain || m_fifo_empty[1] || (EFR_AUTO_CTS() && m_cts) || !m_divisor) // FIXME: check EFCR[2] + return false; + + u16 const data(u16(m_fifo_data[2][fifo_pop(1)] & util::make_bitmask(m_word_length)) << 1); + if (parity::NONE == m_parity) + { + m_shift_reg[1] = ~util::make_bitmask(m_word_length + 1) | data; + } + else + { + m_shift_reg[1] = ~util::make_bitmask(m_word_length + 2) | data; + switch (m_parity) + { + case parity::ODD: + m_shift_reg[1] |= BIT(~population_count_32(data), 0) << (m_word_length + 1); + break; + case parity::EVEN: + m_shift_reg[1] |= BIT(population_count_32(data), 0) << (m_word_length + 1); + break; + case parity::MARK: + m_shift_reg[1] |= u16(1) << (m_word_length + 1); + break; + default: + break; + } + } + m_tx_remain = m_tx_intervals; + m_tx_count = 0; + update_tx(); + m_shift_timer[1]->adjust(attotime::from_ticks(m_divisor * 16 / 2, clock())); + + if (IER_THR_INT() && !(m_interrupts & INTERRUPT_THR)) + { + if (FCR_FIFO_ENABLE()) + { + // TODO: does this only happen at the trigger level, or any time the FIFO is popped above the trigger level? + u8 const spaces(fifo_spaces(1)); + if (spaces >= m_tx_trigger) + { + LOG("TX FIFO spaces %1$u exceed %2$u, setting THR interrupt\n", spaces, m_tx_trigger); + m_interrupts |= INTERRUPT_THR; + update_irq(); + } + else + { + LOG("THR empty, setting THR interrupt\n"); + m_interrupts |= INTERRUPT_THR; + update_irq(); + } + } + } + + return true; +} + + +inline u8 sc16is741a_device::fifo_spaces(unsigned n) const +{ + if (m_fifo_empty[n]) + return FIFO_LENGTH; + else + return (FIFO_LENGTH - m_fifo_head[n] + m_fifo_tail[n]) % FIFO_LENGTH; +} + +inline u8 sc16is741a_device::fifo_fill_level(unsigned n) const +{ + if (!m_fifo_empty[n] && (m_fifo_head[n] == m_fifo_tail[n])) + return FIFO_LENGTH; + else + return (FIFO_LENGTH + m_fifo_head[n] - m_fifo_tail[n]) % FIFO_LENGTH; +} + +inline void sc16is741a_device::fifo_reset(unsigned n) +{ + m_fifo_head[n] = m_fifo_tail[n]; + m_fifo_empty[n] = true; +} + +inline u8 sc16is741a_device::fifo_push(unsigned n) +{ + if (!FCR_FIFO_ENABLE()) + { + if (!m_fifo_empty[n]) + LOG("%1$s FIFO overrun\n", n ? "TX" : "RX"); + m_fifo_empty[n] = false; + return m_fifo_head[n]; + } + else if ((m_fifo_head[n] != m_fifo_tail[n]) || m_fifo_empty[n]) + { + m_fifo_empty[n] = false; + return std::exchange(m_fifo_head[n], (m_fifo_head[n] + 1) & 0x3f); + } + else + { + LOG("%1$s FIFO overrun\n", n ? "TX" : "RX"); + return (m_fifo_head[n] - 1) & 0x3f; + } +} + +inline u8 sc16is741a_device::fifo_pop(unsigned n) +{ + if (m_fifo_empty[n]) + { + assert(m_fifo_head[n] == m_fifo_tail[n]); + LOG("%1$s FIFO underrun\n", n ? "TX" : "RX"); + return m_fifo_tail[n]; + } + else if ((m_fifo_head[n] != m_fifo_tail[n]) || FCR_FIFO_ENABLE()) + { + u8 const result(std::exchange(m_fifo_tail[n], (m_fifo_tail[n] + 1) & 0x3f)); + if (m_fifo_head[n] == m_fifo_tail[n]) + m_fifo_empty[n] = true; + return result; + } + else + { + m_fifo_empty[n] = true; + return m_fifo_tail[n]; + } +} + + +TIMER_CALLBACK_MEMBER(sc16is741a_device::rx_shift) +{ + assert(m_divisor); + + m_shift_reg[0] = (m_shift_reg[0] >> 1) | (u16(m_rx) << 15); + --m_rx_remain; + ++m_rx_count; + if (m_rx_remain) + { + m_shift_timer[0]->adjust(attotime::from_ticks(m_divisor * 16, clock())); + } + else + { + u8 const data(BIT(m_shift_reg[0], 16 + 1 - m_rx_count, m_rx_count - ((parity::NONE == m_parity) ? 2 : 3))); + u8 lsr( + (BIT(~m_shift_reg[0], 15) ? 0x08 : 0x00) | + ((!m_fifo_empty[0] && (!FCR_FIFO_ENABLE() || (m_fifo_head[0] == m_fifo_tail[0]))) ? 0x02 : 0x00)); + switch (m_parity) + { + case parity::NONE: + break; + case parity::ODD: + lsr |= BIT(population_count_32(data) ^ BIT(~m_shift_reg[0], 14), 0) << 2; + break; + case parity::EVEN: + lsr |= BIT(population_count_32(data) ^ BIT(m_shift_reg[0], 14), 0) << 2; + break; + case parity::MARK: + lsr |= BIT(~m_shift_reg[0], 14) << 2; + break; + case parity::SPACE: + lsr |= BIT(m_shift_reg[0], 14) << 2; + break; + } + m_shift_reg[0] = 0xffff; + u8 const pos(fifo_push(0)); + if (lsr && (!BIT(lsr, 1) || !m_fifo_data[1][pos])) + ++m_fifo_errors; + m_fifo_data[0][pos] = data; + m_fifo_data[1][pos] = lsr; + u8 const level(fifo_fill_level(0)); + m_rx_timeout_timer->adjust(attotime::from_ticks(m_divisor * 16 / 2 * 4 * m_rx_intervals, clock())); + + if (!(m_interrupts & INTERRUPT_LINE_STATUS)) + { + if (lsr) + { + assert(1 == m_fifo_errors); + LOG("data error, setting line status interrupt\n"); + m_interrupts |= INTERRUPT_LINE_STATUS; + update_irq(); + } + } + + if (!(m_interrupts & INTERRUPT_RHR)) + { + if (FCR_FIFO_ENABLE()) + { + if (level >= m_rx_trigger) + { + LOG("RX FIFO level %1$u exceeds %2$u, setting RHR interrupt\n", level, m_rx_trigger); + m_interrupts |= INTERRUPT_RHR; + update_irq(); + } + } + else + { + LOG("RHR full, setting RHR interrupt\n"); + m_interrupts |= INTERRUPT_RHR; + update_irq(); + } + } + + if (EFR_AUTO_RTS() && !m_rts) // FIXME: check EFCR[4] + { + if (FCR_FIFO_ENABLE()) + { + u8 const trigger(TCR_LEVEL_HALT()); + if (level >= (trigger * 4)) + { + LOG("RX FIFO level %1$u exceeds %2$u*4, deasserting RTS\n", level, trigger); + set_rts(1); + } + } + else + { + LOG("RHR full, deasserting RTS\n"); + set_rts(1); + } + } + } +} + +TIMER_CALLBACK_MEMBER(sc16is741a_device::tx_shift) +{ + assert(m_divisor); + + if (!BIT(++m_tx_count, 0)) + { + m_shift_reg[1] = (m_shift_reg[1] >> 1) | u16(0x8000); + update_tx(); + } + + if (--m_tx_remain) + m_shift_timer[1]->adjust(attotime::from_ticks(m_divisor * 16 / 2, clock())); + else if (!check_tx()) + m_shift_timer[1]->reset(); +} + +TIMER_CALLBACK_MEMBER(sc16is741a_device::rx_timeout) +{ + if (IER_RHR_INT() && !(m_interrupts & INTERRUPT_RX_TIMEOUT)) + { + LOG("setting RX timeout interrupt\n"); + m_interrupts |= INTERRUPT_RX_TIMEOUT; + update_irq(); + } +} + + +inline void sc16is741a_device::update_trigger_levels() +{ + u8 const rx_level(BIT(m_tlr, 4, 4)); + u8 const tx_level(BIT(m_tlr, 0, 4)); + m_rx_trigger = rx_level ? (rx_level * 4) : RX_TRIGGER_LEVELS[FCR_RX_TRIGGER()]; + m_tx_trigger = tx_level ? (tx_level * 4) : TX_TRIGGER_LEVELS[FCR_TX_TRIGGER()]; +} + +inline void sc16is741a_device::update_data_frame() +{ + m_word_length = BIT(m_lcr, 0, 2) + 5; + if (!LCR_PARITY_ENABLE()) + m_parity = parity::NONE; + else if (!LCR_SET_PARITY()) + m_parity = LCR_EVEN_PARITY() ? parity::EVEN : parity::ODD; + else + m_parity = LCR_EVEN_PARITY() ? parity::SPACE : parity::MARK; + u8 const stop(!LCR_STOP_BIT() ? 2 : (5 == m_word_length) ? 3 : 4); + m_rx_intervals = m_word_length + ((parity::NONE == m_parity) ? 2 : 3); + m_tx_intervals = ((m_word_length + ((parity::NONE == m_parity) ? 1 : 2)) * 2) + stop; +} + +inline void sc16is741a_device::update_divisor() +{ + bool const zero(!m_divisor); + m_divisor = u32(m_dl) * (MCR_CLOCK_DIV4() ? 4 : 1); + if (!zero && !m_divisor) + { + if (m_rx_remain) + { + // FIXME: receive shift register immediately transferred to RHR + LOG("suspending reception due to zero divisor\n"); + m_rx_remain = 0; + m_shift_timer[0]->reset(); + } + + if (!m_shift_timer[1]->expire().is_never()) + { + LOG("suspending transmission due to zero divisor\n"); + m_shift_timer[1]->reset(); + } + + m_rx_timeout_timer->reset(); + } + else if (zero && m_divisor) + { + if (m_tx_remain && m_shift_timer[1]->expire().is_never()) + { + LOG("non-zero divisor caused transmission to resume\n"); + m_shift_timer[1]->adjust(attotime::from_ticks(m_divisor * 16 / 2, clock())); + } + } +} diff --git a/src/devices/machine/sc16is741.h b/src/devices/machine/sc16is741.h new file mode 100644 index 00000000000..ff445e68ec7 --- /dev/null +++ b/src/devices/machine/sc16is741.h @@ -0,0 +1,132 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +// I²C/SPI UART with 64-byte transmit and receive FIFOs +#ifndef MAME_MACHINE_SC16IS741_H +#define MAME_MACHINE_SC16IS741_H + +#pragma once + + +class sc16is741a_device : public device_t +{ +public: + sc16is741a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + virtual ~sc16is741a_device(); + + auto so_cb() { return m_so_cb.bind(); } + auto irq_cb() { return m_irq_cb.bind(); } + auto tx_cb() { return m_tx_cb.bind(); } + auto rts_cb() { return m_rts_cb.bind(); } + + void sclk_w(int state); + void cs_w(int state); + void si_w(int state); + void rx_w(int state); + void cts_w(int state); + +protected: + virtual void device_resolve_objects() override ATTR_COLD; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_post_load() override ATTR_COLD; + +private: + static inline constexpr u8 FIFO_LENGTH = 64; + + enum class phase : u8; + enum class parity : u8; + enum interrupt : u8; + + void update_irq(); + void update_tx(); + void set_rts(u8 state); + + void reg_r(bool first); + void reg_w(); + + void iir_r(bool first); + void lsr_r(bool first); + void msr_r(bool first); + void txlvl_r(bool first); + void rxlvl_r(bool first); + void xon_xoff_r(bool first); + + void thr_w(); + void ier_w(); + void fcr_w(); + void lcr_w(); + void mcr_w(); + void tcr_w(); + void tlr_w(); + void reserved_w(); + void uart_reset_w(); + void dl_w(); + void efr_w(); + void xon_xoff_w(); + + void pop_rx_fifo(); + bool check_tx(); + + u8 fifo_spaces(unsigned n) const; + u8 fifo_fill_level(unsigned n) const; + void fifo_reset(unsigned n); + u8 fifo_push(unsigned n); + u8 fifo_pop(unsigned n); + + TIMER_CALLBACK_MEMBER(rx_shift); + TIMER_CALLBACK_MEMBER(tx_shift); + TIMER_CALLBACK_MEMBER(rx_timeout); + + void update_trigger_levels(); + void update_data_frame(); + void update_divisor(); + + devcb_write_line m_so_cb; + devcb_write_line m_irq_cb; + devcb_write_line m_tx_cb; + devcb_write_line m_rts_cb; + + emu_timer *m_shift_timer[2]; + emu_timer *m_rx_timeout_timer; + + u8 m_irq, m_tx, m_rts; + u8 m_rx, m_cts; + + u8 m_sclk, m_cs, m_si; + phase m_phase; + u8 m_bits, m_buffer; + u8 m_command; + + u8 m_ier; + u8 m_fcr; + u8 m_lcr; + u8 m_mcr; + u8 m_spr; + u8 m_tcr; + u8 m_tlr; + u16 m_dl; + u8 m_efr; + u8 m_xon_xoff[4]; + + u16 m_shift_reg[2]; + u8 m_rx_remain, m_rx_count; + u8 m_tx_remain, m_tx_count; + + u8 m_fifo_head[2], m_fifo_tail[2]; + bool m_fifo_empty[2]; + u8 m_fifo_data[3][FIFO_LENGTH]; + u8 m_fifo_errors; + + u8 m_interrupts; + + u32 m_divisor; + u8 m_word_length; + parity m_parity; + u8 m_rx_intervals, m_tx_intervals; + u8 m_rx_trigger, m_tx_trigger; +}; + + +DECLARE_DEVICE_TYPE(SC16IS741A, sc16is741a_device) + +#endif // MAME_MACHINE_SC16IS741_H diff --git a/src/devices/machine/spi_psram.cpp b/src/devices/machine/spi_psram.cpp index 659ae3ad32c..b463c8adcfa 100644 --- a/src/devices/machine/spi_psram.cpp +++ b/src/devices/machine/spi_psram.cpp @@ -30,9 +30,13 @@ Example PSRAM: * AP Memory APS1604L-SQ (2 MiB) + * AP Memory APS1604M-SQ (2 MiB) * AP Memory APS3204L-SQ (4 MiB) + * AP Memory APS3204M-SQ (4 MiB) * AP Memory APS6404L-SQ (8 MiB) + * AP Memory APS6404M-SQ (8 MiB) * AP Memory APS12804L-SQ (16 MiB) + * AP Memory APS12804M-SQ (16 MiB) * ISS IS66WVS1M8 (1 MiB) * ISS IS66WVS2M8 (2 MiB) * ISS IS66WVS8M8 (8 MiB) @@ -54,30 +58,20 @@ #include "spi_psram.h" -DEFINE_DEVICE_TYPE(SPI_PSRAM, spi_psram_device, "spi_psram", "Generic SPI RAM") +DEFINE_DEVICE_TYPE(SPI_RAM, spi_ram_device, "spi_ram", "Generic SPI RAM") +DEFINE_DEVICE_TYPE(SPI_PSRAM, spi_psram_device, "spi_psram", "Generic SPI/QPI Pseudo-SRAM") -ALLOW_SAVE_TYPE(spi_psram_device::phase) - -enum class spi_psram_device::phase : u8 -{ - IDLE, - COMMAND, - ADDRESS, - WAIT, - READ, - WRITE -}; - - -enum spi_psram_device::command : u8 +enum spi_ram_device::command : u8 { COMMAND_READ = 0x03, COMMAND_FAST_READ = 0x0b, // 8 wait cycles in SPI mode, 4 wait cycles in QPI mode COMMAND_FAST_READ_QUAD = 0xeb, // 6 wait cycles, always 4-bit address and data + COMMAND_WRAPPED_READ = 0x8b, // 8 wait cycles in SPI mode, 5 wait cycles in QPI mode COMMAND_WRITE = 0x02, COMMAND_WRITE_QUAD = 0x38, // always 4-bit address and data + COMMAND_WRAPPED_WRITE = 0x82, COMMAND_QPI_ENTER = 0x35, COMMAND_QPI_EXIT = 0xf5, @@ -100,20 +94,38 @@ enum spi_psram_device::command : u8 }; -spi_psram_device::spi_psram_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : - device_t(mconfig, SPI_PSRAM, tag, owner, clock), +ALLOW_SAVE_TYPE(spi_ram_device::phase) + +enum class spi_ram_device::phase : u8 +{ + IDLE, + COMMAND, + ADDRESS, + WAIT, + READ, + WRITE +}; + + +spi_ram_device::spi_ram_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : + spi_ram_device(mconfig, SPI_RAM, tag, owner, clock) +{ +} + +spi_ram_device::spi_ram_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) : + device_t(mconfig, type, tag, owner, clock), m_sio_cb(*this), m_ram(), m_size(0) { } -spi_psram_device::~spi_psram_device() +spi_ram_device::~spi_ram_device() { } -void spi_psram_device::ce_w(int state) +void spi_ram_device::ce_w(int state) { if (state) { @@ -130,7 +142,7 @@ void spi_psram_device::ce_w(int state) m_ce = state ? 1 : 0; } -void spi_psram_device::sclk_w(int state) +void spi_ram_device::sclk_w(int state) { switch (m_phase) { @@ -139,12 +151,13 @@ void spi_psram_device::sclk_w(int state) case phase::WRITE: if (state && !m_sclk) { - m_buffer = (m_buffer << m_cmd_width) | (m_sio & util::make_bitmask(m_cmd_width)); + m_buffer = (m_buffer << m_data_width) | (m_sio & util::make_bitmask(m_data_width)); m_bits -= m_data_width; if (!m_bits) { if (phase::COMMAND == m_phase) { + m_phase = phase::IDLE; m_cmd = u8(m_buffer); start_command(); } @@ -187,25 +200,37 @@ void spi_psram_device::sclk_w(int state) } break; + case phase::WAIT: + if (state && !m_sclk) + { + m_bits -= m_data_width; + if (!m_bits) + { + m_bits = 8; + m_phase = m_next_phase; + } + } + break; + default: break; } m_sclk = state ? 1 : 0; } -void spi_psram_device::sio_w(offs_t offset, u8 data, u8 mem_mask) +void spi_ram_device::sio_w(offs_t offset, u8 data, u8 mem_mask) { m_sio = data & 0xf; } -void spi_psram_device::device_validity_check(validity_checker &valid) const +void spi_ram_device::device_validity_check(validity_checker &valid) const { if (!m_size || (m_size & (m_size - 1)) || (m_size > 0x0100'0000)) osd_printf_error("Unsupported size %u (must be a power of 2 not larger than 16M)\n", m_size); } -void spi_psram_device::device_resolve_objects() +void spi_ram_device::device_resolve_objects() { m_wrap_mask = util::make_bitmask(10); m_addr = 0; @@ -213,15 +238,17 @@ void spi_psram_device::device_resolve_objects() m_cmd_width = 1; m_data_width = 1; m_bits = 0; + m_wait = 0; m_ce = 1; m_sclk = 0; m_sio = 0xf; m_phase = phase::IDLE; + m_next_phase = phase::IDLE; m_cmd = 0; } -void spi_psram_device::device_start() +void spi_ram_device::device_start() { if (!m_size || (m_size & (m_size - 1)) || (m_size > 0x0100'0000)) osd_printf_error("%s: Unsupported size %u (must be a power of 2 not larger than 16M)\n", tag(), m_size); @@ -235,64 +262,77 @@ void spi_psram_device::device_start() save_item(NAME(m_cmd_width)); save_item(NAME(m_data_width)); save_item(NAME(m_bits)); + save_item(NAME(m_wait)); save_item(NAME(m_ce)); save_item(NAME(m_sclk)); save_item(NAME(m_sio)); save_item(NAME(m_phase)); + save_item(NAME(m_next_phase)); save_item(NAME(m_cmd)); } -void spi_psram_device::start_command() +void spi_ram_device::start_command() { - switch (m_cmd) + switch (cmd()) { case COMMAND_READ: - // FIXME: AP Memory devices don't support this command in QPI mode - m_buffer = 0; - m_data_width = m_cmd_width; - m_bits = 24; - m_phase = phase::ADDRESS; - break; + // FIXME: wait cycles depend on mode and device family + start_read(m_cmd_width, 0); + return; case COMMAND_WRITE: - m_buffer = 0; - m_data_width = m_cmd_width; - m_bits = 24; - m_phase = phase::ADDRESS; - break; + start_write(m_cmd_width); + return; default: - logerror("unimplemented command 0x%02x\n", m_cmd); - m_phase = phase::IDLE; + logerror("unimplemented command 0x%02x in %u-bit mode\n", cmd(), m_cmd_width); } } -void spi_psram_device::address_complete() +void spi_ram_device::address_complete() { - switch (m_cmd) + if (m_wait) { - case COMMAND_READ: - // FIXME: wait cycles depend on mode and device family - m_buffer = m_ram[m_addr]; - m_data_width = m_cmd_width; + m_phase = phase::WAIT; + m_bits = m_wait; + } + else + { + m_buffer = (phase::READ == m_next_phase) ? m_ram[m_addr] : 0; m_bits = 8; - m_phase = phase::READ; - break; + m_phase = m_next_phase; + } +} - case COMMAND_WRITE: - m_buffer = 0; - m_data_width = m_cmd_width; - m_bits = 8; - m_phase = phase::WRITE; - break; - default: - throw false; // if we get here, there's a bug in the code - } +inline void spi_ram_device::set_cmd_width(u8 width) +{ + m_cmd_width = width; } -inline void spi_psram_device::next_address() +inline void spi_ram_device::start_read(u8 width, u8 wait) +{ + m_buffer = 0; + m_data_width = width; + m_bits = 24; + m_wait = wait * width; + m_phase = phase::ADDRESS; + m_next_phase = phase::READ; +} + +inline void spi_ram_device::start_write(u8 width) +{ + m_buffer = 0; + m_data_width = width; + m_bits = 24; + m_wait = 0; + m_phase = phase::ADDRESS; + m_next_phase = phase::WRITE; +} + + +inline void spi_ram_device::next_address() { if (m_wrap_mask) { @@ -304,3 +344,84 @@ inline void spi_psram_device::next_address() m_phase = phase::IDLE; } } + + + +spi_psram_device::spi_psram_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : + spi_ram_device(mconfig, SPI_PSRAM, tag, owner, clock) +{ +} + +spi_psram_device::~spi_psram_device() +{ +} + + +void spi_psram_device::device_resolve_objects() +{ + spi_ram_device::device_resolve_objects(); + + m_reset_enable = false; +} + +void spi_psram_device::device_start() +{ + spi_ram_device::device_start(); + + save_item(NAME(m_reset_enable)); +} + + +void spi_psram_device::start_command() +{ + bool const reset_enable(std::exchange(m_reset_enable, false)); + switch (cmd()) + { + case COMMAND_READ: + if (cmd_width() == 4) + { + // FIXME: AP Memory and Vilsion Technology devices don't support command 0x03 in QPI mode + start_read(4, 4); + return; + } + break; + + case COMMAND_FAST_READ: + start_read(cmd_width(), (cmd_width() == 4) ? 4 : 8); + return; + + case COMMAND_FAST_READ_QUAD: + start_read(4, 6); + return; + + case COMMAND_WRITE_QUAD: + start_write(4); + return; + + case COMMAND_QPI_ENTER: + if (cmd_width() == 1) + { + set_cmd_width(4); + return; + } + break; + + case COMMAND_QPI_EXIT: + if (cmd_width() == 4) + { + set_cmd_width(1); + return; + } + break; + + case COMMAND_RESET_ENABLE: + m_reset_enable = true; + return; + + case COMMAND_RESET: + if (reset_enable) + set_cmd_width(1); + return; + } + spi_ram_device::start_command(); +} diff --git a/src/devices/machine/spi_psram.h b/src/devices/machine/spi_psram.h index d2bc5923763..2bbc858b30f 100644 --- a/src/devices/machine/spi_psram.h +++ b/src/devices/machine/spi_psram.h @@ -9,11 +9,11 @@ #include -class spi_psram_device : public device_t +class spi_ram_device : public device_t { public: - spi_psram_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0U); - virtual ~spi_psram_device(); + spi_ram_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0U); + virtual ~spi_ram_device(); void set_size(u32 size) { m_size = size; } auto sio_cb() { return m_sio_cb.bind(); } @@ -24,17 +24,28 @@ public: void si_w(int state) { sio_w(0, state ? 0xf : 0xe, 0x1); } protected: + enum command : u8; + + spi_ram_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock); + virtual void device_validity_check(validity_checker &valid) const override ATTR_COLD; virtual void device_resolve_objects() override ATTR_COLD; virtual void device_start() override ATTR_COLD; - void start_command(); - void address_complete(); - void next_address(); + virtual void start_command(); + + u8 cmd_width() const { return m_cmd_width; } + u8 cmd() const { return m_cmd; } + + void set_cmd_width(u8 width); + void start_read(u8 width, u8 wait); + void start_write(u8 width); private: enum class phase : u8; - enum command : u8; + + void address_complete(); + void next_address(); devcb_write8 m_sio_cb; @@ -45,13 +56,32 @@ private: u32 m_buffer; u8 m_cmd_width, m_data_width; u8 m_bits; + u8 m_wait; u8 m_ce, m_sclk, m_sio; - phase m_phase; + phase m_phase, m_next_phase; u8 m_cmd; }; +class spi_psram_device : public spi_ram_device +{ +public: + spi_psram_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0U); + virtual ~spi_psram_device(); + +protected: + virtual void device_resolve_objects() override ATTR_COLD; + virtual void device_start() override ATTR_COLD; + + virtual void start_command() override; + +private: + bool m_reset_enable; +}; + + +DECLARE_DEVICE_TYPE(SPI_RAM, spi_ram_device) DECLARE_DEVICE_TYPE(SPI_PSRAM, spi_psram_device) #endif // MAME_MACHINE_SPI_PSRAM_H diff --git a/src/devices/machine/spi_sdcard.cpp b/src/devices/machine/spi_sdcard.cpp index ec4267b9ec3..5fd7526ef25 100644 --- a/src/devices/machine/spi_sdcard.cpp +++ b/src/devices/machine/spi_sdcard.cpp @@ -41,6 +41,7 @@ #include "logmacro.h" + namespace { constexpr u8 DATA_RESPONSE_OK = 0x05; @@ -152,16 +153,17 @@ spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type write_miso(*this), m_image(*this, "image"), m_preferred_type(SD_TYPE_V2), - m_ignore_stop_bit(false), m_blksize(512), m_type(SD_TYPE_V2), m_state(SD_STATE_IDLE), m_ss(0), m_in_bit(0), m_clk_state(0), m_in_latch(0), m_out_latch(0xff), m_cur_bit(0), m_out_delay(0), m_out_count(0), m_out_ptr(0), m_write_ptr(0), m_xferblk(512), m_blknext(0), + m_crc_off(true), m_bACMD(false) { std::fill(std::begin(m_csd), std::end(m_csd), 0); + std::fill(std::begin(m_cmd), std::end(m_cmd), 0xff); } spi_sdcard_device::~spi_sdcard_device() @@ -187,6 +189,7 @@ void spi_sdcard_device::device_start() save_item(NAME(m_write_ptr)); save_item(NAME(m_xferblk)); save_item(NAME(m_blknext)); + save_item(NAME(m_crc_off)); save_item(NAME(m_bACMD)); } @@ -238,6 +241,7 @@ std::error_condition spi_sdcard_device::image_loaded(device_image_interface &ima } m_blksize = m_xferblk = info.sectorbytes; + m_crc_off = true; // set up common CSD fields m_csd[0] = 0x00; // 127: CSD_STRUCTURE:2 (00b) 0:6 @@ -354,6 +358,26 @@ void spi_sdcard_device::spi_clock_w(int state) m_clk_state = state; } +void spi_sdcard_device::spi_ss_w(int state) +{ + if (!m_ss && state) + { + LOGMASKED(LOG_SPI, "SDCARD: selected\n"); + std::fill(std::begin(m_cmd), std::end(m_cmd), 0xff); + m_state = SD_STATE_IDLE; + m_in_latch = 0; + m_cur_bit = 0; + m_out_latch = 0xff; + m_out_delay = 0; + m_out_count = 0; + } + else if (m_ss && !state) + { + LOGMASKED(LOG_SPI, "SDCARD: deselected\n"); + } + m_ss = state; +} + void spi_sdcard_device::latch_in() { m_in_latch &= ~0x01; @@ -446,7 +470,7 @@ void spi_sdcard_device::shift_out() void spi_sdcard_device::do_command() { - if (((m_cmd[0] & 0xc0) == 0x40) && ((m_cmd[5] & 1) || m_ignore_stop_bit)) + if (((m_cmd[0] & 0xc0) == 0x40) && ((m_cmd[5] & 1) || m_crc_off)) { LOGMASKED(LOG_COMMAND, "SDCARD: cmd %02d %02x %02x %02x %02x %02x\n", m_cmd[0] & 0x3f, m_cmd[1], m_cmd[2], m_cmd[3], m_cmd[4], m_cmd[5]); bool clean_cmd = true; @@ -682,8 +706,8 @@ void spi_sdcard_device::do_command() break; case 59: // CMD59 - CRC_ON_OFF + m_crc_off = !BIT(m_cmd[4], 0); m_data[0] = 0; - // TODO CRC 1-on, 0-off send_data(1, SD_STATE_STBY); break; diff --git a/src/devices/machine/spi_sdcard.h b/src/devices/machine/spi_sdcard.h index fc907370972..8eb9ec90d34 100644 --- a/src/devices/machine/spi_sdcard.h +++ b/src/devices/machine/spi_sdcard.h @@ -18,12 +18,11 @@ public: void set_prefer_sd() { m_preferred_type = SD_TYPE_V2; } void set_prefer_sdhc() { m_preferred_type = SD_TYPE_HC; } - void set_ignore_stop_bit(bool ignore) { m_ignore_stop_bit = ignore; } // SPI 4-wire interface auto spi_miso_callback() { return write_miso.bind(); } void spi_clock_w(int state); - void spi_ss_w(int state) { m_ss = state; } + void spi_ss_w(int state); void spi_mosi_w(int state) { m_in_bit = state; } bool get_card_present() { return m_image->exists(); } @@ -62,7 +61,6 @@ private: // configuration sd_type m_preferred_type; - bool m_ignore_stop_bit; // mounted image info std::vector m_sectorbuf; @@ -80,6 +78,7 @@ private: u16 m_out_count, m_out_ptr, m_write_ptr; u16 m_xferblk; u32 m_blknext; + bool m_crc_off; bool m_bACMD; }; diff --git a/src/emu/diserial.cpp b/src/emu/diserial.cpp index 4f31c91ca02..2d900f27459 100644 --- a/src/emu/diserial.cpp +++ b/src/emu/diserial.cpp @@ -45,21 +45,6 @@ device_serial_interface::device_serial_interface(const machine_config &mconfig, m_tra_clock_state(false), m_rcv_clock_state(false) { - /* if sum of all bits in the byte is even, then the data - has even parity, otherwise it has odd parity */ - for (int i=0; i<256; i++) - { - int sum = 0; - int data = i; - - for (int b=0; b<8; b++) - { - sum+=data & 0x01; - data = data>>1; - } - - m_serial_parity_table[i] = sum & 0x01; - } } device_serial_interface::~device_serial_interface() @@ -317,13 +302,13 @@ void device_serial_interface::receive_register_extract() receive_register_reset(); /* strip off stop bits and parity */ - assert(m_rcv_bit_count >0 && m_rcv_bit_count <= 16); - data = m_rcv_register_data>>(16-m_rcv_bit_count); + assert(m_rcv_bit_count > 0 && m_rcv_bit_count <= 16); + data = m_rcv_register_data >> (16 - m_rcv_bit_count); /* mask off other bits so data byte has 0's in unused bits */ - data &= ~(0xff<adjust(m_tra_rate, 0, m_tra_rate); m_tra_bit_count_transmitted = 0; @@ -390,40 +373,35 @@ void device_serial_interface::transmit_register_setup(u8 data_byte) m_tra_flags &=~TRANSMIT_REGISTER_EMPTY; /* start bit */ - for (i=0; i>1; + transmit_register_add_bit(BIT(transmit_data, 0)); + transmit_data >>= 1; } /* parity */ - if (m_df_parity!=PARITY_NONE) + if (m_df_parity != PARITY_NONE) { /* odd or even parity */ u8 parity = 0; switch (m_df_parity) { case PARITY_ODD: - - /* get parity */ - /* if parity = 0, data has even parity - i.e. there is an even number of one bits in the data */ - /* if parity = 1, data has odd parity - i.e. there is an odd number of one bits in the data */ - parity = serial_helper_get_parity(data_byte) ^ 1; + // get parity + // if parity[0] = 0, data has even parity - i.e. there is an even number of one bits in the data + // if parity[0] = 1, data has odd parity - i.e. there is an odd number of one bits in the data + parity = BIT(population_count_32(data_byte), 0) ^ 1; break; case PARITY_EVEN: - parity = serial_helper_get_parity(data_byte); + parity = BIT(population_count_32(data_byte), 0); break; case PARITY_MARK: parity = 1; @@ -436,7 +414,7 @@ void device_serial_interface::transmit_register_setup(u8 data_byte) } /* TX stop bit(s) */ - for (i=0; i + +#include "lnux4004.lh" + + +namespace { + +class linux4004_state : public driver_device +{ +public: + static constexpr feature_type unemulated_features() { return feature::GRAPHICS; } + + linux4004_state(machine_config const &mconfig, device_type type, char const *tag) + : driver_device(mconfig, type, tag) + , m_cpu(*this, "u1") + , m_psram(*this, "u%u", 5U) + , m_sdcard(*this, "sd") + , m_uart(*this, "u9") + , m_memory(*this, "memory") + , m_status(*this, "status") + , m_rom_bank(*this, "rom") + , m_led_pc(*this, "pc%u", 0U) + , m_led_sdcard(*this, "storage") + { + } + + void linux4004(machine_config &config) ATTR_COLD; + +protected: + virtual void machine_start() override ATTR_COLD; + virtual void machine_reset() override ATTR_COLD; + +private: + template void psram_sio_w(offs_t offset, u8 data, u8 mem_mask); + template void miso_w(int state); + + u8 u3_r(); + void u4002_1_3_w(u8 data); + void u4002_1_4_w(u8 data); + void u4002_2_3_w(u8 data); + template void led_pc_w(offs_t offset, u8 data); + + void umips_rom(address_map &map) ATTR_COLD; + void umips_ram(address_map &map) ATTR_COLD; + void umips_status(address_map &map) ATTR_COLD; + void umips_rom_ports(address_map &map) ATTR_COLD; + void umips_ram_ports(address_map &map) ATTR_COLD; + + required_device m_cpu; + required_device_array m_psram; + required_device m_sdcard; + required_device m_uart; + required_shared_ptr m_memory; + required_shared_ptr m_status; + required_memory_bank m_rom_bank; + + output_finder<32> m_led_pc; + output_finder<> m_led_sdcard; + + u8 m_psram_so[2]; + u8 m_u3_in; +}; + + +INPUT_PORTS_START(linux4004) + PORT_START("CONF") + PORT_CONFNAME(0x03, 0x00, "TLB Entries") + PORT_CONFSETTING( 0x03, "4") + PORT_CONFSETTING( 0x02, "8") + PORT_CONFSETTING( 0x01, "12") + PORT_CONFSETTING( 0x00, "16") + PORT_CONFNAME(0x04, 0x00, "U4002-2-4 Installed") + PORT_CONFSETTING( 0x00, DEF_STR(No)) + PORT_CONFSETTING( 0x04, DEF_STR(Yes)) +INPUT_PORTS_END + + +void linux4004_state::linux4004(machine_config &config) +{ + config.set_default_layout(layout_lnux4004); + + I4004(config, m_cpu, 5.5296_MHz_XTAL / 7); + m_cpu->set_rom_map(&linux4004_state::umips_rom); + m_cpu->set_ram_memory_map(&linux4004_state::umips_ram); + m_cpu->set_ram_status_map(&linux4004_state::umips_status); + m_cpu->set_rom_ports_map(&linux4004_state::umips_rom_ports); + m_cpu->set_ram_ports_map(&linux4004_state::umips_ram_ports); + + SPI_PSRAM(config, m_psram[0]); + m_psram[0]->set_size(8 << 20); // supports 4 MiB to 16 MiB + m_psram[0]->sio_cb().set(FUNC(linux4004_state::psram_sio_w<0>)); + + SPI_PSRAM(config, m_psram[1]); + m_psram[1]->set_size(4 << 20); // supports 128 KiB to 16 MiB + m_psram[1]->sio_cb().set(FUNC(linux4004_state::psram_sio_w<1>)); + + SPI_SDCARD(config, m_sdcard, 0U); + m_sdcard->spi_miso_callback().set(FUNC(linux4004_state::miso_w<3>)); + + SC16IS741A(config, m_uart, 3.072_MHz_XTAL); + m_uart->so_cb().set(FUNC(linux4004_state::miso_w<2>)); + m_uart->irq_cb().set_inputline(m_cpu, I4004_TEST_LINE).invert(); + m_uart->tx_cb().set("rs232", FUNC(rs232_port_device::write_txd)); + m_uart->rts_cb().set("rs232", FUNC(rs232_port_device::write_rts)); + + auto &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal")); + rs232.rxd_handler().set(m_uart, FUNC(sc16is741a_device::rx_w)); + rs232.cts_handler().set(m_uart, FUNC(sc16is741a_device::cts_w)); + + SOFTWARE_LIST(config, "sdcard_list").set_original("lnux4004"); +} + + +void linux4004_state::machine_start() +{ + m_rom_bank->configure_entries(0, 2, memregion("4004firmware")->base(), 0x1000); + m_led_pc.resolve(); + m_led_sdcard.resolve(); + + std::fill(std::begin(m_psram_so), std::end(m_psram_so), 1); + m_u3_in = 0; + + save_item(NAME(m_psram_so)); + save_item(NAME(m_u3_in)); +} + +void linux4004_state::machine_reset() +{ + ioport_value const conf(ioport("CONF")->read()); + auto const tlb_empty(BIT(conf, 0, 2)); + auto const u4002_2_4(BIT(conf, 2)); + + m_cpu->space(i4004_cpu_device::AS_RAM_MEMORY).unmap_readwrite(0x0000, 0x02ff); + m_cpu->space(i4004_cpu_device::AS_RAM_STATUS).unmap_readwrite(0x00, 0xbf); + m_cpu->space(i4004_cpu_device::AS_RAM_PORTS).unmap_readwrite(0x08, 0x0b); + m_cpu->space(i4004_cpu_device::AS_RAM_MEMORY).install_ram(0x0000, 0x02ff - (tlb_empty * 0x40), m_memory.target()); + m_cpu->space(i4004_cpu_device::AS_RAM_STATUS).install_ram(0x00, 0xbf - (tlb_empty * 0x10), m_status.target()); + m_cpu->space(i4004_cpu_device::AS_RAM_PORTS).install_write_handler(0x08, 0x0b - tlb_empty, emu::rw_delegate(*this, FUNC(linux4004_state::led_pc_w<4>))); + if (!u4002_2_4) + { + m_cpu->space(i4004_cpu_device::AS_RAM_MEMORY).unmap_readwrite(0x01c0, 0x01ff); + m_cpu->space(i4004_cpu_device::AS_RAM_STATUS).unmap_readwrite(0x70, 0x7f); + } + + std::fill(m_memory.begin(), m_memory.end(), 0); + std::fill(m_status.begin(), m_status.end(), 0); + + u4002_1_3_w(0); + u4002_1_4_w(0); + u4002_2_3_w(0); + for (auto &led : m_led_pc) + led = 0; +} + + +template +void linux4004_state::psram_sio_w(offs_t offset, u8 data, u8 mem_mask) +{ + m_psram_so[N] = BIT(data | ~mem_mask, 1); + miso_w<0>(m_psram_so[0] & m_psram_so[1]); +} + +template +void linux4004_state::miso_w(int state) +{ + m_u3_in = (m_u3_in & ~(u8(1) << N)) | (u8(state ? 1 : 0) << N); +} + + +u8 linux4004_state::u3_r() +{ + return m_u3_in; +} + +void linux4004_state::u4002_1_3_w(u8 data) +{ + m_sdcard->spi_mosi_w(BIT(~data, 0)); + m_sdcard->spi_clock_w(BIT(~data, 1)); + m_sdcard->spi_ss_w(BIT(data, 2) ? ASSERT_LINE : CLEAR_LINE); + m_led_sdcard = BIT(~data, 2); + m_rom_bank->set_entry(BIT(data, 3) ^ 0x01); +} + +void linux4004_state::u4002_1_4_w(u8 data) +{ + m_uart->si_w(BIT(~data, 0)); // TODO: also connected to VFD + m_uart->sclk_w(BIT(~data, 1)); // TODO: also connected to VFD + // VFD_NCS_HV + m_uart->cs_w(BIT(~data, 3)); +} + +void linux4004_state::u4002_2_3_w(u8 data) +{ + m_psram[0]->si_w(BIT(~data, 0)); + m_psram[1]->si_w(BIT(~data, 0)); + m_psram[0]->sclk_w(BIT(~data, 1)); + m_psram[1]->sclk_w(BIT(~data, 1)); + m_psram[0]->ce_w(BIT(~data, 2)); + m_psram[1]->ce_w(BIT(~data, 3)); +} + +template +void linux4004_state::led_pc_w(offs_t offset, u8 data) +{ + for (unsigned i = 0; 4 > i; ++i) + m_led_pc[i | ((N + offset) << 2)] = BIT(data, i); +} + + +void linux4004_state::umips_rom(address_map &map) +{ + map.unmap_value_low(); + map.global_mask(0x0fff); + map(0x0000, 0x0fff).bankr(m_rom_bank); +} + +void linux4004_state::umips_ram(address_map &map) +{ + map.unmap_value_low(); + map(0x0000, 0x02ff).ram().share(m_memory); // up to twelve 4002 chips +} + +void linux4004_state::umips_status(address_map &map) +{ + map.unmap_value_low(); + map(0x00, 0xbf).ram().share(m_status); // up to twelve 4002 chips +} + +void linux4004_state::umips_rom_ports(address_map &map) +{ + map.unmap_value_high(); + map.global_mask(0x0ff); + map(0x000, 0x0ff).r(FUNC(linux4004_state::u3_r)); +} + +void linux4004_state::umips_ram_ports(address_map &map) +{ + map(0x00, 0x03).w(FUNC(linux4004_state::led_pc_w<0>)); + map(0x04, 0x04).w(FUNC(linux4004_state::u4002_1_3_w)); + map(0x05, 0x05).w(FUNC(linux4004_state::u4002_1_4_w)); + map(0x06, 0x06).w(FUNC(linux4004_state::u4002_2_3_w)); + map(0x08, 0x0b).w(FUNC(linux4004_state::led_pc_w<4>)); +} + + +ROM_START(lnux4004) + ROM_REGION(0x2000, "4004firmware", 0) + ROM_LOAD("umips.u4", 0x0000, 0x2000, CRC(27dd98c1) SHA1(a9d2b1990e7ae8ce4a53950430c5186d4cf55a01)) // AT28C64B +ROM_END + +} // anonymous namespace + + +SYST( 2024, lnux4004, 0, 0, linux4004, linux4004, linux4004_state, empty_init, "Dmitry Grinberg", "Linux/4004", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE ) diff --git a/src/mame/layout/lnux4004.lay b/src/mame/layout/lnux4004.lay new file mode 100644 index 00000000000..6b69528fed1 --- /dev/null +++ b/src/mame/layout/lnux4004.lay @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/mame.lst b/src/mame/mame.lst index df67a643bbd..173d893ca0e 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -19767,6 +19767,9 @@ gsz80 // Grant Searle's Simple Z-80 Machine @source:homebrew/homez80.cpp homez80 // +@source:homebrew/linux4004.cpp +lnux4004 + @source:homebrew/lft_chiptune.cpp powernin // Power Ninja Action Challenge, by [lft] (2009) hwchiptn // The Hardware Chiptune Project, by [lft] and kryo (2007) -- cgit v1.2.3