summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author fulivi <fulivi@users.noreply.github.com>2019-05-26 18:00:33 +0200
committer R. Belmont <rb6502@users.noreply.github.com>2019-05-26 12:00:33 -0400
commit41c456c3a61b49d71f75e3c90bff3044c621b5f1 (patch)
tree9f888378b58c4ed6e825065642f1e54797c42d6b
parentecf043eca1bc1144ac4b9766cfb4ea7b113cc57c (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.lua2
-rw-r--r--src/devices/bus/hp9845_io/98046.cpp684
-rw-r--r--src/devices/bus/hp9845_io/98046.h103
-rw-r--r--src/devices/bus/hp9845_io/hp9845_io.cpp60
-rw-r--r--src/devices/bus/hp9845_io/hp9845_io.h16
-rw-r--r--src/devices/bus/hp_optroms/hp_optrom.cpp9
-rw-r--r--src/devices/bus/hp_optroms/hp_optrom.h2
-rw-r--r--src/devices/machine/z80sio.cpp1007
-rw-r--r--src/devices/machine/z80sio.h46
-rw-r--r--src/mame/drivers/hp9845.cpp28
-rw-r--r--src/mame/includes/hp9845.h3
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