diff options
author | 2019-05-26 18:00:33 +0200 | |
---|---|---|
committer | 2019-05-26 12:00:33 -0400 | |
commit | 41c456c3a61b49d71f75e3c90bff3044c621b5f1 (patch) | |
tree | 9f888378b58c4ed6e825065642f1e54797c42d6b | |
parent | ecf043eca1bc1144ac4b9766cfb4ea7b113cc57c (diff) |
Hp9845: added 98046 module emulation (#5115)
* hp9845: fixed handling of optional ROMs (nw)
* z80sio: massive enhancement to Z80 SIO driven by HP98046 test sw
* hp9845: implemented the HP98046 serial I/O module
-rw-r--r-- | scripts/src/bus.lua | 2 | ||||
-rw-r--r-- | src/devices/bus/hp9845_io/98046.cpp | 684 | ||||
-rw-r--r-- | src/devices/bus/hp9845_io/98046.h | 103 | ||||
-rw-r--r-- | src/devices/bus/hp9845_io/hp9845_io.cpp | 60 | ||||
-rw-r--r-- | src/devices/bus/hp9845_io/hp9845_io.h | 16 | ||||
-rw-r--r-- | src/devices/bus/hp_optroms/hp_optrom.cpp | 9 | ||||
-rw-r--r-- | src/devices/bus/hp_optroms/hp_optrom.h | 2 | ||||
-rw-r--r-- | src/devices/machine/z80sio.cpp | 1007 | ||||
-rw-r--r-- | src/devices/machine/z80sio.h | 46 | ||||
-rw-r--r-- | src/mame/drivers/hp9845.cpp | 28 | ||||
-rw-r--r-- | src/mame/includes/hp9845.h | 3 |
11 files changed, 1617 insertions, 343 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 86900efcb62..673553ed3bd 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -3686,6 +3686,8 @@ if (BUSES["HP9845_IO"]~=null) then MAME_DIR .. "src/devices/bus/hp9845_io/98034.h", MAME_DIR .. "src/devices/bus/hp9845_io/98035.cpp", MAME_DIR .. "src/devices/bus/hp9845_io/98035.h", + MAME_DIR .. "src/devices/bus/hp9845_io/98046.cpp", + MAME_DIR .. "src/devices/bus/hp9845_io/98046.h", MAME_DIR .. "src/devices/bus/hp9845_io/hp9885.cpp", MAME_DIR .. "src/devices/bus/hp9845_io/hp9885.h", } diff --git a/src/devices/bus/hp9845_io/98046.cpp b/src/devices/bus/hp9845_io/98046.cpp new file mode 100644 index 00000000000..6a45a83e3df --- /dev/null +++ b/src/devices/bus/hp9845_io/98046.cpp @@ -0,0 +1,684 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + 98046.cpp + + 98046 module (data communications interface) + + Fun fact: I didn't need to dump the fw in 8048 MCU because, + in a way, it's already "dumped" in the test sw. Basically, to + test the correctness of the ROM content, the HP sw reads out the + whole ROM and compares it to the known good image... + + Main reference for this module: + HP, 98046B Interface Installation and Service + + Test software: + HP, 98046-90449, 98046 Interface Test + (see http://www.hpmuseum.net/display_item.php?sw=324) + +*********************************************************************/ + +#include "emu.h" +#include "98046.h" + +// Debugging + +#include "logmacro.h" +#define LOG_IFS_MASK (LOG_GENERAL << 1) +#define LOG_IFS(...) LOGMASKED(LOG_IFS_MASK, __VA_ARGS__) +#define LOG_MCU_MASK (LOG_IFS_MASK << 1) +#define LOG_MCU(...) LOGMASKED(LOG_MCU_MASK, __VA_ARGS__) +#define LOG_CPU_MASK (LOG_MCU_MASK << 1) +#define LOG_CPU(...) LOGMASKED(LOG_CPU_MASK, __VA_ARGS__) +#define LOG_SIO_MASK (LOG_CPU_MASK << 1) +#define LOG_SIO(...) LOGMASKED(LOG_SIO_MASK, __VA_ARGS__) +//#undef VERBOSE +//#define VERBOSE (LOG_GENERAL | LOG_MCU_MASK | LOG_CPU_MASK | LOG_SIO_MASK) + +// Bit manipulation +namespace { + template<typename T> constexpr T BIT_MASK(unsigned n) + { + return (T)1U << n; + } + + template<typename T> void BIT_CLR(T& w , unsigned n) + { + w &= ~BIT_MASK<T>(n); + } + + template<typename T> void BIT_SET(T& w , unsigned n) + { + w |= BIT_MASK<T>(n); + } +} + +// Timers +enum { + TMR_ID_RXC, + TMR_ID_TXC +}; + +// device type definition +DEFINE_DEVICE_TYPE(HP98046_IO_CARD, hp98046_io_card_device , "hp98046" , "HP98046 card") + +hp98046_io_card_device::hp98046_io_card_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : hp9845_io_card_device(mconfig , HP98046_IO_CARD , tag , owner , clock) + , m_cpu(*this , "cpu") + , m_sio(*this , "sio") + , m_rs232(*this , "rs232") + , m_loopback_en(*this , "loop") +{ +} + +hp98046_io_card_device::~hp98046_io_card_device() +{ +} + +READ16_MEMBER(hp98046_io_card_device::reg_r) +{ + uint16_t res = 0; + + switch (offset) { + case 0: + // R4 + // Read from rxFIFO + if (!rx_fifo_flag()) { + m_rxfifo_irq = false; + update_irq(); + } + res = ~m_rx_fifo.dequeue() & 0x1ff; + // Save bit 8 of new word at output of rx FIFO + if (!m_rx_fifo.empty()) { + m_rx_fifo_out_b8 = BIT(m_rx_fifo.peek() , 8); + } + update_flg(); + update_sts(); + break; + + case 1: + // R5 + if (m_rxfifo_overrun) { + BIT_SET(res , 15); + } + BIT_SET(res , 11); + if (m_inten) { + BIT_SET(res , 7); + } + if (!m_r6_r7_pending) { + BIT_SET(res , 0); + } + break; + + case 2: + // R6: not mapped + break; + + case 3: + // R7: not mapped + break; + + default: + break; + } + + LOG_CPU("rd R%u=%04x\n" , offset + 4 , res); + return res; +} + +WRITE16_MEMBER(hp98046_io_card_device::reg_w) +{ + LOG_CPU("wr R%u=%04x\n" , offset + 4 , data); + + switch (offset) { + case 0: + // R4 + // Write to txFIFO + m_tx_fifo_in = (data ^ 0x00ff) & 0x1ff; + m_tx_fifo_pending = true; + load_tx_fifo(); + space.device().execute().yield(); + break; + + case 1: + // R5 + if (BIT(data , 5)) { + // 8048 reset + m_cpu->pulse_input_line(INPUT_LINE_RESET , attotime::zero); + m_inten = false; + } else if (BIT(m_port_2 , 7)) { + // When SIORST is active, inten always sets to 0 + m_inten = false; + } else { + m_inten = BIT(data , 7); + } + m_enoutint = BIT(data , 0); + update_irq(); + break; + + case 2: + // R6 + case 3: + // R7 + m_r6_r7_select = offset == 3; + m_r6_r7 = ~data & 0xff; + set_r6_r7_pending(true); + break; + + default: + break; + } +} + +bool hp98046_io_card_device::has_dual_sc() const +{ + return true; +} + +void hp98046_io_card_device::device_add_mconfig(machine_config &config) +{ + I8048(config , m_cpu , 6_MHz_XTAL); + m_cpu->set_addrmap(AS_PROGRAM , &hp98046_io_card_device::cpu_program_map); + m_cpu->set_addrmap(AS_IO , &hp98046_io_card_device::cpu_io_map); + m_cpu->p1_in_cb().set(FUNC(hp98046_io_card_device::p1_r)); + m_cpu->p2_out_cb().set(FUNC(hp98046_io_card_device::p2_w)); + m_cpu->t1_in_cb().set([this] () { return int(!m_sio_int); }); + // Clock to SIO is actually provided by T0 output of CPU + Z80SIO(config , m_sio , 0); + m_sio->out_int_callback().set(FUNC(hp98046_io_card_device::sio_int_w)); + m_sio->out_txda_callback().set(FUNC(hp98046_io_card_device::sio_txd_w)); + RS232_PORT(config, m_rs232, default_rs232_devices, nullptr); + m_rs232->rxd_handler().set(FUNC(hp98046_io_card_device::rs232_rxd_w)); + m_rs232->dcd_handler().set(FUNC(hp98046_io_card_device::rs232_dcd_w)); + m_rs232->dsr_handler().set(FUNC(hp98046_io_card_device::rs232_dsr_w)); + m_rs232->cts_handler().set(FUNC(hp98046_io_card_device::rs232_cts_w)); + config.m_minimum_quantum = attotime::from_hz(5000); +} + +static INPUT_PORTS_START(hp98046_port) + PORT_START("SC") + PORT_CONFNAME(0xf , 4 - HP9845_IO_FIRST_SC , "Select Codes") + PORT_CONFSETTING(1 , "2 3") + PORT_CONFSETTING(3 , "4 5") + PORT_CONFSETTING(5 , "6 7") + PORT_CONFSETTING(7 , "8 9") + PORT_CONFSETTING(9 , "10 11") + PORT_CONFSETTING(11 , "12 13") + + PORT_START("loop") + PORT_CONFNAME(1 , 0 , "ESK loopback") + PORT_CONFSETTING(0 , DEF_STR(Off)) + PORT_CONFSETTING(1 , DEF_STR(On)) +INPUT_PORTS_END + +ioport_constructor hp98046_io_card_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(hp98046_port); +} + +ROM_START(hp98046) + ROM_REGION(0x400, "cpu" , 0) + ROM_LOAD("1820-2431.bin" , 0 , 0x400 , CRC(e6a068d6) SHA1(d19c35b18fae52b841060ed879f860fd2cae3482)) +ROM_END + +const tiny_rom_entry *hp98046_io_card_device::device_rom_region() const +{ + return ROM_NAME(hp98046); +} + +void hp98046_io_card_device::device_start() +{ + m_ram = std::make_unique<uint8_t[]>(1024); + save_pointer(NAME(m_ram) , 1024); + + m_rxc_timer = timer_alloc(TMR_ID_RXC); + m_txc_timer = timer_alloc(TMR_ID_TXC); +} + +void hp98046_io_card_device::device_reset() +{ + m_port_2 = 0; + m_inten = false; + m_enoutint = false; + update_flg(); + update_sts(); + update_irq(); + m_loopback = m_loopback_en->read() != 0; + // Ensure timers are loaded the 1st time BRGs are configured + m_rxc_sel = ~0; + m_txc_sel = ~0; + m_rxc_timer->reset(); + m_txc_timer->reset(); +} + +void hp98046_io_card_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) { + case TMR_ID_RXC: + m_rxc = !m_rxc; + m_sio->rxca_w(m_rxc); + if (m_loopback && (m_txc_sel == 0 || m_txc_sel == 1)) { + m_sio->txca_w(m_rxc); + m_sio->txcb_w(m_rxc); + m_sio->rxcb_w(m_rxc); + } + break; + + case TMR_ID_TXC: + m_txc = !m_txc; + m_sio->txca_w(m_txc); + m_sio->txcb_w(m_txc); + m_sio->rxcb_w(m_txc); + if (m_loopback && (m_rxc_sel == 0 || m_rxc_sel == 1)) { + m_sio->rxca_w(m_txc); + } + break; + } +} + +void hp98046_io_card_device::cpu_program_map(address_map &map) +{ + map.unmap_value_high(); + map(0x000 , 0x3ff).rom(); + map(0x400 , 0x7ff).r(FUNC(hp98046_io_card_device::ram_r)); +} + +void hp98046_io_card_device::cpu_io_map(address_map &map) +{ + map(0 , 0xff).rw(FUNC(hp98046_io_card_device::cpu_r) , FUNC(hp98046_io_card_device::cpu_w)); +} + +READ8_MEMBER(hp98046_io_card_device::ram_r) +{ + return m_ram[ offset ]; +} + +READ8_MEMBER(hp98046_io_card_device::cpu_r) +{ + if (BIT(m_port_2 , 2)) { + return m_ram[ (offset & 0xff) | (uint16_t(m_port_2 & 3) << 8) ]; + } else if (BIT(offset , 2)) { + uint8_t res = ~0; + + switch (offset & 3) { + case 0: + // xxxx'x100: read from tx FIFO + res = uint8_t(m_tx_fifo.dequeue()); + load_tx_fifo(); + update_flg(); + update_irq(); + break; + + case 1: + // xxxx'x101: read HS + res = get_hs_input(); + break; + + case 2: + // xxxx'x110: clear FIFOs + m_tx_fifo.clear(); + m_rx_fifo.clear(); + load_tx_fifo(); + update_flg(); + update_sts(); + update_irq(); + break; + + case 3: + // xxxx'x111: read r6/r7 + res = m_r6_r7; + set_r6_r7_pending(false); + break; + } + LOG_MCU("CPU R @%02x=%02x\n" , offset , res); + return res; + } else { + uint8_t res = m_sio->cd_ba_r(offset & 3); + LOG_SIO("SIO R @%u=%02x\n" , offset & 3 , res); + return res; + } +} + +WRITE8_MEMBER(hp98046_io_card_device::cpu_w) +{ + if (BIT(m_port_2 , 2)) { + m_ram[ (offset & 0xff) | (uint16_t(m_port_2 & 3) << 8) ] = data; + } else if (BIT(offset , 2)) { + LOG_MCU("CPU W @%02x=%02x\n" , offset , data); + switch (offset & 3) { + case 0: + // xxxx'x100: write to rx FIFO + if (BIT(offset , 6)) { + if (m_rx_fifo.full()) { + m_rxfifo_overrun = true; + } + uint16_t w = data; + if (BIT(offset , 7)) { + BIT_SET(w , 8); + } + // If enqueuing first word, store bit 8 + if (m_rx_fifo.empty()) { + m_rx_fifo_out_b8 = BIT(w , 8); + } + m_rx_fifo.enqueue(w); + } + if (rx_fifo_flag()) { + m_rxfifo_irq = true; + } else { + // Logic of A1U21A 'LS109 JK FF (J=A3 K/=A4) + switch (offset & 0x18) { + case 0x00: + m_rxfifo_irq = false; + break; + + case 0x08: + m_rxfifo_irq = !m_rxfifo_irq; + break; + + case 0x10: + break; + + case 0x18: + m_rxfifo_irq = true; + break; + } + } + update_flg(); + update_sts(); + update_irq(); + break; + + case 1: + // xxxx'x101: write HS + m_hs_out = data; + update_hs_out(); + break; + + case 2: + // xxxx'x110: clear rx FIFO overrun + m_rxfifo_overrun = false; + update_sts(); + break; + + case 3: + // xxxx'x111: write to BRGs + set_brgs(data); + break; + } + } else { + LOG_SIO("%.6f SIO W @%u=%02x\n" , machine().time().as_double() , offset & 3 , data); + m_sio->cd_ba_w(offset & 3 , data); + } +} + +READ8_MEMBER(hp98046_io_card_device::p1_r) +{ + uint8_t res = 0; + // b7: b8 of word @ txFIFO head + if (BIT(m_tx_fifo.peek() , 8)) { + BIT_SET(res , 7); + } + // b6: rxFIFO overrun + if (!m_rxfifo_overrun) { + BIT_SET(res , 6); + } + // b5: rxFIFO not empty + if (!m_rx_fifo.empty()) { + BIT_SET(res , 5); + } + // b4: R6(0)/R7(1) + if (m_r6_r7_select) { + BIT_SET(res , 4); + } + // b3: tx FIFO flag + if (tx_fifo_flag()) { + BIT_SET(res , 3); + } + // b2: tx FIFO not empty + if (!m_tx_fifo.empty()) { + BIT_SET(res , 2); + } + // b1: rx FIFO flag + if (rx_fifo_flag()) { + BIT_SET(res , 1); + } + // b0: rx FIFO not full + if (!m_rx_fifo.full()) { + BIT_SET(res , 0); + } + //LOG("p1=%02x\n" , res); + return res; +} + +WRITE8_MEMBER(hp98046_io_card_device::p2_w) +{ + LOG_MCU("p2=%02x\n" , data); + uint8_t diff = data ^ m_port_2; + m_port_2 = data; + if (BIT(diff , 7)) { + if (BIT(m_port_2 , 7)) { + m_sio->reset(); + set_r6_r7_pending(true); + } + update_hs_out(); + } + if (BIT(diff , 6)) { + update_flg(); + } + if (BIT(diff , 5)) { + update_sts(); + } +} + +WRITE_LINE_MEMBER(hp98046_io_card_device::sio_int_w) +{ + if (m_sio_int != state) { + LOG_SIO("SIO int=%d\n" , state); + } + m_sio_int = state; +} + +WRITE_LINE_MEMBER(hp98046_io_card_device::sio_txd_w) +{ + m_sio->rxb_w(state); + if (m_loopback) { + m_sio->rxa_w(state); + } else { + m_rs232->write_txd(state); + } +} + +WRITE_LINE_MEMBER(hp98046_io_card_device::rs232_rxd_w) +{ + if (!m_loopback) { + m_sio->rxa_w(state); + } +} + +WRITE_LINE_MEMBER(hp98046_io_card_device::rs232_dcd_w) +{ + if (!m_loopback) { + m_sio->dcda_w(state); + } +} + +WRITE_LINE_MEMBER(hp98046_io_card_device::rs232_dsr_w) +{ + if (!m_loopback) { + m_sio->dcdb_w(state); + } +} + +WRITE_LINE_MEMBER(hp98046_io_card_device::rs232_cts_w) +{ + if (!m_loopback) { + m_sio->ctsa_w(state); + } +} + +bool hp98046_io_card_device::rx_fifo_flag() const +{ + return m_rx_fifo.queue_length() >= 16; +} + +bool hp98046_io_card_device::tx_fifo_flag() const +{ + return m_tx_fifo.queue_length() >= 16; +} + +void hp98046_io_card_device::update_flg() +{ + bool flg_e = !m_r6_r7_pending && !m_tx_fifo.full() && BIT(m_port_2 , 6); + bool flg_o = !m_r6_r7_pending && !m_rx_fifo.empty(); + + LOG_IFS("FLG e/o=%d/%d\n" , flg_e , flg_o); + flg_w(flg_e); + flg_nextsc_w(flg_o); +} + +void hp98046_io_card_device::update_sts() +{ + bool sts_e = !BIT(m_port_2 , 5); + bool sts_o = !m_rxfifo_overrun && m_rx_fifo_out_b8; + + LOG_IFS("STS e/o=%d/%d\n" , sts_e , sts_o); + sts_w(sts_e); + sts_nextsc_w(sts_o); +} + +void hp98046_io_card_device::update_irq() +{ + bool irq = m_inten && !m_r6_r7_pending && (m_rxfifo_irq || (m_enoutint && !tx_fifo_flag())); + bool irq_e = irq && !m_rxfifo_irq; + bool irq_o = irq && m_rxfifo_irq; + + LOG_IFS("IRQ e/o=%d/%d\n" , irq_e , irq_o); + irq_w(irq_e); + irq_nextsc_w(irq_o); +} + +void hp98046_io_card_device::update_hs_out() +{ + if (BIT(m_port_2 , 7)) { + m_actual_hs_out = ~0; + } else { + m_actual_hs_out = m_hs_out; + } + if (m_loopback) { + m_sio->ctsa_w(BIT(m_actual_hs_out , 4)); + m_sio->dcda_w(BIT(m_actual_hs_out , 3)); + m_sio->ctsb_w(BIT(m_actual_hs_out , 2)); + m_sio->dcdb_w(BIT(m_actual_hs_out , 1)); + } else { + m_rs232->write_dtr(BIT(m_actual_hs_out , 5)); + m_rs232->write_rts(BIT(m_actual_hs_out , 4)); + // b3 (A2J1-13) not mapped + // b2 (A2J1-15) not mapped (Data Rate Select) + // b1 (A2J1-30) not mapped (Secondary RTS) + // b0 (A2J1-24) not mapped + } +} + +void hp98046_io_card_device::load_tx_fifo() +{ + if (m_tx_fifo_pending && !m_tx_fifo.full()) { + m_tx_fifo.enqueue(m_tx_fifo_in); + m_tx_fifo_pending = false; + update_flg(); + update_irq(); + } +} + +void hp98046_io_card_device::set_r6_r7_pending(bool state) +{ + m_r6_r7_pending = state || BIT(m_port_2 , 7); + m_cpu->set_input_line(MCS48_INPUT_IRQ , m_r6_r7_pending ? ASSERT_LINE : CLEAR_LINE); + update_flg(); + update_irq(); +} + +uint8_t hp98046_io_card_device::get_hs_input() const +{ + uint8_t res = 0xc1; + if (m_loopback) { + // DTR looped back into RI + if (BIT(m_actual_hs_out , 5)) { + BIT_SET(res , 5); + } + // RTS looped back into CTS + if (BIT(m_actual_hs_out , 4)) { + BIT_SET(res , 4); + } + // A2J1-13 looped back into DCD + if (BIT(m_actual_hs_out , 3)) { + BIT_SET(res , 3); + } + // DRS looped back into SCD + if (BIT(m_actual_hs_out , 2)) { + BIT_SET(res , 2); + } + // SRTS looped back into DSR + if (BIT(m_actual_hs_out , 1)) { + BIT_SET(res , 1); + } + } else { + if (m_rs232->ri_r()) { + BIT_SET(res , 5); + } + if (m_rs232->cts_r()) { + BIT_SET(res , 4); + } + if (m_rs232->dcd_r()) { + BIT_SET(res , 3); + } + // SCD always 1 + BIT_SET(res , 2); + if (m_rs232->dsr_r()) { + BIT_SET(res , 1); + } + } + return res; +} + +// Frequencies of HD4702 BRGs +// All frequencies are doubled here because the timers expire twice per RxC/TxC period +static const unsigned brg_freq[] = { + // Sel: frequency Divisor + // ============================ + 0, // 0: external clock - + 0, // 1: external clock - + 1600, // 2: 50 x16 /3072 + 2400, // 3: 75 x16 /2048 + 4267, // 4: ~134.5 x16 /1152 + 6400, // 5: 200 x16 /768 + 19200, // 6: 600 x16 /256 + 76800, // 7: 2400 x16 /64 + 307200, // 8: 9600 x16 /16 + 153600, // 9: 4800 x16 /32 + 57600, // 10: 1800 x16 / 256/3 + 38400, // 11: 1200 x16 /128 + 76800, // 12: 2400 x16 /64 + 9600, // 13: 300 x16 /512 + 4800, // 14: 150 x16 /1024 + 3491 // 15: ~110 x16 /1408 +}; + +void hp98046_io_card_device::set_brgs(uint8_t sel) +{ + LOG_MCU("BRG=%02x\n" , sel); + uint8_t new_rxc_sel = (sel >> 4) & 0xf; + uint8_t new_txc_sel = sel & 0xf; + + if (new_rxc_sel != m_rxc_sel) { + m_rxc_sel = new_rxc_sel; + auto period = attotime::from_hz(brg_freq[ m_rxc_sel ]); + m_rxc_timer->adjust(period , 0 , period); + } + if (new_txc_sel != m_txc_sel) { + m_txc_sel = new_txc_sel; + auto period = attotime::from_hz(brg_freq[ m_txc_sel ]); + m_txc_timer->adjust(period , 0 , period); + } +} diff --git a/src/devices/bus/hp9845_io/98046.h b/src/devices/bus/hp9845_io/98046.h new file mode 100644 index 00000000000..7b6f9f1669d --- /dev/null +++ b/src/devices/bus/hp9845_io/98046.h @@ -0,0 +1,103 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + 98046.h + + 98046 module (data communications interface) + +*********************************************************************/ + +#ifndef MAME_BUS_HP9845_IO_98046_H +#define MAME_BUS_HP9845_IO_98046_H + +#pragma once + +#include "hp9845_io.h" +#include "cpu/mcs48/mcs48.h" +#include "machine/z80sio.h" +#include "bus/rs232/rs232.h" + +class hp98046_io_card_device : public hp9845_io_card_device +{ +public: + // construction/destruction + hp98046_io_card_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + virtual ~hp98046_io_card_device(); + + virtual DECLARE_READ16_MEMBER(reg_r) override; + virtual DECLARE_WRITE16_MEMBER(reg_w) override; + + virtual bool has_dual_sc() const override; + +protected: + // device-level overrides + virtual void device_add_mconfig(machine_config &config) override; + virtual ioport_constructor device_input_ports() const override; + virtual const tiny_rom_entry *device_rom_region() const 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; + +private: + required_device<i8048_device> m_cpu; + required_device<z80sio_device> m_sio; + required_device<rs232_port_device> m_rs232; + required_ioport m_loopback_en; + + std::unique_ptr<uint8_t[]> m_ram; + util::fifo<uint16_t , 32> m_tx_fifo; // A1U7 + util::fifo<uint16_t , 32> m_rx_fifo; // A1U11 + bool m_rx_fifo_out_b8; // Bit 8 of rx FIFO output + + uint16_t m_tx_fifo_in; // A1U1 & A1U9-8 + bool m_tx_fifo_pending; // A1U18-7 + uint8_t m_r6_r7; // A1U2 + bool m_r6_r7_pending; // A1U18-9 + bool m_r6_r7_select; // A1U18-13 + bool m_rxfifo_overrun; // A1U21-9 + bool m_rxfifo_irq; // A1U21-6 + bool m_inten; // A1U15-2 + bool m_enoutint; // A1U15-6 + uint8_t m_hs_out; // A2U4 + uint8_t m_actual_hs_out; // A2U4 output + bool m_sio_int; + uint8_t m_port_2; + bool m_loopback; + + emu_timer *m_rxc_timer; + emu_timer *m_txc_timer; + uint8_t m_rxc_sel; + uint8_t m_txc_sel; + bool m_rxc; + bool m_txc; + + void cpu_program_map(address_map &map); + void cpu_io_map(address_map &map); + DECLARE_READ8_MEMBER(ram_r); + DECLARE_READ8_MEMBER(cpu_r); + DECLARE_WRITE8_MEMBER(cpu_w); + DECLARE_READ8_MEMBER(p1_r); + DECLARE_WRITE8_MEMBER(p2_w); + DECLARE_WRITE_LINE_MEMBER(sio_int_w); + DECLARE_WRITE_LINE_MEMBER(sio_txd_w); + DECLARE_WRITE_LINE_MEMBER(rs232_rxd_w); + DECLARE_WRITE_LINE_MEMBER(rs232_dcd_w); + DECLARE_WRITE_LINE_MEMBER(rs232_dsr_w); + DECLARE_WRITE_LINE_MEMBER(rs232_cts_w); + bool rx_fifo_flag() const; + bool tx_fifo_flag() const; + void update_flg(); + void update_sts(); + void update_irq(); + void update_hs_out(); + void load_tx_fifo(); + void set_r6_r7_pending(bool state); + uint8_t get_hs_input() const; + void set_brgs(uint8_t sel); +}; + +// device type definitions +DECLARE_DEVICE_TYPE(HP98046_IO_CARD, hp98046_io_card_device) + +#endif // MAME_BUS_HP9845_IO_98046_H diff --git a/src/devices/bus/hp9845_io/hp9845_io.cpp b/src/devices/bus/hp9845_io/hp9845_io.cpp index a6a21015d25..663c8fd9977 100644 --- a/src/devices/bus/hp9845_io/hp9845_io.cpp +++ b/src/devices/bus/hp9845_io/hp9845_io.cpp @@ -13,6 +13,7 @@ #include "98032.h" #include "98035.h" #include "98034.h" +#include "98046.h" // device type definition DEFINE_DEVICE_TYPE(HP9845_IO_SLOT, hp9845_io_slot_device, "hp98x5_io_slot", "HP98x5 I/O Slot") @@ -26,12 +27,16 @@ hp9845_io_slot_device::hp9845_io_slot_device(const machine_config &mconfig, cons m_irq_cb_func(*this), m_sts_cb_func(*this), m_flg_cb_func(*this), + m_irq_nextsc_cb_func(*this), + m_sts_nextsc_cb_func(*this), + m_flg_nextsc_cb_func(*this), m_dmar_cb_func(*this) { option_reset(); option_add("98032_gpio" , HP98032_IO_CARD); option_add("98034_hpib" , HP98034_IO_CARD); option_add("98035_rtc" , HP98035_IO_CARD); + option_add("98046" , HP98046_IO_CARD); set_default_option(nullptr); set_fixed(false); } @@ -45,6 +50,9 @@ void hp9845_io_slot_device::device_start() m_irq_cb_func.resolve_safe(); m_sts_cb_func.resolve_safe(); m_flg_cb_func.resolve_safe(); + m_irq_nextsc_cb_func.resolve_safe(); + m_sts_nextsc_cb_func.resolve_safe(); + m_flg_nextsc_cb_func.resolve_safe(); m_dmar_cb_func.resolve_safe(); hp9845_io_card_device *card = dynamic_cast<hp9845_io_card_device*>(get_card_device()); @@ -69,6 +77,21 @@ WRITE_LINE_MEMBER(hp9845_io_slot_device::flg_w) m_flg_cb_func(state); } +WRITE_LINE_MEMBER(hp9845_io_slot_device::irq_nextsc_w) +{ + m_irq_nextsc_cb_func(state); +} + +WRITE_LINE_MEMBER(hp9845_io_slot_device::sts_nextsc_w) +{ + m_sts_nextsc_cb_func(state); +} + +WRITE_LINE_MEMBER(hp9845_io_slot_device::flg_nextsc_w) +{ + m_flg_nextsc_cb_func(state); +} + WRITE_LINE_MEMBER(hp9845_io_slot_device::dmar_w) { m_dmar_cb_func(state); @@ -87,6 +110,17 @@ int hp9845_io_slot_device::get_rw_handlers(read16_delegate& rhandler , write16_d } } +bool hp9845_io_slot_device::has_dual_sc() const +{ + hp9845_io_card_device *card = dynamic_cast<hp9845_io_card_device*>(get_card_device()); + + if (card != nullptr) { + return card->has_dual_sc(); + } else { + return false; + } +} + // +---------------------+ // |hp9845_io_card_device| // +---------------------+ @@ -100,6 +134,11 @@ uint8_t hp9845_io_card_device::get_sc(void) return m_select_code_port->read() + HP9845_IO_FIRST_SC; } +bool hp9845_io_card_device::has_dual_sc() const +{ + return false; +} + hp9845_io_card_device::hp9845_io_card_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock), device_slot_card_interface(mconfig, *this), @@ -133,6 +172,27 @@ WRITE_LINE_MEMBER(hp9845_io_card_device::flg_w) } } +WRITE_LINE_MEMBER(hp9845_io_card_device::irq_nextsc_w) +{ + if (m_slot_dev) { + m_slot_dev->irq_nextsc_w(state); + } +} + +WRITE_LINE_MEMBER(hp9845_io_card_device::sts_nextsc_w) +{ + if (m_slot_dev) { + m_slot_dev->sts_nextsc_w(state); + } +} + +WRITE_LINE_MEMBER(hp9845_io_card_device::flg_nextsc_w) +{ + if (m_slot_dev) { + m_slot_dev->flg_nextsc_w(state); + } +} + WRITE_LINE_MEMBER(hp9845_io_card_device::dmar_w) { if (m_slot_dev) { diff --git a/src/devices/bus/hp9845_io/hp9845_io.h b/src/devices/bus/hp9845_io/hp9845_io.h index 7252ac25520..c85f7632ffe 100644 --- a/src/devices/bus/hp9845_io/hp9845_io.h +++ b/src/devices/bus/hp9845_io/hp9845_io.h @@ -46,22 +46,33 @@ public: auto irq() { return m_irq_cb_func.bind(); } auto sts() { return m_sts_cb_func.bind(); } auto flg() { return m_flg_cb_func.bind(); } + auto irq_nextsc() { return m_irq_nextsc_cb_func.bind(); } + auto sts_nextsc() { return m_sts_nextsc_cb_func.bind(); } + auto flg_nextsc() { return m_flg_nextsc_cb_func.bind(); } auto dmar() { return m_dmar_cb_func.bind(); } // irq/sts/flg/dmar signal handlers for card devices DECLARE_WRITE_LINE_MEMBER(irq_w); DECLARE_WRITE_LINE_MEMBER(sts_w); DECLARE_WRITE_LINE_MEMBER(flg_w); + DECLARE_WRITE_LINE_MEMBER(irq_nextsc_w); + DECLARE_WRITE_LINE_MEMBER(sts_nextsc_w); + DECLARE_WRITE_LINE_MEMBER(flg_nextsc_w); DECLARE_WRITE_LINE_MEMBER(dmar_w); // getter for r/w handlers // return value is SC (negative if no card is attached to slot) int get_rw_handlers(read16_delegate& rhandler , write16_delegate& whandler); + bool has_dual_sc() const; + private: devcb_write_line m_irq_cb_func; devcb_write_line m_sts_cb_func; devcb_write_line m_flg_cb_func; + devcb_write_line m_irq_nextsc_cb_func; + devcb_write_line m_sts_nextsc_cb_func; + devcb_write_line m_flg_nextsc_cb_func; devcb_write_line m_dmar_cb_func; }; @@ -77,6 +88,8 @@ public: // SC getter uint8_t get_sc(void); + virtual bool has_dual_sc() const; + protected: // construction/destruction hp9845_io_card_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); @@ -89,6 +102,9 @@ protected: DECLARE_WRITE_LINE_MEMBER(irq_w); DECLARE_WRITE_LINE_MEMBER(sts_w); DECLARE_WRITE_LINE_MEMBER(flg_w); + DECLARE_WRITE_LINE_MEMBER(irq_nextsc_w); + DECLARE_WRITE_LINE_MEMBER(sts_nextsc_w); + DECLARE_WRITE_LINE_MEMBER(flg_nextsc_w); DECLARE_WRITE_LINE_MEMBER(dmar_w); }; diff --git a/src/devices/bus/hp_optroms/hp_optrom.cpp b/src/devices/bus/hp_optroms/hp_optrom.cpp index 58b4f556cfc..fc54a168c83 100644 --- a/src/devices/bus/hp_optroms/hp_optrom.cpp +++ b/src/devices/bus/hp_optroms/hp_optrom.cpp @@ -41,6 +41,10 @@ hp_optrom_slot_device::hp_optrom_slot_device(const machine_config &mconfig, cons m_base_addr(0), m_end_addr(0) { + option_reset(); + option_add_internal("rom", HP_OPTROM_CART); + set_default_option(nullptr); + set_fixed(false); } hp_optrom_slot_device::~hp_optrom_slot_device() @@ -125,8 +129,3 @@ std::string hp_optrom_slot_device::get_default_card_software(get_default_card_so { return software_get_default_slot("rom"); } - -void hp_optrom_slot_devices(device_slot_interface &device) -{ - device.option_add_internal("rom", HP_OPTROM_CART); -} diff --git a/src/devices/bus/hp_optroms/hp_optrom.h b/src/devices/bus/hp_optroms/hp_optrom.h index f2677884849..9b507ba64eb 100644 --- a/src/devices/bus/hp_optroms/hp_optrom.h +++ b/src/devices/bus/hp_optroms/hp_optrom.h @@ -70,6 +70,4 @@ protected: DECLARE_DEVICE_TYPE(HP_OPTROM_SLOT, hp_optrom_slot_device) DECLARE_DEVICE_TYPE(HP_OPTROM_CART, hp_optrom_cart_device) -void hp_optrom_slot_devices(device_slot_interface &device); - #endif // MAME_BUS_HP_OPTROMS_HP_OPTROM_H diff --git a/src/devices/machine/z80sio.cpp b/src/devices/machine/z80sio.cpp index b985340e576..dfa5a13ad5e 100644 --- a/src/devices/machine/z80sio.cpp +++ b/src/devices/machine/z80sio.cpp @@ -179,9 +179,9 @@ enum : uint8_t enum : uint8_t { WR3_RX_ENABLE = 0x01, - WR3_SYNC_CHAR_LOAD_INHIBIT= 0x02, // not supported - WR3_ADDRESS_SEARCH_MODE = 0x04, // not supported - WR3_RX_CRC_ENABLE = 0x08, // not supported + WR3_SYNC_CHAR_LOAD_INHIBIT= 0x02, + WR3_ADDRESS_SEARCH_MODE = 0x04, + WR3_RX_CRC_ENABLE = 0x08, WR3_ENTER_HUNT_PHASE = 0x10, WR3_AUTO_ENABLES = 0x20, WR3_RX_WORD_LENGTH_MASK = 0xc0, @@ -196,14 +196,14 @@ enum : uint8_t WR4_PARITY_ENABLE = 0x01, WR4_PARITY_EVEN = 0x02, WR4_STOP_BITS_MASK = 0x0c, - WR4_STOP_BITS_SYNC = 0x00, // partially supported + WR4_STOP_BITS_SYNC = 0x00, WR4_STOP_BITS_1 = 0x04, WR4_STOP_BITS_1_5 = 0x08, WR4_STOP_BITS_2 = 0x0c, - WR4_SYNC_MODE_MASK = 0x30, // partially supported - WR4_SYNC_MODE_8_BIT = 0x00, // partially supported - WR4_SYNC_MODE_16_BIT = 0x10, // partially supported - WR4_SYNC_MODE_SDLC = 0x20, // partially supported + WR4_SYNC_MODE_MASK = 0x30, + WR4_SYNC_MODE_8_BIT = 0x00, + WR4_SYNC_MODE_16_BIT = 0x10, + WR4_SYNC_MODE_SDLC = 0x20, WR4_SYNC_MODE_EXT = 0x30, // partially supported WR4_CLOCK_RATE_MASK = 0xc0, WR4_CLOCK_RATE_X1 = 0x00, @@ -227,6 +227,8 @@ enum : uint8_t WR5_DTR = 0x80 }; +constexpr uint32_t TX_SR_MASK = 0xfffffU; +constexpr uint16_t SDLC_RESIDUAL = 0x1d0f; //************************************************************************** // DEVICE DEFINITIONS @@ -309,21 +311,20 @@ inline void z80sio_channel::set_dtr(int state) } } -inline void z80sio_channel::tx_setup(uint16_t data, int bits, int parity, bool framing, bool special) +inline void z80sio_channel::tx_setup(uint16_t data, int bits, bool framing, bool crc_tx, bool abort_tx) { - m_tx_bits = bits; - m_tx_parity = parity; - m_tx_sr = data | (~uint16_t(0) << bits); - if (parity) - { - if (m_wr4 & WR4_PARITY_EVEN) - m_tx_sr &= ~(uint16_t(1) << m_tx_bits); - ++m_tx_bits; - } + m_rr1 |= RR1_ALL_SENT; + m_tx_parity = false; + m_tx_sr = data; + m_tx_sr &= ~(~uint32_t(0) << bits); + m_tx_sr |= ~uint32_t(0) << (bits + 3); m_tx_flags = ((!framing && (m_wr5 & WR5_TX_CRC_ENABLE)) ? TX_FLAG_CRC : 0U) | (framing ? TX_FLAG_FRAMING : 0U) | - (special ? TX_FLAG_SPECIAL : 0U); + (abort_tx ? TX_FLAG_ABORT_TX : 0U) | + (crc_tx ? TX_FLAG_CRC_TX : 0U) | + (!framing && !crc_tx && !abort_tx ? TX_FLAG_DATA_TX : 0U); + LOGBIT("%.6f TX_SR %05x data %04x flags %x\n" , machine().time().as_double() , m_tx_sr & TX_SR_MASK , data , m_tx_flags); } inline void z80sio_channel::tx_setup_idle() @@ -331,24 +332,21 @@ inline void z80sio_channel::tx_setup_idle() switch (m_wr4 & WR4_SYNC_MODE_MASK) { case WR4_SYNC_MODE_8_BIT: - tx_setup(m_wr6, 8, 0, true, false); + case WR4_SYNC_MODE_EXT: + // External sync mode sends a single sync byte + tx_setup(m_wr6, 8, true, false, false); break; case WR4_SYNC_MODE_16_BIT: - tx_setup(uint16_t(m_wr6) | (uint16_t(m_wr7) << 8), 16, 0, true, false); + tx_setup(uint16_t(m_wr6) | (uint16_t(m_wr7) << 8), 16, true, false, false); break; case WR4_SYNC_MODE_SDLC: // SDLC transmit examples don't show flag being loaded, implying it's hard-coded on the transmit side - tx_setup(0x7e, 8, 0, true, false); - break; - case WR4_SYNC_MODE_EXT: - // TODO: what does a real chip do for sync idle in external sync mode? - // This is based on the assumption that bit 4 controls 8-/16-bit idle pattern (fits for monosync/bisync/SDLC). - tx_setup(uint16_t(m_wr6) | (uint16_t(m_wr7) << 8), 16, 0, true, false); + tx_setup(0x7e, 8, true, false, false); break; } + m_tx_in_pkt = false; } - //------------------------------------------------- // z80sio_device - constructor //------------------------------------------------- @@ -697,7 +695,6 @@ uint8_t z80sio_device::read_vector() { if (m_int_state[prio[i]] & Z80_DAISY_INT) { - constexpr uint8_t RR1_SPECIAL(RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR | RR1_END_OF_FRAME); switch (prio[i]) { case 0 + z80sio_channel::INT_TRANSMIT: @@ -705,9 +702,9 @@ uint8_t z80sio_device::read_vector() case 0 + z80sio_channel::INT_EXTERNAL: return vec | 0x0aU; case 0 + z80sio_channel::INT_RECEIVE: - if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanA->m_rr1 & (RR1_SPECIAL | RR1_PARITY_ERROR))) + if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanA->m_rr1 & (m_chanA->get_special_rx_mask() | RR1_PARITY_ERROR))) return vec | 0x0eU; - else if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanA->m_rr1 & RR1_SPECIAL)) + else if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanA->m_rr1 & m_chanA->get_special_rx_mask())) return vec | 0x0eU; else return vec | 0x0cU; @@ -716,9 +713,9 @@ uint8_t z80sio_device::read_vector() case 3 + z80sio_channel::INT_EXTERNAL: return vec | 0x02U; case 3 + z80sio_channel::INT_RECEIVE: - if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanB->m_rr1 & (RR1_SPECIAL | RR1_PARITY_ERROR))) + if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanB->m_rr1 & (m_chanB->get_special_rx_mask() | RR1_PARITY_ERROR))) return vec | 0x06U; - else if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanB->m_rr1 & RR1_SPECIAL)) + else if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanB->m_rr1 & m_chanB->get_special_rx_mask())) return vec | 0x06U; else return vec | 0x04U; @@ -922,10 +919,9 @@ z80sio_channel::z80sio_channel( , m_rx_bit(0) , m_rx_sr(0) , m_rx_first(0) - , m_rx_break(0) , m_rxd(1) , m_tx_data(0) - , m_tx_clock(0), m_tx_count(0), m_tx_bits(0), m_tx_parity(0), m_tx_sr(0), m_tx_crc(0), m_tx_hist(0), m_tx_flags(0) + , m_tx_clock(0), m_tx_count(0), m_tx_parity(0), m_tx_sr(0), m_tx_crc(0), m_tx_hist(0), m_tx_flags(0) , m_txd(1), m_dtr(0), m_rts(0) , m_ext_latched(0), m_brk_latched(0), m_cts(0), m_dcd(0), m_sync(0) , m_rr1_auto_reset(rr1_auto_reset) @@ -938,7 +934,7 @@ z80sio_channel::z80sio_channel( } z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : z80sio_channel(mconfig, Z80SIO_CHANNEL, tag, owner, clock, RR1_CRC_FRAMING_ERROR) + : z80sio_channel(mconfig, Z80SIO_CHANNEL, tag, owner, clock, RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RESIDUE_CODE_MASK) { } @@ -981,19 +977,31 @@ void z80sio_channel::device_start() save_item(NAME(m_rx_error_fifo)); save_item(NAME(m_rx_clock)); save_item(NAME(m_rx_count)); + save_item(NAME(m_dlyd_rxd)); save_item(NAME(m_rx_bit)); + save_item(NAME(m_rx_bit_limit)); + save_item(NAME(m_rx_sync_fsm)); + save_item(NAME(m_rx_one_cnt)); save_item(NAME(m_rx_sr)); + save_item(NAME(m_rx_sync_sr)); + save_item(NAME(m_rx_crc_delay)); + save_item(NAME(m_rx_crc)); + save_item(NAME(m_rx_crc_en)); + save_item(NAME(m_rx_parity)); save_item(NAME(m_rx_first)); - save_item(NAME(m_rx_break)); save_item(NAME(m_tx_data)); save_item(NAME(m_tx_clock)); save_item(NAME(m_tx_count)); - save_item(NAME(m_tx_bits)); + save_item(NAME(m_tx_phase)); save_item(NAME(m_tx_parity)); + save_item(NAME(m_tx_in_pkt)); + save_item(NAME(m_tx_forced_sync)); save_item(NAME(m_tx_sr)); save_item(NAME(m_tx_crc)); save_item(NAME(m_tx_hist)); save_item(NAME(m_tx_flags)); + save_item(NAME(m_tx_delay)); + save_item(NAME(m_all_sent_delay)); save_item(NAME(m_txd)); save_item(NAME(m_dtr)); save_item(NAME(m_rts)); @@ -1016,9 +1024,11 @@ void z80sio_channel::device_reset() m_rx_fifo_depth = 0; m_rx_data_fifo = m_rx_error_fifo = 0U; m_rx_bit = 0; + m_rx_one_cnt = 0; + m_rx_sync_fsm = SYNC_FSM_HUNT; m_tx_count = 0; - m_tx_bits = 0; m_rr0 &= ~RR0_RX_CHAR_AVAILABLE; + m_rr0 |= RR0_SYNC_HUNT; m_rr1 &= ~(RR1_PARITY_ERROR | RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR); // disable receiver @@ -1029,6 +1039,10 @@ void z80sio_channel::device_reset() m_rr0 |= RR0_TX_BUFFER_EMPTY | RR0_TX_UNDERRUN; m_rr1 |= RR1_ALL_SENT; m_tx_flags = 0U; + m_tx_delay = ~0; + m_all_sent_delay = 0; + m_tx_in_pkt = false; + m_tx_forced_sync = true; // TODO: what happens to WAIT/READY? @@ -1045,6 +1059,10 @@ void z80sio_channel::device_reset() m_uart->reset_interrupts(); } +bool z80sio_channel::is_tx_idle() const +{ + return (m_tx_sr & TX_SR_MASK) == TX_SR_MASK; +} //------------------------------------------------- // transmit_enable - start transmission if @@ -1052,19 +1070,37 @@ void z80sio_channel::device_reset() //------------------------------------------------- void z80sio_channel::transmit_enable() { - if (!m_tx_bits && transmit_allowed()) + if (transmit_allowed()) { - if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) + if (is_tx_idle()) { - LOGTX("Channel %c synchronous transmit enabled - load sync pattern\n", 'A' + m_index); - tx_setup_idle(); - if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(true); + if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) + { + LOGTX("Channel %c synchronous transmit enabled - load sync pattern\n", 'A' + m_index); + tx_setup_idle(); + m_tx_forced_sync = false; + if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) + set_ready(true); + } + else if (!(m_rr0 & RR0_TX_BUFFER_EMPTY)) + async_tx_setup(); } - else if (!(m_rr0 & RR0_TX_BUFFER_EMPTY)) + } + else + { + // Send at least 1 sync once tx is re-enabled + m_tx_forced_sync = true; + LOGBIT("tx forced set 1\n"); + + // If tx is disabled during CRC transmission, flag/sync is sent for the remaining bits + if (m_tx_flags & TX_FLAG_CRC_TX) { - async_tx_setup(); + m_tx_flags = TX_FLAG_FRAMING; + set_tx_empty(false , (m_rr0 & RR0_TX_BUFFER_EMPTY) != 0); } + m_tx_in_pkt = false; + // Not sure if RR0_TX_UNDERRUN is set when tx is disabled. It certainly makes sense to be that way. + m_rr0 |= RR0_TX_UNDERRUN; } } @@ -1093,52 +1129,65 @@ void z80sio_channel::sync_tx_sr_empty() if (!transmit_allowed()) { LOGTX("%s() Channel %c Transmitter Disabled m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_wr5); + m_tx_flags = 0; + } + else if (m_tx_forced_sync || + ((m_rr0 & RR0_TX_BUFFER_EMPTY) && ((m_rr0 & RR0_TX_UNDERRUN) || !(m_wr5 & WR5_TX_CRC_ENABLE)))) + { + LOGBIT("tx forced = %d\n" , m_tx_forced_sync); + m_tx_forced_sync = false; - // transmit disabled, set flag if nothing pending - m_tx_flags &= ~TX_FLAG_SPECIAL; - if (m_rr0 & RR0_TX_BUFFER_EMPTY) - m_rr1 |= RR1_ALL_SENT; + if (!(m_rr0 & RR0_TX_UNDERRUN)) + { + m_rr0 |= RR0_TX_UNDERRUN; + trigger_ext_int(); + } + // TODO: Check + // if ((m_tx_flags & (TX_FLAG_CRC_TX | TX_FLAG_DATA_TX)) && (m_wr1 & WR1_TX_INT_ENABLE)) + // // At the beginning of the sync/flag sequence that closes a frame, send tx interrupt + // m_uart->trigger_interrupt(m_index, INT_TRANSMIT); + if (m_tx_flags & TX_FLAG_CRC_TX) + { + // At the end of CRC transmission, set tx empty + m_tx_flags = 0; + set_tx_empty (false , (m_rr0 & RR0_TX_BUFFER_EMPTY) != 0); + } + tx_setup_idle(); } else if (!(m_rr0 & RR0_TX_BUFFER_EMPTY)) { LOGTX("%s() Channel %c Transmit Data Byte '%02x' m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_tx_data, m_wr5); - tx_setup(m_tx_data, get_tx_word_length(m_tx_data), (m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0, false, false); - + tx_setup(m_tx_data, get_tx_word_length(), false, false, false); // empty transmit buffer - m_rr0 |= RR0_TX_BUFFER_EMPTY; - if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(true); - if (m_wr1 & WR1_TX_INT_ENABLE) - m_uart->trigger_interrupt(m_index, INT_TRANSMIT); - } - else if ((m_rr0 & RR0_TX_UNDERRUN) || ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_8_BIT)) - { - // uts20 always resets the underrun/end-of-message flag if it sees it set, but wants to see sync (not CRC) on the loopback. - // It seems odd that automatic CRC transmission would be disabled by certain modes, but this at least allows the test to pass. - - LOGTX("%s() Channel %c Underrun - load sync pattern m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_wr5); - bool const first_idle((m_tx_flags & TX_FLAG_SPECIAL) || !(m_tx_flags & TX_FLAG_FRAMING)); - tx_setup_idle(); - - if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(true); - m_rr1 |= RR1_ALL_SENT; - - // if this is the first sync pattern, generate an interrupt indicating that the next frame can be sent - // FIXME: uts20 definitely doesn't want a Tx interrupt here, but what does SDLC mode want? - // In that case, it would seem that the underrun flag would already be set when the CRC was loaded. - if (!(m_rr0 & RR0_TX_UNDERRUN)) - trigger_ext_int(); - else if (first_idle && (m_wr1 & WR1_TX_INT_ENABLE)) - m_uart->trigger_interrupt(m_index, INT_TRANSMIT); + set_tx_empty(false , true); } else { LOGTX("%s() Channel %c Transmit FCS '%04x' m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_tx_crc, m_wr5); - // just for fun, SDLC sends the FCS inverted in reverse bit order - uint16_t const fcs(bitswap<16>(m_tx_crc, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)); - tx_setup(((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC) ? ~fcs : fcs, 16, 0, false, true); + // Send CRC. 16 bits are counted by loading 2 flag/sync bytes into tx SR (these bits + // are actually sent out when tx is disabled during CRC transmission) + uint16_t flags = 0; + switch (m_wr4 & WR4_SYNC_MODE_MASK) + { + case WR4_SYNC_MODE_8_BIT: + case WR4_SYNC_MODE_EXT: + flags = (uint16_t(m_wr6) << 8) | m_wr6; + break; + case WR4_SYNC_MODE_16_BIT: + flags = uint16_t(m_wr6) | (uint16_t(m_wr7) << 8); + break; + case WR4_SYNC_MODE_SDLC: + flags = 0x7e7e; + // In SDLC mode, invert CRC before sending it out + m_tx_crc = ~m_tx_crc; + // In addition, ensure at least 1 flag is sent out before next frame + m_tx_forced_sync = true; + break; + } + tx_setup(flags, 16, false, true, false); + set_tx_empty(true , true); + LOGBIT("Send CRC=%04x\n" , m_tx_crc); // set the underrun flag so it will send sync next time m_rr0 |= RR0_TX_UNDERRUN; @@ -1146,6 +1195,43 @@ void z80sio_channel::sync_tx_sr_empty() } } +bool z80sio_channel::get_tx_empty() const +{ + // During CRC transmission, tx buffer is shown as full + return (m_rr0 & RR0_TX_BUFFER_EMPTY) && + (m_tx_flags & TX_FLAG_CRC_TX) == 0; +} + +void z80sio_channel::set_tx_empty(bool prev_state, bool new_state) +{ + if (new_state) + m_rr0 |= RR0_TX_BUFFER_EMPTY; + else + m_rr0 &= ~RR0_TX_BUFFER_EMPTY; + + bool curr_tx_empty = get_tx_empty(); + + if (!prev_state && curr_tx_empty) + { + if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) + set_ready(true); + if (m_wr1 & WR1_TX_INT_ENABLE) + m_uart->trigger_interrupt(m_index, INT_TRANSMIT); + } + else if (prev_state && !curr_tx_empty) + { + if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) + set_ready(false); + } +} + +void z80sio_channel::update_crc(uint16_t& crc , bool bit) +{ + if (BIT(crc , 15) ^ bit) + crc = (crc << 1) ^ ((m_wr5 & WR5_CRC16) ? 0x8005U : 0x1021U); + else + crc <<= 1; +} //------------------------------------------------- // async_tx_setup - set up for asynchronous @@ -1155,15 +1241,27 @@ void z80sio_channel::async_tx_setup() { LOGTX("%s() Channel %c Transmit Data Byte '%02x' m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_tx_data, m_wr5); - tx_setup(uint16_t(m_tx_data) << 1, get_tx_word_length(m_tx_data) + 1, (m_wr4 & WR4_PARITY_ENABLE) ? 2 : 0, false, false); - ++m_tx_bits; // stop bit + // 5 bits: | 11x 1 | tx_data (8 bits) | 0 | + // 6 bits: | 10x 1 | 000 | tx_data (6 bits) | 0 | + // 7 bits: | 9x 1 | 000 | tx_data (7 bits) | 0 | + // 8 bits: | 8x 1 | 000 | tx_data (8 bits) | 0 | + // Add start bit on the right + m_tx_sr = uint32_t(m_tx_data) << 1; + auto wl = get_tx_word_length(); + if (wl != 5) + // Filter out bits to be ignored in m_tx_data + m_tx_sr &= ~(~uint32_t(0) << (wl + 1)); + // Add 1s on the left + m_tx_sr |= ~uint32_t(0) << (wl + 4); + LOGBIT("TX_SR %05x TX_D %02x\n" , m_tx_sr & TX_SR_MASK , m_tx_data); + m_tx_parity = false; + + m_tx_flags = TX_FLAG_DATA_TX; // empty transmit buffer - m_rr0 |= RR0_TX_BUFFER_EMPTY; - if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(true); - if (m_wr1 & WR1_TX_INT_ENABLE) - m_uart->trigger_interrupt(m_index, INT_TRANSMIT); + set_tx_empty(false , true); + m_rr1 &= ~RR1_ALL_SENT; + m_all_sent_delay = 0; } @@ -1264,10 +1362,8 @@ void z80sio_channel::update_dtr_rts_break() // RTS is affected by transmit queue state in asynchronous mode if (m_wr5 & WR5_RTS) set_rts(0); // when the RTS bit is set, the _RTS output goes low - else if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) + else if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC || (m_rr1 & RR1_ALL_SENT)) set_rts(1); // in synchronous mode, there's no automatic RTS - else - set_rts(((m_rr0 & RR0_TX_BUFFER_EMPTY) && !m_tx_count) ? 1 : 0); // TODO: is this affected by transmit enable? // break immediately forces spacing condition on TxD output out_txd_cb((m_wr5 & WR5_SEND_BREAK) ? 0 : m_txd); @@ -1315,20 +1411,6 @@ int z80sio_channel::get_tx_word_length() const return 5; } -int z80sio_channel::get_tx_word_length(uint8_t data) const -{ - LOG("%s(%02x)\n", FUNCNAME, data); - - // deal with "five bits or less" mode (the actual chips probably detect a sentinel pattern in the transmit shift register) - int bits = get_tx_word_length(); - if (5 == bits) - { - for (int b = 7; (b >= 4) && BIT(data, b); --b) - --bits; - } - return bits; -} - /* * This register contains the status of the receive and transmit buffers; the * DCD, CTS, and SYNC inputs; the Transmit Underrun/EOM latch; and the @@ -1336,10 +1418,10 @@ int z80sio_channel::get_tx_word_length(uint8_t data) const uint8_t z80sio_channel::do_sioreg_rr0() { LOGR("%s\n", FUNCNAME); - if (m_tx_flags & TX_FLAG_SPECIAL) - return m_rr0 & ~RR0_TX_BUFFER_EMPTY; - else - return m_rr0; + uint8_t tmp = m_rr0 & ~RR0_TX_BUFFER_EMPTY; + if (get_tx_empty()) + tmp |= RR0_TX_BUFFER_EMPTY; + return tmp; } /* @@ -1423,6 +1505,8 @@ void z80sio_channel::do_sioreg_wr0_resets(uint8_t data) break; case WR0_CRC_RESET_RX: /* In Synchronous mode: all Os (zeros) (CCITT-O CRC-16) */ LOGCMD("Z80SIO Channel %c : CRC_RESET_RX - not implemented\n", 'A' + m_index); + m_rx_crc = ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC) ? ~uint16_t(0U) : uint16_t(0U); + m_rx_crc_en = false; break; case WR0_CRC_RESET_TX: /* In HDLC mode: all 1s (ones) (CCITT-1) */ LOGCMD("Z80SIO Channel %c : CRC_RESET_TX\n", 'A' + m_index); @@ -1430,7 +1514,9 @@ void z80sio_channel::do_sioreg_wr0_resets(uint8_t data) break; case WR0_CRC_RESET_TX_UNDERRUN: /* Resets Tx underrun/EOM bit (D6 of the SRO register) */ LOGCMD("Z80SIO Channel %c : CRC_RESET_TX_UNDERRUN\n", 'A' + m_index); - m_rr0 &= ~RR0_TX_UNDERRUN; + // Command is accepted in active part of packet only + if (m_tx_in_pkt) + m_rr0 &= ~RR0_TX_UNDERRUN; break; default: /* Will not happen unless someone messes with the mask */ logerror("Z80SIO Channel %c : %s Wrong CRC reset/init command:%02x\n", 'A' + m_index, FUNCNAME, data & WR0_CRC_RESET_CODE_MASK); @@ -1457,13 +1543,9 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data) else { LOGCMD("%s Ch:%c : Send abort command\n", FUNCNAME, 'A' + m_index); - // FIXME: how does this interact with interrupts? - // For now assume it behaves like automatically sending CRC and generates a transmit interrupt when a new frame can be sent. - tx_setup(0xff, 8, 0, true, true); - m_rr0 |= RR0_TX_BUFFER_EMPTY; - m_rr1 &= ~RR1_ALL_SENT; - if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(false); + bool prev_tx_empty = get_tx_empty(); + tx_setup(0xff, 8, true, false, true); + set_tx_empty(prev_tx_empty , true); } break; case WR0_RESET_EXT_STATUS: @@ -1492,12 +1574,12 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data) { // clearing framing and overrun errors advances the FIFO // TODO: Intel 8274 manual doesn't mention this behaviour - is it specific to Z80 SIO? - m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR); + m_rr1 &= ~(RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR); advance_rx_fifo(); } else { - m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR); + m_rr1 &= ~(RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR); } break; case WR0_RETURN_FROM_INT: @@ -1547,7 +1629,7 @@ void z80sio_channel::do_sioreg_wr1(uint8_t data) else if (data & WR1_WRDY_ON_RX_TX) set_ready(bool(m_rr0 & RR0_RX_CHAR_AVAILABLE)); else - set_ready((m_rr0 & RR0_TX_BUFFER_EMPTY) && !(m_tx_flags & TX_FLAG_SPECIAL)); + set_ready(m_rr0 & RR0_TX_BUFFER_EMPTY); } void z80sio_channel::do_sioreg_wr2(uint8_t data) @@ -1576,16 +1658,7 @@ void z80sio_channel::do_sioreg_wr3(uint8_t data) else if ((data & WR3_ENTER_HUNT_PHASE) && ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC)) { // TODO: should this re-initialise hunt logic if already in hunt phase for 8-bit/16-bit/SDLC sync? - if ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_EXT) - { - m_rx_bit = 0; - } - else if (!(m_rr0 & RR0_SYNC_HUNT)) - { - m_rx_bit = 0; - m_rr0 |= RR0_SYNC_HUNT; - trigger_ext_int(); - } + enter_hunt_mode(); } } @@ -1693,21 +1766,23 @@ void z80sio_channel::data_write(uint8_t data) // fill transmit buffer m_tx_data = data; - m_rr0 &= ~RR0_TX_BUFFER_EMPTY; - m_rr1 &= ~RR1_ALL_SENT; - if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX)) - set_ready(false); + set_tx_empty(get_tx_empty() , false); + if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) + m_tx_in_pkt = true; + else + { + // ALL_SENT is only meaningful in async mode, in sync mode it's always 1 + m_rr1 &= ~RR1_ALL_SENT; + m_all_sent_delay = 0; + } - // handle automatic RTS bool const async((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC); - if (async && !(m_wr5 & WR5_RTS)) - set_rts(0); // TODO: if transmission is disabled when the data buffer is full, is this still asserted? // clear transmit interrupt m_uart->clear_interrupt(m_index, INT_TRANSMIT); // may be possible to transmit immediately (synchronous mode will load when sync pattern completes) - if (async && !m_tx_bits && transmit_allowed()) + if (async && is_tx_idle() && transmit_allowed()) async_tx_setup(); } @@ -1750,6 +1825,13 @@ void z80sio_channel::advance_rx_fifo() } } +uint8_t z80sio_channel::get_special_rx_mask() const +{ + return ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) ? + (RR1_RX_OVERRUN_ERROR | RR1_END_OF_FRAME) : + (RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR); +} + //------------------------------------------------- // receive_enabled - conditions have changed @@ -1761,73 +1843,282 @@ void z80sio_channel::receive_enabled() bool const sync_mode((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC); m_rx_count = sync_mode ? 0 : ((get_clock_mode() - 1) / 2); m_rx_bit = 0; - if (sync_mode && ((m_wr4 & WR4_SYNC_MODE_MASK) != WR4_SYNC_MODE_EXT)) - m_rr0 |= RR0_SYNC_HUNT; + if (sync_mode) + enter_hunt_mode(); } +void z80sio_channel::enter_hunt_mode() +{ + if (!(m_rr0 & RR0_SYNC_HUNT)) + { + m_rx_sync_fsm = SYNC_FSM_HUNT; + m_rr0 |= RR0_SYNC_HUNT; + trigger_ext_int(); + } +} //------------------------------------------------- // sync_receive - synchronous reception handler //------------------------------------------------- - void z80sio_channel::sync_receive() { - // TODO: this is a fundamentally flawed approach - it's just the quickest way to get uts20 to pass some tests - // Sync acquisition works, but sync load suppression doesn't work right. - // Assembled data needs to be separated from the receive shift register for SDLC. - // Supporting receive checksum for modes other than SDLC is going to be very complicated due to all the bit delays involved. + LOGBIT("%.6f Channel %c Sync Received Bit %d, sync=%02x, sr=%03x, crc_dly=%02x, crc=%04x, FSM=%d, bit=%d, limit=%d\n" , machine().time().as_double(), 'A' + m_index, m_dlyd_rxd, m_rx_sync_sr, m_rx_sr, m_rx_crc_delay, m_rx_crc, m_rx_sync_fsm, m_rx_bit, m_rx_bit_limit); - bool const ext_sync((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_EXT); - bool const hunt_phase(ext_sync ? m_sync : (m_rr0 & RR0_SYNC_HUNT)); - if (hunt_phase) + bool sync_sr_out = BIT(m_rx_sync_sr , 0); + m_rx_sync_sr = (m_rx_sync_sr >> 1) & 0x7f; + if (m_dlyd_rxd) + m_rx_sync_sr |= 0x80; + + bool wr7_matched = m_rx_sync_sr == m_wr7; + + switch (m_rx_sync_fsm) { - // check for sync detection - bool acquired(false); - int limit(16); + case SYNC_FSM_HUNT: + { + bool got_sync = false; switch (m_wr4 & WR4_SYNC_MODE_MASK) { case WR4_SYNC_MODE_8_BIT: - case WR4_SYNC_MODE_SDLC: - acquired = (m_rx_bit >= 8) && ((m_rx_sr & 0xff00U) == (uint16_t(m_wr7) << 8)); - limit = 8; + if (wr7_matched) + { + LOGRCV("Channel %c 8-bit Sync Acquired\n", 'A' + m_index); + got_sync = true; + } break; + case WR4_SYNC_MODE_16_BIT: - acquired = (m_rx_bit >= 16) && (m_rx_sr == ((uint16_t(m_wr7) << 8) | uint16_t(m_wr6))); + { + m_rx_sr = (m_rx_sr >> 1) & 0x7f; + if (sync_sr_out) + m_rx_sr |= 0x80; + if ((m_rx_sr & 0xff) == m_wr6 && wr7_matched) + { + LOGRCV("Channel %c 16-bit Sync Acquired\n", 'A' + m_index); + got_sync = true; + } + break; + } + + case WR4_SYNC_MODE_EXT: + // Not entirely correct: sync input is synchronized 2 bits off in the real hw + got_sync = m_sync; + break; + + default: break; } - if (acquired) + if (got_sync) { - // TODO: make this do something sensible in SDLC mode - // FIXME: set sync output for one receive bit cycle - // FIXME: what if sync load isn't suppressed? - LOGRCV("%s() Channel %c Character Sync Acquired\n", FUNCNAME, 'A' + m_index); - m_rr0 &= ~RR0_SYNC_HUNT; + if (m_rr0 & RR0_SYNC_HUNT) + { + m_rr0 &= ~RR0_SYNC_HUNT; + trigger_ext_int(); + } + m_rx_sync_fsm = SYNC_FSM_1ST_CHAR; + m_rx_crc_en = false; m_rx_bit = 0; - trigger_ext_int(); + m_rx_bit_limit = get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0); + m_rx_parity = false; } - else + } + break; + + case SYNC_FSM_1ST_CHAR: + case SYNC_FSM_IN_FRAME: + { + bool rx_sr_out = BIT(m_rx_sr , 0); + bool rx_crc_delay_out = BIT(m_rx_crc_delay , 0); + m_rx_crc_delay = (m_rx_crc_delay >> 1); + if (rx_sr_out) + m_rx_crc_delay |= 0x80; + if (m_rx_crc_en) + update_crc(m_rx_crc , rx_crc_delay_out); + m_rx_sr = (m_rx_sr >> 1) & ((1U << (m_rx_bit_limit - 1)) - 1); + if (m_dlyd_rxd) { - // track number of bits we have - m_rx_bit = (std::min)(m_rx_bit + 1, limit); + m_rx_sr |= (1U << (m_rx_bit_limit - 1)); + m_rx_parity = !m_rx_parity; } + if (++m_rx_bit == m_rx_bit_limit) + { + if (!(m_wr3 & WR3_SYNC_CHAR_LOAD_INHIBIT) || + ((m_rx_sr & 0xff) != m_wr6 && !wr7_matched)) + { + uint8_t status_byte = 0; + if (m_rx_crc != 0) + status_byte |= RR1_CRC_FRAMING_ERROR; + if (m_wr4 & WR4_PARITY_EVEN) + m_rx_parity = !m_rx_parity; + if (!m_rx_parity && (m_wr4 & WR4_PARITY_ENABLE)) + status_byte |= RR1_PARITY_ERROR; + uint8_t data = m_rx_sr & 0xff; + if (m_rx_bit_limit < 8) + // Fill the unused part of character with ones + data |= ~((1U << m_rx_bit_limit) - 1); + queue_received(data , status_byte); + } + m_rx_bit = 0; + m_rx_bit_limit = get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0); + m_rx_parity = false; + m_rx_crc_en = (m_rx_sync_fsm == SYNC_FSM_IN_FRAME) && (m_wr3 & WR3_RX_CRC_ENABLE); + m_rx_sync_fsm = SYNC_FSM_IN_FRAME; + } + break; + } + + default: + LOG("Invalid Sync FSM state (%d)\n" , m_rx_sync_fsm); + m_rx_sync_fsm = SYNC_FSM_HUNT; + } + + m_dlyd_rxd = m_rxd; +} + +//------------------------------------------------- +// sdlc_receive - SDLC reception handler +//------------------------------------------------- +void z80sio_channel::sdlc_receive() +{ + LOGBIT("Channel %c SDLC Received Bit %d, sync=%02x, sr=%03x, FSM=%d, bit=%d, limit=%d\n", 'A' + m_index, m_rxd, m_rx_sync_sr, m_rx_sr, m_rx_sync_fsm, m_rx_bit, m_rx_bit_limit); + + // Check for flag + bool flag_matched = m_rx_sync_sr == m_wr7; + + // Shift RxD into sync SR + bool sync_sr_out = BIT(m_rx_sync_sr , 0); + m_rx_sync_sr >>= 1; + if (m_rxd) + m_rx_sync_sr |= 0x80; + + // Zero deletion & abort detection + bool zero_deleted = false; + if (sync_sr_out) + { + m_rx_sr = (m_rx_sr >> 1) | (1U << 10); + if (m_rx_one_cnt < 7 && ++m_rx_one_cnt == 7) + { + LOGRCV("SDLC Abort detected\n"); + m_rr0 |= RR0_BREAK_ABORT; + if (!m_brk_latched) { + m_brk_latched = 1; + trigger_ext_int(); + } + enter_hunt_mode(); + } + } + else if (m_rx_one_cnt == 5) + { + m_rx_one_cnt = 0; + // Ignore the zero + zero_deleted = true; } else { - // FIXME: SDLC needs to monitor for flag/abort - // FIXME: what if sync load is suppressed? - // FIXME: what about receive checksum and the nasty internal shift register delays? - int const word_length(get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0)); - if (++m_rx_bit == word_length) + m_rx_sr >>= 1; + m_rx_one_cnt = 0; + if (m_rr0 & RR0_BREAK_ABORT) { - uint16_t const data((m_rx_sr >> (16 - word_length)) | (~uint16_t(0) << word_length)); - m_rx_bit = 0; - LOGRCV("%s() Channel %c Received Data %02x\n", FUNCNAME, 'A' + m_index, data & 0xff); - queue_received(data, 0U); + m_rr0 &= ~RR0_BREAK_ABORT; + if (!m_brk_latched) { + m_brk_latched = 1; + trigger_ext_int(); + } } } - LOGBIT("%s() Channel %c Read Bit %d\n", FUNCNAME, 'A' + m_index, m_rxd); - m_rx_sr = (m_rx_sr >> 1) | (m_rxd ? 0x8000U : 0x0000U); + switch (m_rx_sync_fsm) + { + case SYNC_FSM_HUNT: + case SYNC_FSM_EVICT: + if (flag_matched) + { + // Got sync + m_rx_sync_fsm = SYNC_FSM_EVICT; + m_rx_bit = 0; + m_rx_bit_limit = 7; + LOGRCV("Channel %c SDLC Sync Acquired\n", 'A' + m_index); + if (m_rr0 & RR0_SYNC_HUNT) + { + m_rr0 &= ~RR0_SYNC_HUNT; + trigger_ext_int(); + } + } + else if (m_rx_sync_fsm == SYNC_FSM_EVICT && ++m_rx_bit == m_rx_bit_limit) + { + m_rx_sync_fsm = SYNC_FSM_1ST_CHAR; + m_rx_crc = ~0; + m_rx_bit = 0; + m_rx_bit_limit = 11; + } + break; + + case SYNC_FSM_1ST_CHAR: + case SYNC_FSM_IN_FRAME: + if (zero_deleted) + break; + if (++m_rx_bit == m_rx_bit_limit) + m_rx_bit = 0; + if (flag_matched) + { + // Got closing flag + if (m_rx_sync_fsm != SYNC_FSM_1ST_CHAR) + { + // Frame ended + LOGRCV("SDLC frame ended, CRC=%04x, residual=%d\n" , m_rx_crc , m_rx_bit); + uint8_t status_byte = RR1_END_OF_FRAME; + if (m_rx_crc != SDLC_RESIDUAL) + status_byte |= RR1_CRC_FRAMING_ERROR; + // The residue code is nothing but the bit-reversed accumulated bit count + if (BIT(m_rx_bit , 0)) + status_byte |= 0x08; + if (BIT(m_rx_bit , 1)) + status_byte |= 0x04; + if (BIT(m_rx_bit , 2)) + status_byte |= 0x02; + // Is the last character masked according to rx word length? + // We don't mask it here, after all it just holds a (useless) part of CRC + queue_received(m_rx_sr & 0xff , status_byte); + } + // else: frame ended before 11 bits are received, discard it + m_rx_sync_fsm = SYNC_FSM_EVICT; + m_rx_bit = 0; + m_rx_bit_limit = 7; + } + else + { + // Update rx CRC + update_crc(m_rx_crc , sync_sr_out); + LOGBIT("SDLC CRC=%04x/%d\n" , m_rx_crc , sync_sr_out); + + if (m_rx_bit == 0) + { + // Check for address byte + if (m_rx_sync_fsm == SYNC_FSM_1ST_CHAR && (m_wr3 & WR3_ADDRESS_SEARCH_MODE) && + (m_rx_sr & 0xff) != 0xff && (m_rx_sr & 0xff) != m_wr6) + { + LOGRCV("Channel %c SDLC Address %02x not matching %02x\n" , 'A' + m_index , m_rx_sr & 0xff , m_wr6); + // Address not matching, ignore this frame + m_rx_sync_fsm = SYNC_FSM_HUNT; + } + else + { + m_rx_bit_limit = get_rx_word_length(); + uint8_t data = m_rx_sr & 0xff; + if (m_rx_bit_limit != 8) + // Fill the unused part of character with ones + data |= ~((1U << m_rx_bit_limit) - 1); + LOGRCV("SDLC rx data=%02x (%d bits)\n" , data , m_rx_bit_limit); + queue_received(data , 0); + m_rx_sync_fsm = SYNC_FSM_IN_FRAME; + } + } + } + break; + + default: + LOG("Invalid SDLC FSM state (%d)\n" , m_rx_sync_fsm); + m_rx_sync_fsm = SYNC_FSM_HUNT; + } } //------------------------------------------------- @@ -1844,20 +2135,6 @@ void z80sio_channel::receive_data() void z80sio_channel::queue_received(uint16_t data, uint32_t error) { - if (m_wr4 & WR4_PARITY_ENABLE) - { - int const word_length = get_rx_word_length(); - uint16_t par(data); - for (int i = 1; word_length >= i; ++i) - par ^= BIT(par, i); - - if (bool(BIT(par, 0)) == bool(m_wr4 & WR4_PARITY_EVEN)) - { - LOGRCV(" Parity error detected\n"); - error |= RR1_PARITY_ERROR; - } - } - if (3 == m_rx_fifo_depth) { LOG(" Receive FIFO overrun detected\n"); @@ -1875,7 +2152,7 @@ void z80sio_channel::queue_received(uint16_t data, uint32_t error) m_rx_data_fifo |= uint32_t(data & 0x00ffU) << (8 * m_rx_fifo_depth); m_rx_error_fifo |= error << (8 * m_rx_fifo_depth); if (!m_rx_fifo_depth) - m_rr1 |= uint8_t(error); + m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(error); ++m_rx_fifo_depth; } @@ -1887,7 +2164,7 @@ void z80sio_channel::queue_received(uint16_t data, uint32_t error) switch (m_wr1 & WR1_RX_INT_MODE_MASK) { case WR1_RX_INT_FIRST: - if (m_rx_first || (error & (RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR))) + if (m_rx_first || (error & get_special_rx_mask())) m_uart->trigger_interrupt(m_index, INT_RECEIVE); m_rx_first = 0; break; @@ -1915,9 +2192,8 @@ WRITE_LINE_MEMBER( z80sio_channel::cts_w ) m_cts = state; trigger_ext_int(); - // this may enable transmission - if (!state) - transmit_enable(); + // this may enable/disable transmission + transmit_enable(); } } @@ -1966,27 +2242,21 @@ WRITE_LINE_MEMBER( z80sio_channel::sync_w ) WRITE_LINE_MEMBER( z80sio_channel::rxc_w ) { //LOG("Z80SIO \"%s\" Channel %c : Receiver Clock Pulse\n", owner()->tag(), m_index + 'A'); + //if ((receive_allowed() || m_rx_bit != 0) && state && !m_rx_clock) if (receive_allowed() && state && !m_rx_clock) { // RxD sampled on rising edge int const clocks = get_clock_mode() - 1; - // break termination detection - // TODO: how does this interact with receiver being disable or synchronous modes? - if (m_rxd && !m_brk_latched && (m_rr0 & RR0_BREAK_ABORT)) - { - LOGRCV("Break termination detected\n"); - m_rr0 &= ~RR0_BREAK_ABORT; - m_brk_latched = 1; - trigger_ext_int(); - } - if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) { // synchronous receive is a different beast if (!m_rx_count) { - sync_receive(); + if ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC) + sdlc_receive(); + else + sync_receive(); m_rx_count = clocks; } else @@ -1994,70 +2264,98 @@ WRITE_LINE_MEMBER( z80sio_channel::rxc_w ) --m_rx_count; } } - else if (!m_rx_bit) + else if (!(m_rr0 & RR0_BREAK_ABORT) || m_rxd) { - // look for start bit - if (m_rxd) - { - // line idle - m_rx_count = (std::max)(m_rx_count, (clocks / 2) + 1) - 1; - } - else if (!m_rx_count) + // break termination detection + if ((m_rr0 & RR0_BREAK_ABORT) && m_rxd) { - // half a bit period expired, start shifting bits - m_rx_count = clocks; - ++m_rx_bit; - m_rx_sr = ~uint16_t(0U); + LOGRCV("Break termination detected\n"); + m_rr0 &= ~RR0_BREAK_ABORT; + if (!m_brk_latched) { + m_brk_latched = 1; + trigger_ext_int(); + } } - else + if (!m_rx_bit) { - // ensure start bit lasts long enough - --m_rx_count; + // look for start bit + if (m_rxd) + { + // line idle + m_rx_count = (std::max)(m_rx_count, (clocks / 2) + 1) - 1; + } + else if (!m_rx_count) + { + // half a bit period expired, start shifting bits + m_rx_count = clocks; + ++m_rx_bit; + m_rx_sr = ~uint16_t(0U); + } + else + { + // ensure start bit lasts long enough + --m_rx_count; + } } - } - else if (!m_rx_count) - { - // sample a data/parity/stop bit - if (!m_rxd) - m_rx_sr &= ~uint16_t(1U << (m_rx_bit - 1)); - int const word_length(get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0)); - bool const stop_reached((word_length + 1) == m_rx_bit); - LOGBIT("%s() Channel %c Received %s Bit %d\n", FUNCNAME, 'A' + m_index, stop_reached ? "Stop" : "Data", m_rxd); - - if (stop_reached) + else if (!m_rx_count) { - // this is the stop bit - framing error adds a half bit period - m_rx_count = m_rxd ? (clocks / 2) : clocks; - m_rx_bit = 0; - - LOGRCV("%s() Channel %c Received Data %02x\n", FUNCNAME, 'A' + m_index, m_rx_sr & 0xff); - - // check framing errors and break condition - uint16_t const stop_bit = uint16_t(1U) << word_length; - bool const brk(!(m_rx_sr & ((stop_bit << 1) - 1))); - queue_received(m_rx_sr | stop_bit, (m_rx_sr & stop_bit) ? 0U : RR1_CRC_FRAMING_ERROR); - - // break interrupt - if (brk && !m_brk_latched && !(m_rr0 & RR0_BREAK_ABORT)) + // sample a data/parity/stop bit + if (!m_rxd) + m_rx_sr &= ~uint16_t(1U << (m_rx_bit - 1)); + int const word_length(get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0)); + bool const stop_reached((word_length + 1) == m_rx_bit); + LOGBIT("%s() Channel %c Received %s Bit %d\n", FUNCNAME, 'A' + m_index, stop_reached ? "Stop" : "Data", m_rxd); + + if (stop_reached) { - LOGRCV("Break detected\n"); - m_rr0 |= RR0_BREAK_ABORT; - m_brk_latched = 1; - trigger_ext_int(); + // this is the stop bit - framing error adds a half bit period + m_rx_count = m_rxd ? (clocks / 2) : clocks; + m_rx_bit = 0; + + LOGRCV("%s() Channel %c Received Data %02x\n", FUNCNAME, 'A' + m_index, m_rx_sr & 0xff); + + // check framing errors and break condition + uint16_t const stop_bit = uint16_t(1U) << word_length; + bool const brk(!(m_rx_sr & ((stop_bit << 1) - 1))); + uint8_t error = brk || (m_rx_sr & stop_bit) ? 0U : RR1_CRC_FRAMING_ERROR; + if (m_wr4 & WR4_PARITY_ENABLE) + { + int const word_length = get_rx_word_length(); + uint16_t par(m_rx_sr); + for (int i = 1; word_length >= i; ++i) + par ^= BIT(par, i); + + if (bool(BIT(par, 0)) == bool(m_wr4 & WR4_PARITY_EVEN)) + { + LOGRCV(" Parity error detected\n"); + error |= RR1_PARITY_ERROR; + } + } + + queue_received(m_rx_sr | stop_bit, error); + + // break interrupt + if (brk && !m_brk_latched && !(m_rr0 & RR0_BREAK_ABORT)) + { + LOGRCV("Break detected\n"); + m_rr0 |= RR0_BREAK_ABORT; + m_brk_latched = 1; + trigger_ext_int(); + } + } + else + { + // wait a whole bit period for the next bit + m_rx_count = clocks; + ++m_rx_bit; } } else { - // wait a whole bit period for the next bit - m_rx_count = clocks; - ++m_rx_bit; + // bit period hasn't expired + --m_rx_count; } } - else - { - // bit period hasn't expired - --m_rx_count; - } } m_rx_clock = state; } @@ -2072,87 +2370,142 @@ WRITE_LINE_MEMBER( z80sio_channel::txc_w ) if (!state && m_tx_clock) { // falling edge active - if (m_tx_count) + if (m_tx_count == 0) { - // divide transmit clock - --m_tx_count; - } - else if (!m_tx_bits) - { - // idle marking line - if (!m_txd) - { - m_txd = 1; - if (!(m_wr5 & WR5_SEND_BREAK)) - out_txd_cb(1); - } - - if (((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC) && (m_rr0 & RR0_TX_BUFFER_EMPTY)) - { - // when the RTS bit is reset in asynchronous mode, the _RTS output goes high after the transmitter empties - if (!(m_wr5 & WR5_RTS) && !m_rts) - set_rts(1); // TODO: if transmission is disabled when the data buffer is full, is this still asserted? - - // if transmit buffer is empty in asynchronous mode then all characters have been sent - m_rr1 |= RR1_ALL_SENT; - } + // x1 clock + m_tx_phase = true; + // Shift delay by a half bit and duplicate last input bit + m_tx_delay = (m_tx_delay << 1) | (m_tx_delay & 1); } else + m_tx_count--; + if (m_tx_count == 0) { - bool const sdlc_mode((m_wr4 & (WR4_STOP_BITS_MASK | WR4_SYNC_MODE_MASK)) == (WR4_STOP_BITS_SYNC | WR4_SYNC_MODE_SDLC)); - bool const framing(m_tx_flags & TX_FLAG_FRAMING); - bool const stuff_zero(sdlc_mode && !framing && ((m_tx_hist & 0x1fU) == 0x1fU)); - - // have bits, shift out - int const db(stuff_zero ? 0 : BIT(m_tx_sr, 0)); - if (!stuff_zero) - { - LOGBIT("%s() Channel %c transmit %s bit %d m_wr5:%02x\n", FUNCNAME, 'A' + m_index, framing ? "framing" : "data", db, m_wr5); - if (m_tx_parity >= m_tx_bits) - m_tx_parity = 0; - else if (m_tx_parity) - m_tx_sr ^= uint16_t(db) << (m_tx_bits - m_tx_parity); - m_tx_sr >>= 1; - - if (m_tx_flags & TX_FLAG_CRC) - { - uint16_t const poly((m_wr5 & WR5_CRC16) ? 0x8005U : device_sdlc_consumer_interface::POLY_SDLC); - m_tx_crc = device_sdlc_consumer_interface::update_frame_check(poly, m_tx_crc, db); - } - } - else + m_tx_phase = !m_tx_phase; + // Load delay for half bit + m_tx_count = get_clock_mode() / 2; + // Send out a delayed half bit + bool new_txd = BIT(m_tx_delay , 3); + if (new_txd != m_txd && !(m_wr5 & WR5_SEND_BREAK)) { - LOGBIT("%s() Channel %c stuff bit %d m_wr5:%02x\n", FUNCNAME, 'A' + m_index, db, m_wr5); + LOGBIT("%.6f TX %d DLY %x\n" , machine().time().as_double() , new_txd , m_tx_delay & 0xf); + out_txd_cb(new_txd); } - m_tx_hist = (m_tx_hist << 1) | db; - - // update output line state - if (bool(m_txd) != bool(db)) + m_txd = new_txd; + // Check for ALL SENT condition + if (!(m_rr1 & RR1_ALL_SENT) && BIT(m_all_sent_delay , 3)) { - m_txd = db; - if (!(m_wr5 & WR5_SEND_BREAK)) - out_txd_cb(m_txd); + LOGBIT("%.6f ALL_SENT\n" , machine().time().as_double()); + m_rr1 |= RR1_ALL_SENT; + if (!(m_wr5 & WR5_RTS)) + set_rts(1); } - - // calculate next bit time - m_tx_count = get_clock_mode(); - if (!stuff_zero && !--m_tx_bits) + // Shift delay by a half bit and duplicate last input bit + // When m_tx_phase is false, LSB is replaced by new bit (see below) + m_tx_delay = (m_tx_delay << 1) | (m_tx_delay & 1); + m_all_sent_delay <<= 1; + if (!m_tx_phase) { - switch (m_wr4 & WR4_STOP_BITS_MASK) + // Generate a new bit + bool new_bit = false; + if ((m_wr4 & (WR4_SYNC_MODE_MASK | WR4_STOP_BITS_MASK)) == (WR4_SYNC_MODE_SDLC | WR4_STOP_BITS_SYNC) && + !(m_tx_flags & TX_FLAG_FRAMING) && (m_tx_hist & 0x1f) == 0x1f) + // SDLC, not sending framing & 5 ones in a row: do zero insertion + new_bit = false; + else { - case WR4_STOP_BITS_SYNC: - case WR4_STOP_BITS_1: - break; - case WR4_STOP_BITS_1_5: - m_tx_count = ((m_tx_count * 3) + 1) / 2; // TODO: what does 1.5 stop bits do in TxC/1 mode? the +1 here rounds it up - break; - case WR4_STOP_BITS_2: - m_tx_count *= 2; - break; + bool get_out = false; + while (!get_out) + { + // Pattern for parity bit in SR? + // 17x 1 || 000 + if ((m_tx_sr & TX_SR_MASK) == 0xffff8) + { + if ((m_wr4 & WR4_PARITY_ENABLE) != 0 && + (m_tx_flags & TX_FLAG_DATA_TX)) + { + new_bit = m_tx_parity; + if (!(m_wr4 & WR4_PARITY_EVEN)) + new_bit = !new_bit; + get_out = true; + } + } + // Pattern for 1st stop bit? + // 18x 1 || 00 + else if ((m_tx_sr & TX_SR_MASK) == 0xffffc) + { + if ((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC) + { + new_bit = true; + get_out = true; + } + } + // Pattern for 2nd stop bit? + // 19x 1 || 0 + else if ((m_tx_sr & TX_SR_MASK) == 0xffffe) + { + if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_1_5 || + (m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_2) + { + new_bit = true; + if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_1_5) + // Force current stop bit to last for 1/2 bit time + m_tx_phase = true; + get_out = true; + } + } + // Pattern for idle tx? + // 20x 1 + else if (is_tx_idle()) + { + transmit_complete(); + if (is_tx_idle()) + { + new_bit = true; + get_out = true; + } + else + continue; + } + else if (m_tx_flags & TX_FLAG_CRC_TX) + { + // CRC bits (from MSB to LSB) + new_bit = BIT(m_tx_crc , 15); + m_tx_crc <<= 1; + if ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC) + m_tx_crc |= 1; + get_out = true; + } + else + { + // Start bit or data bits + new_bit = BIT(m_tx_sr , 0); + // Update parity + if (new_bit) + m_tx_parity = !m_tx_parity; + // Update CRC + if (m_tx_flags & TX_FLAG_CRC) + { + LOGBIT("CRC %04x/%d\n" , m_tx_crc , new_bit); + update_crc(m_tx_crc , new_bit); + } + get_out = true; + } + // Shift right 1 bit && insert 1 at MSB + m_tx_sr = (m_tx_sr >> 1) | 0x80000; + } + if ((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC && is_tx_idle() && (m_rr0 & RR0_TX_BUFFER_EMPTY)) + m_all_sent_delay |= 1U; + else + m_all_sent_delay = 0; } - transmit_complete(); + if (m_tx_flags & TX_FLAG_FRAMING) + m_tx_hist = 0; + else + m_tx_hist = (m_tx_hist << 1) | new_bit; + // Insert new bit in delay register + m_tx_delay = (m_tx_delay & ~1U) | new_bit; } - --m_tx_count; } } m_tx_clock = state; diff --git a/src/devices/machine/z80sio.h b/src/devices/machine/z80sio.h index fabe6a20bfc..c2d1c5e0f8e 100644 --- a/src/devices/machine/z80sio.h +++ b/src/devices/machine/z80sio.h @@ -167,8 +167,19 @@ protected: enum : uint8_t { TX_FLAG_CRC = 1U << 0, // include in checksum calculation - TX_FLAG_FRAMING = 1U << 1, // tranmitting framing bits - TX_FLAG_SPECIAL = 1U << 2 // transmitting checksum or abort sequence + TX_FLAG_FRAMING = 1U << 1, // transmitting framing bits + TX_FLAG_ABORT_TX= 1U << 2, // transmitting abort sequence + TX_FLAG_CRC_TX = 1U << 3, // transmitting CRC value + TX_FLAG_DATA_TX = 1U << 4 // transmitting frame data + }; + + // Sync/SDLC FSM states + enum + { + SYNC_FSM_HUNT = 0, // Hunt for start sync/flag + SYNC_FSM_EVICT = 1, // Evict flag from sync SR + SYNC_FSM_1ST_CHAR = 2, // Receiving 1st character + SYNC_FSM_IN_FRAME = 3 // Inside a frame }; z80sio_channel( @@ -191,7 +202,6 @@ protected: int get_clock_mode(); int get_rx_word_length(); int get_tx_word_length() const; - int get_tx_word_length(uint8_t data) const; // receiver state int m_rx_fifo_depth; @@ -200,26 +210,37 @@ protected: int m_rx_clock; // receive clock line state int m_rx_count; // clocks until next sample + bool m_dlyd_rxd; // delayed RxD int m_rx_bit; // receive data bit (0 = start bit, 1 = LSB, etc.) + int m_rx_bit_limit; // bits to assemble for next character (sync/SDLC) + int m_rx_sync_fsm; // Sync/SDLC FSM state + uint8_t m_rx_one_cnt; // SDLC: counter to delete stuffed zeros uint16_t m_rx_sr; // receive shift register + uint8_t m_rx_sync_sr; // rx sync SR + uint8_t m_rx_crc_delay; // rx CRC delay SR + uint16_t m_rx_crc; // rx CRC accumulator + bool m_rx_crc_en; // rx CRC enabled + bool m_rx_parity; // accumulated parity int m_rx_first; // first character received - int m_rx_break; // receive break condition int m_rxd; - int m_sh; // sync hunt // transmitter state uint8_t m_tx_data; int m_tx_clock; // transmit clock line state int m_tx_count; // clocks until next bit transition - int m_tx_bits; // remaining bits in shift register - int m_tx_parity; // parity bit position or zero if disabled - uint16_t m_tx_sr; // transmit shift register + bool m_tx_phase; // phase of bit clock + bool m_tx_parity; // accumulated parity + bool m_tx_in_pkt; // In active part of packet (sync mode) + bool m_tx_forced_sync; // Force sync/flag + uint32_t m_tx_sr; // transmit shift register uint16_t m_tx_crc; // calculated transmit checksum uint8_t m_tx_hist; // transmit history (for bitstuffing) uint8_t m_tx_flags; // internal transmit control flags + uint8_t m_tx_delay; // 2-bit tx delay (4 half-bits) + uint8_t m_all_sent_delay; // SR for all-sent delay int m_txd; int m_dtr; // data terminal ready @@ -247,17 +268,24 @@ private: bool transmit_allowed() const; void receive_enabled(); + void enter_hunt_mode(); void sync_receive(); + void sdlc_receive(); void receive_data(); void queue_received(uint16_t data, uint32_t error); void advance_rx_fifo(); + uint8_t get_special_rx_mask() const; + bool is_tx_idle() const; void transmit_enable(); void transmit_complete(); void async_tx_setup(); void sync_tx_sr_empty(); - void tx_setup(uint16_t data, int bits, int parity, bool framing, bool special); + void tx_setup(uint16_t data, int bits, bool framing, bool crc_tx, bool abort_tx); void tx_setup_idle(); + bool get_tx_empty() const; + void set_tx_empty(bool prev_state, bool new_state); + void update_crc(uint16_t& crc , bool bit); void reset_ext_status(); void read_ext(); diff --git a/src/mame/drivers/hp9845.cpp b/src/mame/drivers/hp9845.cpp index d4f64f10393..c01b496ca23 100644 --- a/src/mame/drivers/hp9845.cpp +++ b/src/mame/drivers/hp9845.cpp @@ -479,6 +479,10 @@ void hp9845_base_state::device_reset() if ((sc = m_io_slot[i]->get_rw_handlers(rhandler , whandler)) >= 0) { logerror("Install R/W handlers for slot %u @ SC = %d\n", i, sc); m_ppu->space(AS_IO).install_readwrite_handler(sc * 4 , sc * 4 + 3 , rhandler , whandler); + if (m_io_slot[ i ]->has_dual_sc()) { + logerror("Installing dual SC\n"); + m_ppu->space(AS_IO).install_readwrite_handler(sc * 4 + 4 , sc * 4 + 7 , rhandler , whandler); + } } m_slot_sc[ i ] = sc; } @@ -668,6 +672,27 @@ void hp9845_base_state::set_flg_slot(unsigned slot , int state) m_io_sys->set_flg(uint8_t(sc) , state); } +void hp9845_base_state::set_irq_nextsc_slot(unsigned slot , int state) +{ + int sc = m_slot_sc[ slot ]; + assert(sc >= 0); + m_io_sys->set_irq(uint8_t(sc + 1) , state); +} + +void hp9845_base_state::set_sts_nextsc_slot(unsigned slot , int state) +{ + int sc = m_slot_sc[ slot ]; + assert(sc >= 0); + m_io_sys->set_sts(uint8_t(sc + 1) , state); +} + +void hp9845_base_state::set_flg_nextsc_slot(unsigned slot , int state) +{ + int sc = m_slot_sc[ slot ]; + assert(sc >= 0); + m_io_sys->set_flg(uint8_t(sc + 1) , state); +} + void hp9845_base_state::set_dmar_slot(unsigned slot , int state) { int sc = m_slot_sc[ slot ]; @@ -3692,6 +3717,9 @@ void hp9845_base_state::hp9845_base(machine_config &config) tmp.irq().set([this , slot](int state) { set_irq_slot(slot , state); }); tmp.sts().set([this , slot](int state) { set_sts_slot(slot , state); }); tmp.flg().set([this , slot](int state) { set_flg_slot(slot , state); }); + tmp.irq_nextsc().set([this , slot](int state) { set_irq_nextsc_slot(slot , state); }); + tmp.sts_nextsc().set([this , slot](int state) { set_sts_nextsc_slot(slot , state); }); + tmp.flg_nextsc().set([this , slot](int state) { set_flg_nextsc_slot(slot , state); }); tmp.dmar().set([this , slot](int state) { set_dmar_slot(slot , state); }); } diff --git a/src/mame/includes/hp9845.h b/src/mame/includes/hp9845.h index 1be0c35adf1..f4f51c37668 100644 --- a/src/mame/includes/hp9845.h +++ b/src/mame/includes/hp9845.h @@ -74,6 +74,9 @@ protected: void set_irq_slot(unsigned slot , int state); void set_sts_slot(unsigned slot , int state); void set_flg_slot(unsigned slot , int state); + void set_irq_nextsc_slot(unsigned slot , int state); + void set_sts_nextsc_slot(unsigned slot , int state); + void set_flg_nextsc_slot(unsigned slot , int state); void set_dmar_slot(unsigned slot , int state); // Character generator |