summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine')
-rw-r--r--src/devices/machine/1mb5.cpp295
-rw-r--r--src/devices/machine/1mb5.h95
-rw-r--r--src/devices/machine/28fxxx.cpp236
-rw-r--r--src/devices/machine/28fxxx.h84
-rw-r--r--src/devices/machine/6522via.cpp17
-rw-r--r--src/devices/machine/68307.cpp4
-rw-r--r--src/devices/machine/68307.h3
-rw-r--r--src/devices/machine/68561mpcc.cpp2
-rw-r--r--src/devices/machine/74123.cpp26
-rw-r--r--src/devices/machine/74123.h10
-rw-r--r--src/devices/machine/74259.cpp15
-rw-r--r--src/devices/machine/74259.h3
-rw-r--r--src/devices/machine/adc083x.cpp8
-rw-r--r--src/devices/machine/adc0844.cpp208
-rw-r--r--src/devices/machine/adc0844.h156
-rw-r--r--src/devices/machine/am9513.cpp12
-rw-r--r--src/devices/machine/atmel_arm_aic.cpp93
-rw-r--r--src/devices/machine/atmel_arm_aic.h63
-rw-r--r--src/devices/machine/ay31015.cpp13
-rw-r--r--src/devices/machine/cdp1852.h2
-rw-r--r--src/devices/machine/com8116.cpp2
-rw-r--r--src/devices/machine/ds1204.cpp2
-rw-r--r--src/devices/machine/ds128x.cpp12
-rw-r--r--src/devices/machine/ds128x.h1
-rw-r--r--src/devices/machine/ds1315.cpp49
-rw-r--r--src/devices/machine/ds1315.h24
-rw-r--r--src/devices/machine/eeprom.cpp8
-rw-r--r--src/devices/machine/eepromser.cpp43
-rw-r--r--src/devices/machine/eepromser.h9
-rw-r--r--src/devices/machine/genpc.cpp11
-rw-r--r--src/devices/machine/genpc.h17
-rw-r--r--src/devices/machine/i8155.cpp250
-rw-r--r--src/devices/machine/i8155.h33
-rw-r--r--src/devices/machine/input_merger.cpp4
-rw-r--r--src/devices/machine/mc14411.cpp2
-rw-r--r--src/devices/machine/mc146818.cpp61
-rw-r--r--src/devices/machine/mc146818.h3
-rw-r--r--src/devices/machine/mc68681.cpp282
-rw-r--r--src/devices/machine/mc68681.h104
-rw-r--r--src/devices/machine/mc68901.cpp58
-rw-r--r--src/devices/machine/mc68901.h4
-rw-r--r--src/devices/machine/netlist.cpp48
-rw-r--r--src/devices/machine/netlist.h23
-rw-r--r--src/devices/machine/phi.cpp2
-rw-r--r--src/devices/machine/sdlc.cpp302
-rw-r--r--src/devices/machine/sdlc.h116
-rw-r--r--src/devices/machine/sega_scu.cpp808
-rw-r--r--src/devices/machine/sega_scu.h156
-rw-r--r--src/devices/machine/smc91c9x.cpp630
-rw-r--r--src/devices/machine/smc91c9x.h28
-rw-r--r--src/devices/machine/smpc.cpp129
-rw-r--r--src/devices/machine/smpc.h26
-rw-r--r--src/devices/machine/stvcd.cpp13
-rw-r--r--src/devices/machine/terminal.cpp4
-rw-r--r--src/devices/machine/upd4701.cpp3
-rw-r--r--src/devices/machine/vrc5074.cpp17
-rw-r--r--src/devices/machine/wd_fdc.cpp18
-rw-r--r--src/devices/machine/wd_fdc.h12
-rw-r--r--src/devices/machine/x2212.cpp8
-rw-r--r--src/devices/machine/z80scc.cpp526
-rw-r--r--src/devices/machine/z80scc.h355
-rw-r--r--src/devices/machine/z80sio.cpp1934
-rw-r--r--src/devices/machine/z80sio.h391
63 files changed, 5777 insertions, 2096 deletions
diff --git a/src/devices/machine/1mb5.cpp b/src/devices/machine/1mb5.cpp
new file mode 100644
index 00000000000..50a3ea59365
--- /dev/null
+++ b/src/devices/machine/1mb5.cpp
@@ -0,0 +1,295 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/*********************************************************************
+
+ 1mb5.cpp
+
+ HP-8x I/O Translator chip (1MB5-0101)
+
+ Reference for this chip:
+ HP, aug 79, 1MB5 Detailed specification - Translator chip
+
+*********************************************************************/
+
+#include "emu.h"
+#include "1mb5.h"
+
+// Debugging
+#define VERBOSE 0
+#include "logmacro.h"
+
+// device type definition
+DEFINE_DEVICE_TYPE(HP_1MB5, hp_1mb5_device, "hp_1mb5", "HP 1MB5")
+
+// 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);
+ }
+
+ template<typename T> void COPY_BIT(bool bit , T& w , unsigned n)
+ {
+ if (bit) {
+ BIT_SET(w , n);
+ } else {
+ BIT_CLR(w , n);
+ }
+ }
+}
+
+hp_1mb5_device::hp_1mb5_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+: device_t(mconfig , HP_1MB5 , tag , owner , clock),
+ m_irl_handler(*this),
+ m_halt_handler(*this),
+ m_reset_handler(*this),
+ m_int_handler(*this)
+{
+}
+
+READ8_MEMBER(hp_1mb5_device::cpu_r)
+{
+ uint8_t res = 0;
+
+ switch (offset) {
+ case 0:
+ // Read SR
+ res = m_sr & 0x7e;
+ if (m_obf) {
+ BIT_SET(res , 7);
+ }
+ if (m_ibf) {
+ BIT_SET(res , 0);
+ }
+ break;
+
+ case 1:
+ // Read IB
+ res = m_ib;
+ m_ibf = false;
+ update_halt();
+ break;
+ }
+
+ //LOG("RD %u=%02x\n" , offset , res);
+ return res;
+}
+
+WRITE8_MEMBER(hp_1mb5_device::cpu_w)
+{
+ //LOG("WR %u=%02x\n" , offset , data);
+ bool need_resched = false;
+
+ switch (offset) {
+ case 0:
+ // Write CR
+ m_cr = data;
+ need_resched |= set_reset(BIT(m_cr , 7));
+ need_resched |= set_int(!BIT(m_cr , 0));
+ break;
+
+ case 1:
+ // Write OB
+ m_ob = data;
+ m_obf = true;
+ update_halt();
+ break;
+ }
+ if (need_resched) {
+ LOG("resched %s\n" , space.device().tag());
+ space.device().execute().yield();
+ }
+}
+
+READ8_MEMBER(hp_1mb5_device::uc_r)
+{
+ uint8_t res = 0;
+ bool need_resched = false;
+
+ switch (offset) {
+ case 0:
+ // Read CR
+ res = m_cr & 0x7e;
+ if (m_obf) {
+ BIT_SET(res , 7);
+ }
+ if (m_ibf) {
+ BIT_SET(res , 0);
+ }
+ break;
+
+ case 1:
+ // Read OB
+ res = m_ob;
+ m_obf = false;
+ need_resched |= update_halt();
+ break;
+ }
+
+ if (need_resched) {
+ LOG("resched %s\n" , space.device().tag());
+ space.device().execute().spin();
+ }
+ //LOG("RDU %u=%02x\n" , offset , res);
+ return res;
+}
+
+WRITE8_MEMBER(hp_1mb5_device::uc_w)
+{
+ //LOG("WRU %u=%02x SR=%02x\n" , offset , data , m_sr);
+ bool need_resched = false;
+
+ switch (offset) {
+ case 0:
+ // Write SR
+ if (!BIT(m_sr , 0) && BIT(data , 0)) {
+ need_resched |= set_service(true);
+ }
+ m_sr = data;
+ m_hlten = BIT(m_sr , 7);
+ if (update_halt() && !m_halt) {
+ need_resched = true;
+ }
+ break;
+
+ case 1:
+ // Write IB
+ m_ib = data;
+ m_ibf = true;
+ need_resched |= update_halt();
+ break;
+ }
+ if (need_resched) {
+ LOG("resched %s\n" , space.device().tag());
+ space.device().execute().spin();
+ }
+}
+
+READ_LINE_MEMBER(hp_1mb5_device::irl_r)
+{
+ return m_service;
+}
+
+READ_LINE_MEMBER(hp_1mb5_device::halt_r)
+{
+ return m_halt;
+}
+
+READ_LINE_MEMBER(hp_1mb5_device::reset_r)
+{
+ return m_reset;
+}
+
+READ_LINE_MEMBER(hp_1mb5_device::int_r)
+{
+ return m_cint;
+}
+
+void hp_1mb5_device::inten()
+{
+ // Enabling interrupts (i.e. writing to 0xff40) removes uC reset
+ set_reset(false);
+}
+
+void hp_1mb5_device::clear_service()
+{
+ set_service(false);
+}
+
+void hp_1mb5_device::device_start()
+{
+ m_irl_handler.resolve_safe();
+ m_halt_handler.resolve_safe();
+ m_reset_handler.resolve_safe();
+ m_int_handler.resolve_safe();
+
+ save_item(NAME(m_sr));
+ save_item(NAME(m_cr));
+ save_item(NAME(m_ib));
+ save_item(NAME(m_ob));
+ save_item(NAME(m_ibf));
+ save_item(NAME(m_obf));
+ save_item(NAME(m_hlten));
+ save_item(NAME(m_service));
+ save_item(NAME(m_cint));
+ save_item(NAME(m_reset));
+ save_item(NAME(m_halt));
+}
+
+void hp_1mb5_device::device_reset()
+{
+ m_sr = 0;
+ m_cr = 0;
+ m_ib = 0;
+ m_ob = 0;
+ m_ibf = false;
+ m_obf = false;
+ m_hlten = false;
+ m_service = false;
+ m_cint = true;
+ m_reset = true;
+ m_halt = false;
+
+ m_irl_handler(false);
+ m_halt_handler(false);
+ m_reset_handler(true);
+ m_int_handler(true);
+}
+
+bool hp_1mb5_device::set_service(bool new_service)
+{
+ if (new_service != m_service) {
+ m_service = new_service;
+ //LOG("irl=%d\n" , m_service);
+ m_irl_handler(m_service);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool hp_1mb5_device::update_halt()
+{
+ bool new_halt = m_hlten && m_obf && !m_ibf;
+ if (new_halt != m_halt) {
+ //LOG("HALT=%d\n" , new_halt);
+ m_halt = new_halt;
+ m_halt_handler(m_halt);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool hp_1mb5_device::set_reset(bool new_reset)
+{
+ if (new_reset != m_reset) {
+ m_reset = new_reset;
+ m_reset_handler(m_reset);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool hp_1mb5_device::set_int(bool new_int)
+{
+ if (new_int != m_cint) {
+ m_cint = new_int;
+ LOG("cint=%d\n" , m_cint);
+ m_int_handler(m_cint);
+ return true;
+ } else {
+ return false;
+ }
+}
diff --git a/src/devices/machine/1mb5.h b/src/devices/machine/1mb5.h
new file mode 100644
index 00000000000..3350e4a968c
--- /dev/null
+++ b/src/devices/machine/1mb5.h
@@ -0,0 +1,95 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/*********************************************************************
+
+ 1mb5.h
+
+ HP-8x I/O Translator chip (1MB5-0101)
+
+*********************************************************************/
+
+#ifndef MAME_MACHINE_1MB5_H
+#define MAME_MACHINE_1MB5_H
+
+#pragma once
+
+#define MCFG_1MB5_IRL_HANDLER(_devcb) \
+ devcb = &hp_1mb5_device::set_irl_handler(*device , DEVCB_##_devcb);
+
+#define MCFG_1MB5_HALT_HANDLER(_devcb) \
+ devcb = &hp_1mb5_device::set_halt_handler(*device , DEVCB_##_devcb);
+
+#define MCFG_1MB5_RESET_HANDLER(_devcb) \
+ devcb = &hp_1mb5_device::set_reset_handler(*device , DEVCB_##_devcb);
+
+#define MCFG_1MB5_INT_HANDLER(_devcb) \
+ devcb = &hp_1mb5_device::set_int_handler(*device , DEVCB_##_devcb);
+
+class hp_1mb5_device : public device_t
+{
+public:
+ // construction/destruction
+ hp_1mb5_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ // static configuration helpers
+ template <class Object> static devcb_base &set_irl_handler(device_t &device, Object &&cb) { return downcast<hp_1mb5_device &>(device).m_irl_handler.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_halt_handler(device_t &device, Object &&cb) { return downcast<hp_1mb5_device &>(device).m_halt_handler.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_reset_handler(device_t &device, Object &&cb) { return downcast<hp_1mb5_device &>(device).m_reset_handler.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_int_handler(device_t &device, Object &&cb) { return downcast<hp_1mb5_device &>(device).m_int_handler.set_callback(std::forward<Object>(cb)); }
+
+ // CPU access
+ DECLARE_READ8_MEMBER(cpu_r);
+ DECLARE_WRITE8_MEMBER(cpu_w);
+
+ // uC access
+ DECLARE_READ8_MEMBER(uc_r);
+ DECLARE_WRITE8_MEMBER(uc_w);
+
+ // Signals to CPU
+ DECLARE_READ_LINE_MEMBER(irl_r);
+ DECLARE_READ_LINE_MEMBER(halt_r);
+
+ // Signals to uC
+ DECLARE_READ_LINE_MEMBER(reset_r);
+ DECLARE_READ_LINE_MEMBER(int_r);
+
+ // Interrupt enable
+ void inten();
+
+ // Interrupt clearing
+ void clear_service();
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+private:
+ devcb_write_line m_irl_handler;
+ devcb_write_line m_halt_handler;
+ devcb_write_line m_reset_handler;
+ devcb_write_line m_int_handler;
+
+ // Registers
+ uint8_t m_sr;
+ uint8_t m_cr;
+ uint8_t m_ib;
+ uint8_t m_ob;
+ bool m_ibf;
+ bool m_obf;
+ bool m_hlten;
+ bool m_service;
+ bool m_cint;
+ bool m_reset;
+ bool m_halt;
+
+ bool set_service(bool new_service);
+ bool update_halt();
+ bool set_reset(bool new_reset);
+ bool set_int(bool new_int);
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(HP_1MB5, hp_1mb5_device)
+
+#endif /* MAME_MACHINE_1MB5_H */
diff --git a/src/devices/machine/28fxxx.cpp b/src/devices/machine/28fxxx.cpp
new file mode 100644
index 00000000000..9ef279dcd44
--- /dev/null
+++ b/src/devices/machine/28fxxx.cpp
@@ -0,0 +1,236 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * An implementation of the 28Fxxx flash memory devices, fabricated by many
+ * suppliers including Intel, Atmel, AMD, SGS, TI and Toshiba. Range includes:
+ *
+ * Part Bits Org.
+ * ------ ----- ------
+ * 28F256 256K 32Kx8
+ * 28F512 512K 64Kx8
+ * 28F010 1024K 128Kx8
+ * 28F020 2048K 256Kx8
+ * 28F040 4096K 512Kx8
+ *
+ * These devices use a JEDEC-standard pinout allowing them to be fitted to a
+ * standard EPROM socket, but have their own command set which is not
+ * compatible with the set implemented by the intelfsh devices. It appears the
+ * command set used here is a variation on the one defined by JEDEC standard
+ * 21-C, as "dual power supply eeprom command set", in figure 3.5.1-11 on page
+ * 23, here: https://www.jedec.org/system/files/docs/3_05_01R14.pdf
+ *
+ * This implementation has been developed primarily against Intel's datasheet,
+ * with some minor adjustments to match details in the AMD equivalent.
+ *
+ * TODO
+ * - implement more variants
+ * - testing in systems other than InterPro
+ *
+ */
+
+#include "emu.h"
+#include "28fxxx.h"
+
+#define VERBOSE 0
+#include "logmacro.h"
+
+// manufacturer codes defined by JEDEC: https://www.jedec.org/system/files/docs/JEP106AV.pdf
+enum manufacturer_codes
+{
+ MFG_AMD = 0x01,
+ MFG_INTEL = 0x89,
+};
+
+DEFINE_DEVICE_TYPE(INTEL_28F010, intel_28f010_device, "intel_28f010", "Intel 28F010 1024K (128K x 8) CMOS Flash Memory")
+DEFINE_DEVICE_TYPE(AMD_28F010, amd_28f010_device, "amd_28f010", "Am28F010 1 Megabit (128K x 8-Bit) CMOS 12.0 Volt, Bulk Erase Flash Memory")
+
+ALLOW_SAVE_TYPE(base_28fxxx_device::state);
+
+base_28fxxx_device::base_28fxxx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 size, u8 manufacturer_code, u8 device_code)
+ : device_t(mconfig, type, tag, owner, clock)
+ , device_nvram_interface(mconfig, *this)
+ , m_region(*this, DEVICE_SELF)
+ , m_size(size)
+ , m_manufacturer_code(manufacturer_code)
+ , m_device_code(device_code)
+ , m_program_power(CLEAR_LINE)
+ , m_state(STATE_READ_MEMORY)
+{
+ assert_always((m_size & (m_size - 1)) == 0, "memory size must be an exact power of two");
+}
+
+intel_28f010_device::intel_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : base_28fxxx_device(mconfig, INTEL_28F010, tag, owner, clock, 0x20000, MFG_INTEL, 0xb4)
+{
+}
+
+amd_28f010_device::amd_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : base_28fxxx_device(mconfig, AMD_28F010, tag, owner, clock, 0x20000, MFG_AMD, 0xa7)
+{
+}
+
+void base_28fxxx_device::device_start()
+{
+ m_data = std::make_unique<u8[]>(m_size);
+
+ save_item(NAME(m_program_power));
+ save_pointer(NAME(m_data.get()), m_size);
+
+ save_item(NAME(m_state));
+ save_item(NAME(m_address_latch));
+}
+
+void base_28fxxx_device::nvram_default()
+{
+ if (m_region.found())
+ {
+ u32 bytes = m_region->bytes();
+ if (bytes > m_size)
+ bytes = m_size;
+
+ for (offs_t offs = 0; offs < bytes; offs++)
+ m_data[offs] = m_region->as_u8(offs);
+ }
+ else
+ erase();
+}
+
+void base_28fxxx_device::nvram_read(emu_file &file)
+{
+ file.read(m_data.get(), m_size);
+}
+
+void base_28fxxx_device::nvram_write(emu_file &file)
+{
+ file.write(m_data.get(), m_size);
+}
+
+void base_28fxxx_device::erase()
+{
+ memset(m_data.get(), 0xff, m_size);
+}
+
+READ8_MEMBER(base_28fxxx_device::read)
+{
+ switch (m_state)
+ {
+ case STATE_READ_MEMORY:
+ return m_data.get()[offset & (m_size - 1)];
+
+ case STATE_READ_IDENTIFIER:
+ switch (offset)
+ {
+ case 0: return m_manufacturer_code;
+ case 1: return m_device_code;
+ }
+ LOG("unknown read identifier offset 0x%08x (%s)\n", offset, machine().describe_context());
+ return space.unmap();
+
+ case STATE_ERASE_VERIFY:
+ return m_data.get()[m_address_latch];
+
+ case STATE_PROGRAM_VERIFY:
+ return m_data.get()[m_address_latch];
+
+ default:
+ LOG("unexpected read offset 0x%08x mask 0x%08x state %d (%s)\n", offset, mem_mask, m_state, machine().describe_context());
+ return space.unmap();
+ }
+}
+
+WRITE8_MEMBER(base_28fxxx_device::write)
+{
+ // writes are ignored unless Vpp is asserted
+ if (!m_program_power)
+ return;
+
+ switch (m_state)
+ {
+ case STATE_ERASE_SETUP:
+ if (data == ERASE)
+ {
+ LOG("erase command initiated (%s)\n", machine().describe_context());
+ erase();
+ m_state = STATE_ERASE;
+ }
+ else if (data == RESET)
+ {
+ LOG("erase command reset (%s)\n", machine().describe_context());
+ m_state = STATE_READ_MEMORY;
+ }
+ else
+ LOG("unexpected command 0x%02x in erase setup state (%s)\n", data, machine().describe_context());
+ break;
+
+ case STATE_ERASE:
+ if (data == ERASE_VERIFY)
+ {
+ m_address_latch = offset & (m_size - 1);
+ m_state = STATE_ERASE_VERIFY;
+ }
+ else
+ LOG("unexpected command 0x%02x in erase state (%s)\n", data, machine().describe_context());
+ break;
+
+ case STATE_PROGRAM_SETUP:
+ m_address_latch = offset & (m_size - 1);
+
+ LOG("programming address 0x%08x data 0x%02x\n", m_address_latch, data);
+
+ // bits can only be written from uncharged (logical 1) to charged (logical 0) state
+ m_data.get()[m_address_latch] &= data;
+
+ m_state = STATE_PROGRAM;
+ break;
+
+ case STATE_PROGRAM:
+ if (data == PROGRAM_VERIFY)
+ m_state = STATE_PROGRAM_VERIFY;
+ else if (data == RESET)
+ {
+ LOG("program command reset (%s)\n", machine().describe_context());
+
+ m_state = STATE_READ_MEMORY;
+ }
+ else
+ LOG("unexpected command 0x%02x in program state (%s)\n", data, machine().describe_context());
+ break;
+
+ default:
+ // start a new command
+ switch (data)
+ {
+ case READ_MEMORY:
+ m_state = STATE_READ_MEMORY;
+ break;
+
+ case READ_IDENTIFIER:
+ case READ_IDENTIFIER_ALT:
+ m_state = STATE_READ_IDENTIFIER;
+ break;
+
+ case ERASE:
+ m_state = STATE_ERASE_SETUP;
+ break;
+
+ case ERASE_VERIFY:
+ m_address_latch = offset & (m_size - 1);
+ m_state = STATE_ERASE_VERIFY;
+ break;
+
+ case PROGRAM:
+ m_state = STATE_PROGRAM_SETUP;
+ break;
+
+ case RESET:
+ m_state = STATE_READ_MEMORY;
+ break;
+
+ default:
+ LOG("unexpected command 0x%02x in state %d (%s)\n", data, m_state, machine().describe_context());
+ break;
+ }
+ break;
+ }
+}
diff --git a/src/devices/machine/28fxxx.h b/src/devices/machine/28fxxx.h
new file mode 100644
index 00000000000..313cda82873
--- /dev/null
+++ b/src/devices/machine/28fxxx.h
@@ -0,0 +1,84 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_MACHINE_28FXXX_H
+#define MAME_MACHINE_28FXXX_H
+
+#pragma once
+
+class base_28fxxx_device : public device_t, public device_nvram_interface
+{
+public:
+ enum commands
+ {
+ READ_MEMORY = 0x00,
+ ERASE = 0x20,
+ PROGRAM = 0x40,
+ READ_IDENTIFIER_ALT = 0x80, // defined in AMD datasheet, but not Intel
+ READ_IDENTIFIER = 0x90,
+ ERASE_VERIFY = 0xa0,
+ PROGRAM_VERIFY = 0xc0,
+ RESET = 0xff
+ };
+
+ DECLARE_WRITE_LINE_MEMBER(vpp) { m_program_power = state; }
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+
+protected:
+ base_28fxxx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 size, u8 manufacturer_code, u8 device_code);
+
+ virtual void device_start() override;
+
+ virtual void nvram_default() override;
+ virtual void nvram_read(emu_file &file) override;
+ virtual void nvram_write(emu_file &file) override;
+
+ optional_memory_region m_region;
+
+private:
+ void erase();
+
+ // device specific parameters
+ const u32 m_size;
+ const u8 m_manufacturer_code;
+ const u8 m_device_code;
+
+ // accessible device state
+ int m_program_power;
+ std::unique_ptr<u8[]> m_data;
+
+ // internal state
+ enum state : u8
+ {
+ STATE_READ_MEMORY,
+ STATE_READ_IDENTIFIER,
+ STATE_ERASE_SETUP,
+ STATE_ERASE,
+ STATE_ERASE_RESET,
+ STATE_ERASE_VERIFY,
+ STATE_PROGRAM_SETUP,
+ STATE_PROGRAM,
+ STATE_PROGRAM_VERIFY
+ };
+ state m_state;
+
+ u32 m_address_latch;
+};
+
+class intel_28f010_device : public base_28fxxx_device
+{
+public:
+ intel_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+};
+
+class amd_28f010_device : public base_28fxxx_device
+{
+public:
+ amd_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+};
+
+DECLARE_DEVICE_TYPE(INTEL_28F010, intel_28f010_device)
+DECLARE_DEVICE_TYPE(AMD_28F010, amd_28f010_device)
+
+#endif // MAME_MACHINE_28FXXX_H
diff --git a/src/devices/machine/6522via.cpp b/src/devices/machine/6522via.cpp
index 6ce5e514a7e..ae1b7da0f61 100644
--- a/src/devices/machine/6522via.cpp
+++ b/src/devices/machine/6522via.cpp
@@ -347,12 +347,17 @@ void via6522_device::clear_int(int data)
{
if (m_ifr & data)
{
+ LOGINT("cleared\n");
m_ifr &= ~data;
output_irq();
LOG("%s:6522VIA chip %s: IFR = %02X\n", machine().describe_context(), tag(), m_ifr);
}
+ else
+ {
+ LOGINT("not cleared\n");
+ }
}
@@ -559,6 +564,7 @@ READ8_MEMBER( via6522_device::read )
val = m_latch_b;
}
+ LOGINT("PB INT ");
CLR_PB_INT();
break;
@@ -573,6 +579,7 @@ READ8_MEMBER( via6522_device::read )
val = m_latch_a;
}
+ LOGINT("PA INT ");
CLR_PA_INT();
if (m_out_ca2 && (CA2_PULSE_OUTPUT(m_pcr) || CA2_AUTO_HS(m_pcr)))
@@ -607,6 +614,7 @@ READ8_MEMBER( via6522_device::read )
break;
case VIA_T1CL:
+ LOGINT("T1CL INT ");
clear_int(INT_T1);
val = get_counter1_value() & 0xFF;
break;
@@ -624,6 +632,7 @@ READ8_MEMBER( via6522_device::read )
break;
case VIA_T2CL:
+ LOGINT("T2CL INT ");
clear_int(INT_T2);
if (m_t2_active)
{
@@ -666,6 +675,7 @@ READ8_MEMBER( via6522_device::read )
m_out_cb1 = 1;
m_cb1_handler(m_out_cb1);
m_shift_counter = 0x0f;
+ LOGINT("SR INT ");
clear_int(INT_SR);
LOGSHIFT(" - ACR: %02x ", m_acr);
if (SI_O2_CONTROL(m_acr) || SO_O2_CONTROL(m_acr))
@@ -730,6 +740,7 @@ WRITE8_MEMBER( via6522_device::write )
output_pb();
}
+ LOGINT("PB INT ");
CLR_PB_INT();
if (m_out_cb2 && CB2_AUTO_HS(m_pcr))
@@ -747,6 +758,7 @@ WRITE8_MEMBER( via6522_device::write )
output_pa();
}
+ LOGINT("PA INT ");
CLR_PA_INT();
if (m_out_ca2 && (CA2_PULSE_OUTPUT(m_pcr) || CA2_AUTO_HS(m_pcr)))
@@ -795,6 +807,7 @@ WRITE8_MEMBER( via6522_device::write )
case VIA_T1LH:
m_t1lh = data;
+ LOGINT("T1LH INT ");
clear_int(INT_T1);
break;
@@ -802,6 +815,7 @@ WRITE8_MEMBER( via6522_device::write )
m_t1ch = m_t1lh = data;
m_t1cl = m_t1ll;
+ LOGINT("T1CH INT ");
clear_int(INT_T1);
m_t1_pb7 = 0;
@@ -823,6 +837,7 @@ WRITE8_MEMBER( via6522_device::write )
m_t2ch = m_t2lh = data;
m_t2cl = m_t2ll;
+ LOGINT("T2 INT ");
clear_int(INT_T2);
if (!T2_COUNT_PB6(m_acr))
@@ -851,6 +866,7 @@ WRITE8_MEMBER( via6522_device::write )
}
m_shift_counter = 0x0f;
+ LOGINT("SR INT ");
clear_int(INT_SR);
LOGSHIFT(" - ACR is: %02x ", m_acr);
if (SO_O2_CONTROL(m_acr) || SI_O2_CONTROL(m_acr))
@@ -940,6 +956,7 @@ WRITE8_MEMBER( via6522_device::write )
{
data = 0x7f;
}
+ LOGINT("IFR INT ");
clear_int(data);
break;
}
diff --git a/src/devices/machine/68307.cpp b/src/devices/machine/68307.cpp
index 64e52ebb295..adc65ce199b 100644
--- a/src/devices/machine/68307.cpp
+++ b/src/devices/machine/68307.cpp
@@ -44,7 +44,7 @@ ADDRESS_MAP_END
MACHINE_CONFIG_MEMBER( m68307_cpu_device::device_add_mconfig )
- MCFG_MC68681_ADD("internal68681", 16000000/4) // ?? Mhz - should be specified in inline config
+ MCFG_DEVICE_ADD("internal68681", MC68681, 16000000/4) // ?? Mhz - should be specified in inline config
MCFG_MC68681_IRQ_CALLBACK(WRITELINE(m68307_cpu_device, m68307_duart_irq_handler))
MCFG_MC68681_A_TX_CALLBACK(WRITELINE(m68307_cpu_device, m68307_duart_txa))
MCFG_MC68681_B_TX_CALLBACK(WRITELINE(m68307_cpu_device, m68307_duart_txb))
@@ -158,7 +158,7 @@ void m68307_cpu_device::write_dword_m68307(offs_t address, uint32_t data)
void m68307_cpu_device::init16_m68307(address_space &space)
{
m_space = &space;
- m_direct = &space.direct();
+ m_direct = space.direct<0>();
opcode_xor = 0;
readimm16 = m68k_readimm16_delegate(&m68307_cpu_device::simple_read_immediate_16_m68307, this);
diff --git a/src/devices/machine/68307.h b/src/devices/machine/68307.h
index 3ed993ea3f7..a64c9985ce9 100644
--- a/src/devices/machine/68307.h
+++ b/src/devices/machine/68307.h
@@ -77,9 +77,6 @@ protected:
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
- virtual uint32_t disasm_min_opcode_bytes() const override { return 2; }
- virtual uint32_t disasm_max_opcode_bytes() const override { return 10; }
-
virtual uint32_t execute_min_cycles() const override { return 4; }
virtual uint32_t execute_max_cycles() const override { return 158; }
diff --git a/src/devices/machine/68561mpcc.cpp b/src/devices/machine/68561mpcc.cpp
index 3f500827cdf..8b93885ffc2 100644
--- a/src/devices/machine/68561mpcc.cpp
+++ b/src/devices/machine/68561mpcc.cpp
@@ -587,7 +587,7 @@ void mpcc_device::tra_complete()
}
//-------------------------------------------------
-// rcv_callback - called when it is time to sample incomming data bit
+// rcv_callback - called when it is time to sample incoming data bit
//-------------------------------------------------
void mpcc_device::rcv_callback()
{
diff --git a/src/devices/machine/74123.cpp b/src/devices/machine/74123.cpp
index 5efb34ec085..6596bf58bf3 100644
--- a/src/devices/machine/74123.cpp
+++ b/src/devices/machine/74123.cpp
@@ -121,7 +121,7 @@ int ttl74123_device::timer_running()
TIMER_CALLBACK_MEMBER( ttl74123_device::output_callback )
{
- m_output_changed_cb((offs_t)0, param);
+ m_output_changed_cb(param);
}
@@ -147,7 +147,7 @@ TIMER_CALLBACK_MEMBER( ttl74123_device::clear_callback )
{
int output = timer_running();
- m_output_changed_cb((offs_t)0, output);
+ m_output_changed_cb(output);
}
//-------------------------------------------------
@@ -190,15 +190,15 @@ void ttl74123_device::start_pulse()
// a_w - write register a data
//-------------------------------------------------
-WRITE8_MEMBER( ttl74123_device::a_w )
+WRITE_LINE_MEMBER( ttl74123_device::a_w )
{
/* start/regtrigger pulse if B=HI and falling edge on A (while clear is HI) */
- if (!data && m_a && m_b && m_clear)
+ if (!state && m_a && m_b && m_clear)
{
start_pulse();
}
- m_a = data;
+ m_a = state;
}
@@ -206,15 +206,15 @@ WRITE8_MEMBER( ttl74123_device::a_w )
// b_w - write register b data
//-------------------------------------------------
-WRITE8_MEMBER( ttl74123_device::b_w)
+WRITE_LINE_MEMBER( ttl74123_device::b_w)
{
/* start/regtrigger pulse if A=LO and rising edge on B (while clear is HI) */
- if (data && !m_b && !m_a && m_clear)
+ if (state && !m_b && !m_a && m_clear)
{
start_pulse();
}
- m_b = data;
+ m_b = state;
}
@@ -222,20 +222,20 @@ WRITE8_MEMBER( ttl74123_device::b_w)
// clear_w - write register clear data
//-------------------------------------------------
-WRITE8_MEMBER( ttl74123_device::clear_w)
+WRITE_LINE_MEMBER( ttl74123_device::clear_w)
{
/* start/regtrigger pulse if B=HI and A=LO and rising edge on clear */
- if (data && !m_a && m_b && !m_clear)
+ if (state && !m_a && m_b && !m_clear)
{
start_pulse();
}
- else if (!data) /* clear the output */
+ else if (!state) /* clear the output */
{
m_timer->adjust(attotime::zero);
LOG("74123: Cleared\n");
}
- m_clear = data;
+ m_clear = state;
}
@@ -243,7 +243,7 @@ WRITE8_MEMBER( ttl74123_device::clear_w)
// reset_w - reset device
//-------------------------------------------------
-WRITE8_MEMBER( ttl74123_device::reset_w)
+WRITE_LINE_MEMBER( ttl74123_device::reset_w)
{
set_output();
}
diff --git a/src/devices/machine/74123.h b/src/devices/machine/74123.h
index 0da73384cef..1316249070b 100644
--- a/src/devices/machine/74123.h
+++ b/src/devices/machine/74123.h
@@ -105,10 +105,10 @@ public:
static void set_clear_pin_value(device_t &device, int value) { downcast<ttl74123_device &>(device).m_clear = value; }
template <class Object> static devcb_base &set_output_changed_callback(device_t &device, Object &&cb) { return downcast<ttl74123_device &>(device).m_output_changed_cb.set_callback(std::forward<Object>(cb)); }
- DECLARE_WRITE8_MEMBER(a_w);
- DECLARE_WRITE8_MEMBER(b_w);
- DECLARE_WRITE8_MEMBER(clear_w);
- DECLARE_WRITE8_MEMBER(reset_w);
+ DECLARE_WRITE_LINE_MEMBER(a_w);
+ DECLARE_WRITE_LINE_MEMBER(b_w);
+ DECLARE_WRITE_LINE_MEMBER(clear_w);
+ DECLARE_WRITE_LINE_MEMBER(reset_w);
protected:
// device-level overrides
@@ -133,7 +133,7 @@ private:
int m_a; /* initial/constant value of the A pin */
int m_b; /* initial/constant value of the B pin */
int m_clear; /* initial/constant value of the Clear pin */
- devcb_write8 m_output_changed_cb;
+ devcb_write_line m_output_changed_cb;
};
diff --git a/src/devices/machine/74259.cpp b/src/devices/machine/74259.cpp
index da6a7cfa99d..fdd0354e622 100644
--- a/src/devices/machine/74259.cpp
+++ b/src/devices/machine/74259.cpp
@@ -313,17 +313,28 @@ WRITE8_MEMBER(addressable_latch_device::write_a3)
}
//-------------------------------------------------
-// write_nibble - write handler using LSB of
+// write_nibble_d0 - write handler using LSB of
// data as input and next three bits as address
// (offset is ignored)
//-------------------------------------------------
-WRITE8_MEMBER(addressable_latch_device::write_nibble)
+WRITE8_MEMBER(addressable_latch_device::write_nibble_d0)
{
write_bit((data & 0x0e) >> 1, data & 0x01);
}
//-------------------------------------------------
+// write_nibble_d3 - write handler using bit 3 of
+// data as input and lowest three bits as address
+// (offset is ignored)
+//-------------------------------------------------
+
+WRITE8_MEMBER(addressable_latch_device::write_nibble_d3)
+{
+ write_bit(data & 0x07, BIT(data, 3));
+}
+
+//-------------------------------------------------
// clear - pulse clear line from bus write
//-------------------------------------------------
diff --git a/src/devices/machine/74259.h b/src/devices/machine/74259.h
index e4496240687..0805537d06b 100644
--- a/src/devices/machine/74259.h
+++ b/src/devices/machine/74259.h
@@ -77,7 +77,8 @@ public:
DECLARE_WRITE8_MEMBER(write_d7);
DECLARE_WRITE8_MEMBER(write_a0);
DECLARE_WRITE8_MEMBER(write_a3);
- DECLARE_WRITE8_MEMBER(write_nibble);
+ DECLARE_WRITE8_MEMBER(write_nibble_d0);
+ DECLARE_WRITE8_MEMBER(write_nibble_d3);
DECLARE_WRITE8_MEMBER(clear);
// read handlers (inlined for the sake of optimization)
diff --git a/src/devices/machine/adc083x.cpp b/src/devices/machine/adc083x.cpp
index 317e2dbb18a..26323a1d146 100644
--- a/src/devices/machine/adc083x.cpp
+++ b/src/devices/machine/adc083x.cpp
@@ -46,10 +46,10 @@ enum
TYPE DEFINITIONS
***************************************************************************/
-DEFINE_DEVICE_TYPE(ADC0831, adc0831_device, "adc0831", "ADC0831")
-DEFINE_DEVICE_TYPE(ADC0832, adc0832_device, "adc0832", "ADC0832")
-DEFINE_DEVICE_TYPE(ADC0834, adc0834_device, "adc0834", "ADC0834")
-DEFINE_DEVICE_TYPE(ADC0838, adc0838_device, "adc0838", "ADC0838")
+DEFINE_DEVICE_TYPE(ADC0831, adc0831_device, "adc0831", "ADC0831 A/D Converter")
+DEFINE_DEVICE_TYPE(ADC0832, adc0832_device, "adc0832", "ADC0832 A/D Converter")
+DEFINE_DEVICE_TYPE(ADC0834, adc0834_device, "adc0834", "ADC0834 A/D Converter")
+DEFINE_DEVICE_TYPE(ADC0838, adc0838_device, "adc0838", "ADC0838 A/D Converter")
adc083x_device::adc083x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t mux_bits)
: device_t(mconfig, type, tag, owner, clock),
diff --git a/src/devices/machine/adc0844.cpp b/src/devices/machine/adc0844.cpp
new file mode 100644
index 00000000000..103702a8d14
--- /dev/null
+++ b/src/devices/machine/adc0844.cpp
@@ -0,0 +1,208 @@
+// license: BSD-3-Clause
+// copyright-holders: Dirk Best
+/***************************************************************************
+
+ ADC0844/ADC0848
+
+ A/D Converter With Multiplexer Options
+
+***************************************************************************/
+
+#include "emu.h"
+#include "adc0844.h"
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+DEFINE_DEVICE_TYPE(ADC0844, adc0844_device, "adc0844", "ADC0844 A/D Converter")
+DEFINE_DEVICE_TYPE(ADC0848, adc0848_device, "adc0848", "ADC0848 A/D Converter")
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// adc0844_device - constructor
+//-------------------------------------------------
+
+adc0844_device::adc0844_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, type, tag, owner, clock),
+ m_intr_cb(*this),
+ m_ch1_cb(*this), m_ch2_cb(*this), m_ch3_cb(*this), m_ch4_cb(*this),
+ m_conversion_timer(nullptr),
+ m_channel(0x0f),
+ m_result(0xff)
+{
+}
+
+adc0844_device::adc0844_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ adc0844_device(mconfig, ADC0844, tag, owner, clock)
+{
+}
+
+//-------------------------------------------------
+// adc0848_device - constructor
+//-------------------------------------------------
+
+adc0848_device::adc0848_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ adc0844_device(mconfig, ADC0848, tag, owner, clock),
+ m_ch5_cb(*this), m_ch6_cb(*this), m_ch7_cb(*this), m_ch8_cb(*this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void adc0844_device::device_start()
+{
+ // resolve callbacks
+ m_intr_cb.resolve_safe();
+ m_ch1_cb.resolve_safe(0xff);
+ m_ch2_cb.resolve_safe(0xff);
+ m_ch3_cb.resolve_safe(0xff);
+ m_ch4_cb.resolve_safe(0xff);
+
+ // allocate timers
+ m_conversion_timer = timer_alloc();
+
+ // register for save states
+ save_item(NAME(m_channel));
+ save_item(NAME(m_result));
+}
+
+void adc0848_device::device_start()
+{
+ adc0844_device::device_start();
+
+ // resolve callbacks
+ m_ch5_cb.resolve_safe(0xff);
+ m_ch6_cb.resolve_safe(0xff);
+ m_ch7_cb.resolve_safe(0xff);
+ m_ch8_cb.resolve_safe(0xff);
+}
+
+//-------------------------------------------------
+// clamp - restrict value to 0..255
+//-------------------------------------------------
+
+uint8_t adc0844_device::clamp(int value)
+{
+ if (value > 0xff)
+ return 0xff;
+ else if (value < 0)
+ return 0x00;
+ else
+ return value;
+}
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void adc0844_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (m_channel)
+ {
+ // differential
+ case 0x00:
+ case 0x08: m_result = clamp(0xff - (m_ch2_cb(0) - m_ch1_cb(0))); break;
+ case 0x01:
+ case 0x09: m_result = clamp(0xff - (m_ch1_cb(0) - m_ch2_cb(0))); break;
+ case 0x02:
+ case 0x0a: m_result = clamp(0xff - (m_ch4_cb(0) - m_ch3_cb(0))); break;
+ case 0x03:
+ case 0x0b: m_result = clamp(0xff - (m_ch3_cb(0) - m_ch4_cb(0))); break;
+ // single-ended
+ case 0x04: m_result = m_ch1_cb(0); break;
+ case 0x05: m_result = m_ch2_cb(0); break;
+ case 0x06: m_result = m_ch3_cb(0); break;
+ case 0x07: m_result = m_ch4_cb(0); break;
+ // pseudo-differential
+ case 0x0c: m_result = clamp(0xff - (m_ch4_cb(0) - m_ch1_cb(0))); break;
+ case 0x0d: m_result = clamp(0xff - (m_ch4_cb(0) - m_ch2_cb(0))); break;
+ case 0x0e: m_result = clamp(0xff - (m_ch4_cb(0) - m_ch3_cb(0))); break;
+ // undefined
+ case 0x0f: m_result = 0x00; break;
+ }
+
+ m_intr_cb(ASSERT_LINE);
+}
+
+void adc0848_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (m_channel)
+ {
+ // differential
+ case 0x00:
+ case 0x10: m_result = clamp(0xff - (m_ch2_cb(0) - m_ch1_cb(0))); break;
+ case 0x01:
+ case 0x11: m_result = clamp(0xff - (m_ch1_cb(0) - m_ch2_cb(0))); break;
+ case 0x02:
+ case 0x12: m_result = clamp(0xff - (m_ch4_cb(0) - m_ch3_cb(0))); break;
+ case 0x03:
+ case 0x13: m_result = clamp(0xff - (m_ch3_cb(0) - m_ch4_cb(0))); break;
+ case 0x04:
+ case 0x14: m_result = clamp(0xff - (m_ch6_cb(0) - m_ch5_cb(0))); break;
+ case 0x05:
+ case 0x15: m_result = clamp(0xff - (m_ch5_cb(0) - m_ch6_cb(0))); break;
+ case 0x06:
+ case 0x16: m_result = clamp(0xff - (m_ch8_cb(0) - m_ch7_cb(0))); break;
+ case 0x07:
+ case 0x17: m_result = clamp(0xff - (m_ch7_cb(0) - m_ch8_cb(0))); break;
+ // single-ended
+ case 0x08: m_result = m_ch1_cb(0); break;
+ case 0x09: m_result = m_ch2_cb(0); break;
+ case 0x0a: m_result = m_ch3_cb(0); break;
+ case 0x0b: m_result = m_ch4_cb(0); break;
+ case 0x0c: m_result = m_ch5_cb(0); break;
+ case 0x0d: m_result = m_ch6_cb(0); break;
+ case 0x0e: m_result = m_ch7_cb(0); break;
+ case 0x0f: m_result = m_ch8_cb(0); break;
+ // pseudo-differential
+ case 0x18: m_result = clamp(0xff - (m_ch8_cb(0) - m_ch1_cb(0))); break;
+ case 0x19: m_result = clamp(0xff - (m_ch8_cb(0) - m_ch2_cb(0))); break;
+ case 0x1a: m_result = clamp(0xff - (m_ch8_cb(0) - m_ch3_cb(0))); break;
+ case 0x1b: m_result = clamp(0xff - (m_ch8_cb(0) - m_ch4_cb(0))); break;
+ case 0x1c: m_result = clamp(0xff - (m_ch8_cb(0) - m_ch5_cb(0))); break;
+ case 0x1d: m_result = clamp(0xff - (m_ch8_cb(0) - m_ch6_cb(0))); break;
+ case 0x1e: m_result = clamp(0xff - (m_ch8_cb(0) - m_ch7_cb(0))); break;
+ // undefined
+ case 0x1f: m_result = 0x00; break;
+ }
+
+ m_intr_cb(ASSERT_LINE);
+}
+
+
+//**************************************************************************
+// INTERFACE
+//**************************************************************************
+
+READ8_MEMBER( adc0844_device::read )
+{
+ m_intr_cb(CLEAR_LINE);
+
+ return m_result;
+}
+
+WRITE8_MEMBER( adc0844_device::write )
+{
+ m_intr_cb(CLEAR_LINE);
+
+ // set channel and start conversion
+ m_channel = data & 0x0f;
+ m_conversion_timer->adjust(attotime::from_usec(40));
+}
+
+WRITE8_MEMBER( adc0848_device::write )
+{
+ m_intr_cb(CLEAR_LINE);
+
+ // set channel and start conversion
+ m_channel = data & 0x1f;
+ m_conversion_timer->adjust(attotime::from_usec(40));
+}
diff --git a/src/devices/machine/adc0844.h b/src/devices/machine/adc0844.h
new file mode 100644
index 00000000000..404a4cfa7a4
--- /dev/null
+++ b/src/devices/machine/adc0844.h
@@ -0,0 +1,156 @@
+// license: BSD-3-Clause
+// copyright-holders: Dirk Best
+/***************************************************************************
+
+ ADC0844
+
+ A/D Converter With Multiplexer Options
+
+ ___ ___
+ /RD 1 |* u | 20 VCC
+ /CS 2 | | 19 /WR
+ CH1 3 | | 18 /INTR
+ CH2 4 | | 17 DB0/MA0
+ CH3 5 | | 16 DB1/MA1
+ CH4 6 | | 15 DB2/MA2
+ AGND 7 | | 14 DB3/MA3
+ VREF 8 | | 13 DB4
+ DB7 9 | | 12 DB5
+ DGND 10 |_______| 11 DB6
+
+***************************************************************************/
+
+#ifndef MAME_DEVICES_MACHINE_ADC0844_H
+#define MAME_DEVICES_MACHINE_ADC0844_H
+
+#pragma once
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_ADC0844_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, ADC0844, 0)
+
+#define MCFG_ADC0844_INTR_CB(_devcb) \
+ devcb = &adc0844_device::set_intr_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ADC0844_CH1_CB(_devcb) \
+ devcb = &adc0844_device::set_ch1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ADC0844_CH2_CB(_devcb) \
+ devcb = &adc0844_device::set_ch2_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ADC0844_CH3_CB(_devcb) \
+ devcb = &adc0844_device::set_ch3_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ADC0844_CH4_CB(_devcb) \
+ devcb = &adc0844_device::set_ch4_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ADC0848_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, ADC0848, 0)
+
+#define MCFG_ADC0848_INTR_CB MCFG_ADC0844_INTR_CB
+#define MCFG_ADC0848_CH1_CB MCFG_ADC0844_CH1_CB
+#define MCFG_ADC0848_CH2_CB MCFG_ADC0844_CH2_CB
+#define MCFG_ADC0848_CH3_CB MCFG_ADC0844_CH3_CB
+#define MCFG_ADC0848_CH4_CB MCFG_ADC0844_CH4_CB
+
+#define MCFG_ADC0848_CH5_CB(_devcb) \
+ devcb = &adc0848_device::set_ch5_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ADC0848_CH6_CB(_devcb) \
+ devcb = &adc0848_device::set_ch6_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ADC0848_CH7_CB(_devcb) \
+ devcb = &adc0848_device::set_ch7_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ADC0848_CH8_CB(_devcb) \
+ devcb = &adc0848_device::set_ch8_callback(*device, DEVCB_##_devcb);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+class adc0844_device : public device_t
+{
+public:
+ // construction/destruction
+ adc0844_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ // configuration
+ template <class Object> static devcb_base &set_intr_callback(device_t &device, Object &&cb)
+ { return downcast<adc0844_device &>(device).m_intr_cb.set_callback(std::forward<Object>(cb)); }
+
+ template <class Object> static devcb_base &set_ch1_callback(device_t &device, Object &&cb)
+ { return downcast<adc0844_device &>(device).m_ch1_cb.set_callback(std::forward<Object>(cb)); }
+
+ template <class Object> static devcb_base &set_ch2_callback(device_t &device, Object &&cb)
+ { return downcast<adc0844_device &>(device).m_ch2_cb.set_callback(std::forward<Object>(cb)); }
+
+ template <class Object> static devcb_base &set_ch3_callback(device_t &device, Object &&cb)
+ { return downcast<adc0844_device &>(device).m_ch3_cb.set_callback(std::forward<Object>(cb)); }
+
+ template <class Object> static devcb_base &set_ch4_callback(device_t &device, Object &&cb)
+ { return downcast<adc0844_device &>(device).m_ch4_cb.set_callback(std::forward<Object>(cb)); }
+
+ DECLARE_READ8_MEMBER(read);
+ virtual DECLARE_WRITE8_MEMBER(write);
+
+protected:
+ adc0844_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+ uint8_t clamp(int value);
+
+ // callbacks
+ devcb_write_line m_intr_cb;
+ devcb_read8 m_ch1_cb, m_ch2_cb, m_ch3_cb, m_ch4_cb;
+
+ emu_timer *m_conversion_timer;
+
+ // state
+ int m_channel;
+ uint8_t m_result;
+};
+
+class adc0848_device : public adc0844_device
+{
+public:
+ // construction/destruction
+ adc0848_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ // configuration
+ template <class Object> static devcb_base &set_ch5_callback(device_t &device, Object &&cb)
+ { return downcast<adc0848_device &>(device).m_ch5_cb.set_callback(std::forward<Object>(cb)); }
+
+ template <class Object> static devcb_base &set_ch6_callback(device_t &device, Object &&cb)
+ { return downcast<adc0848_device &>(device).m_ch6_cb.set_callback(std::forward<Object>(cb)); }
+
+ template <class Object> static devcb_base &set_ch7_callback(device_t &device, Object &&cb)
+ { return downcast<adc0848_device &>(device).m_ch7_cb.set_callback(std::forward<Object>(cb)); }
+
+ template <class Object> static devcb_base &set_ch8_callback(device_t &device, Object &&cb)
+ { return downcast<adc0848_device &>(device).m_ch8_cb.set_callback(std::forward<Object>(cb)); }
+
+ virtual DECLARE_WRITE8_MEMBER(write) override;
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+private:
+ devcb_read8 m_ch5_cb, m_ch6_cb, m_ch7_cb, m_ch8_cb;
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(ADC0844, adc0844_device)
+DECLARE_DEVICE_TYPE(ADC0848, adc0848_device)
+
+#endif // MAME_DEVICES_MACHINE_ADC0844_H
diff --git a/src/devices/machine/am9513.cpp b/src/devices/machine/am9513.cpp
index 2ce1241e086..60f76e691c8 100644
--- a/src/devices/machine/am9513.cpp
+++ b/src/devices/machine/am9513.cpp
@@ -100,13 +100,17 @@ void am9513_device::device_start()
std::fill(std::begin(m_counter_armed), std::end(m_counter_armed), false);
std::fill(std::begin(m_counter_running), std::end(m_counter_running), false);
std::fill(std::begin(m_alternate_count), std::end(m_alternate_count), false);
- std::fill(std::begin(m_src), std::end(m_src), true);
- std::fill(std::begin(m_gate), std::end(m_gate), true);
- std::fill(std::begin(m_gate_alt), std::end(m_gate_alt), true);
- std::fill(std::begin(m_gate_active), std::end(m_gate_active), true);
std::fill(std::begin(m_tc), std::end(m_tc), false);
std::fill(std::begin(m_toggle), std::end(m_toggle), false);
+ // Unused SRC and GATE inputs are typically grounded
+ std::fill(std::begin(m_src), std::end(m_src), false);
+ std::fill(std::begin(m_gate), std::end(m_gate), false);
+ std::fill(std::begin(m_gate_active), std::end(m_gate_active), false);
+
+ // Alternate gate inputs should be tied high if not used
+ std::fill(std::begin(m_gate_alt), std::end(m_gate_alt), true);
+
// Set up frequency timers
for (int f = 0; f < 5; f++)
{
diff --git a/src/devices/machine/atmel_arm_aic.cpp b/src/devices/machine/atmel_arm_aic.cpp
new file mode 100644
index 00000000000..27c2dcbb826
--- /dev/null
+++ b/src/devices/machine/atmel_arm_aic.cpp
@@ -0,0 +1,93 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+/*
+ ARM AIC (Advanced Interrupt Controller) from Atmel
+ typically integrated into the AM91SAM series of chips
+
+ see http://sam7-ex256.narod.ru/include/HTML/AT91SAM7X256_AIC.html
+ current implementation only handles basics needed for pgm2 (pgm2.cpp)
+ if this peripheral is not available as a standalone chip it could also be moved to
+ the CPU folder alongside the ARM instead
+*/
+
+#include "emu.h"
+#include "atmel_arm_aic.h"
+
+DEFINE_DEVICE_TYPE(ARM_AIC, arm_aic_device, "arm_aic", "ARM Advanced Interrupt Controller")
+
+
+DEVICE_ADDRESS_MAP_START( regs_map, 32, arm_aic_device )
+ AM_RANGE(0x000, 0x07f) AM_READWRITE(aic_smr_r, aic_smr_w) // AIC_SMR[32] (AIC_SMR) Source Mode Register
+ AM_RANGE(0x080, 0x0ff) AM_READWRITE(aic_svr_r, aic_svr_w) // AIC_SVR[32] (AIC_SVR) Source Vector Register
+ AM_RANGE(0x100, 0x103) AM_READ(irq_vector_r) // AIC_IVR IRQ Vector Register
+ AM_RANGE(0x104, 0x107) AM_READ(firq_vector_r) // AIC_FVR FIQ Vector Register
+// 0x108 AIC_ISR Interrupt Status Register
+// 0x10C AIC_IPR Interrupt Pending Register
+// 0x110 AIC_IMR Interrupt Mask Register
+// 0x114 AIC_CISR Core Interrupt Status Register
+ AM_RANGE(0x120, 0x123) AM_WRITE(aic_iecr_w) // 0x120 AIC_IECR Interrupt Enable Command Register
+ AM_RANGE(0x124, 0x127) AM_WRITE(aic_idcr_w) // 0x124 AIC_IDCR Interrupt Disable Command Register
+ AM_RANGE(0x128, 0x12b) AM_WRITE(aic_iccr_w) // 0x128 AIC_ICCR Interrupt Clear Command Register
+// 0x12C AIC_ISCR Interrupt Set Command Register
+ AM_RANGE(0x130, 0x133) AM_WRITE(aic_eoicr_w) // 0x130 AIC_EOICR End of Interrupt Command Register
+// 0x134 AIC_SPU Spurious Vector Register
+// 0x138 AIC_DCR Debug Control Register (Protect)
+// 0x140 AIC_FFER Fast Forcing Enable Register
+// 0x144 AIC_FFDR Fast Forcing Disable Register
+// 0x148 AIC_FFSR Fast Forcing Status Register
+ADDRESS_MAP_END
+
+READ32_MEMBER(arm_aic_device::irq_vector_r)
+{
+ return m_current_irq_vector;
+}
+
+READ32_MEMBER(arm_aic_device::firq_vector_r)
+{
+ return m_current_firq_vector;
+}
+
+void arm_aic_device::device_start()
+{
+ m_irq_out.resolve_safe();
+
+ save_item(NAME(m_irqs_enabled));
+ save_item(NAME(m_current_irq_vector));
+ save_item(NAME(m_current_firq_vector));
+
+ save_item(NAME(m_aic_smr));
+ save_item(NAME(m_aic_svr));
+}
+
+void arm_aic_device::device_reset()
+{
+ m_irqs_enabled = 0;
+ m_current_irq_vector = 0;
+ m_current_firq_vector = 0;
+
+ for(auto & elem : m_aic_smr) { elem = 0; }
+ for(auto & elem : m_aic_svr) { elem = 0; }
+}
+
+void arm_aic_device::set_irq(int identity)
+{
+ for (int i = 0;i < 32;i++)
+ {
+ if (m_aic_smr[i] == identity)
+ {
+ if ((m_irqs_enabled >> i) & 1)
+ {
+ m_current_irq_vector = m_aic_svr[i];
+ m_irq_out(ASSERT_LINE);
+ return;
+ }
+ }
+ }
+}
+
+WRITE32_MEMBER(arm_aic_device::aic_iccr_w)
+{
+ //logerror("%s: aic_iccr_w %08x (Interrupt Clear Command Register)\n", machine().describe_context().c_str(), data);
+ m_irq_out(CLEAR_LINE);
+};
diff --git a/src/devices/machine/atmel_arm_aic.h b/src/devices/machine/atmel_arm_aic.h
new file mode 100644
index 00000000000..101c57cc672
--- /dev/null
+++ b/src/devices/machine/atmel_arm_aic.h
@@ -0,0 +1,63 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+#ifndef MAME_MACHINE_ATMEL_ARM_AIC_H
+#define MAME_MACHINE_ATMEL_ARM_AIC_H
+
+#pragma once
+
+DECLARE_DEVICE_TYPE(ARM_AIC, arm_aic_device)
+
+#define MCFG_ARM_AIC_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, ARM_AIC, 0)
+
+#define MCFG_IRQ_LINE_CB(_devcb) \
+ devcb = &arm_aic_device::set_line_callback(*device, DEVCB_##_devcb);
+
+class arm_aic_device : public device_t
+{
+public:
+ // construction/destruction
+ arm_aic_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, ARM_AIC, tag, owner, clock),
+ m_irq_out(*this)
+ {
+ }
+
+ // configuration
+ template <class Object> static devcb_base &set_line_callback(device_t &device, Object &&cb) { return downcast<arm_aic_device &>(device).m_irq_out.set_callback(std::forward<Object>(cb)); }
+
+ DECLARE_ADDRESS_MAP(regs_map, 32);
+
+ DECLARE_READ32_MEMBER(irq_vector_r);
+ DECLARE_READ32_MEMBER(firq_vector_r);
+
+ // can't use AM_RAM and AM_SHARE in device submaps
+ DECLARE_READ32_MEMBER(aic_smr_r) { return m_aic_smr[offset]; };
+ DECLARE_READ32_MEMBER(aic_svr_r) { return m_aic_svr[offset]; };
+ DECLARE_WRITE32_MEMBER(aic_smr_w) { COMBINE_DATA(&m_aic_smr[offset]); };
+ DECLARE_WRITE32_MEMBER(aic_svr_w) { COMBINE_DATA(&m_aic_svr[offset]); };
+
+ DECLARE_WRITE32_MEMBER(aic_iecr_w) { /*logerror("%s: aic_iecr_w %08x (Interrupt Enable Command Register)\n", machine().describe_context().c_str(), data);*/ COMBINE_DATA(&m_irqs_enabled); };
+ DECLARE_WRITE32_MEMBER(aic_idcr_w) { /*logerror("%s: aic_idcr_w %08x (Interrupt Disable Command Register)\n", machine().describe_context().c_str(), data);*/ };
+ DECLARE_WRITE32_MEMBER(aic_iccr_w);
+ DECLARE_WRITE32_MEMBER(aic_eoicr_w){ /*logerror("%s: aic_eoicr_w (End of Interrupt Command Register)\n", machine().describe_context().c_str());*/ }; // value doesn't matter
+
+ void set_irq(int identity);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+private:
+ uint32_t m_irqs_enabled;
+ uint32_t m_current_irq_vector;
+ uint32_t m_current_firq_vector;
+
+ uint32_t m_aic_smr[32];
+ uint32_t m_aic_svr[32];
+
+ devcb_write_line m_irq_out;
+};
+
+#endif
diff --git a/src/devices/machine/ay31015.cpp b/src/devices/machine/ay31015.cpp
index aba1e284156..30dd0164e60 100644
--- a/src/devices/machine/ay31015.cpp
+++ b/src/devices/machine/ay31015.cpp
@@ -4,8 +4,13 @@
ay31015.c by Robbbert, May 2008. Bugs fixed by Judge.
- Code for the AY-3-1014A, AY-3-1015(D), AY-5-1013(A), and AY-6-1013 UARTs
- The HD6402 UART is compatible with the AY-3-1015 UART.
+ Code for the General Instruments AY-3-1014A, AY-3-1015(D), AY-5-1013(A),
+ and AY-6-1013 UARTs (Universal Asynchronous Receiver/Transmitters).
+
+ Compatible UARTs were produced by Harris (HD6402), TI (TMS6011),
+ Western Digital (TR1602/TR1402/TR1863/TR1865), AMI (S1883), Signetics
+ (2536), National (MM5303), Standard Microsystems (COM2502/COM2017),
+ Tesla (MHB1012) and other companies.
This is cycle-accurate according to the specifications.
@@ -94,8 +99,8 @@ Start bit (low), Bit 0, Bit 1... highest bit, Parity bit (if enabled), 1-2 stop
-DEFINE_DEVICE_TYPE(AY31015, ay31015_device, "ay31015", "AY-3-1015")
-DEFINE_DEVICE_TYPE(AY51013, ay51013_device, "ay51013", "AY-5-1013")
+DEFINE_DEVICE_TYPE(AY31015, ay31015_device, "ay31015", "AY-3-1015 UART")
+DEFINE_DEVICE_TYPE(AY51013, ay51013_device, "ay51013", "AY-5-1013 UART")
ay31015_device::ay31015_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock),
diff --git a/src/devices/machine/cdp1852.h b/src/devices/machine/cdp1852.h
index d017aaa2482..ba63cd2efb8 100644
--- a/src/devices/machine/cdp1852.h
+++ b/src/devices/machine/cdp1852.h
@@ -67,6 +67,8 @@ public:
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
+ uint8_t do_r() { return m_data; }
+
protected:
// device-level overrides
virtual void device_start() override;
diff --git a/src/devices/machine/com8116.cpp b/src/devices/machine/com8116.cpp
index cbc10280125..bbc8d02119d 100644
--- a/src/devices/machine/com8116.cpp
+++ b/src/devices/machine/com8116.cpp
@@ -26,6 +26,7 @@ DEFINE_DEVICE_TYPE(COM8116, com8116_device, "com8116", "COM8116 Dual BRG")
// SMC/COM5016(T) with no dash, aka 'STD' part on the datasheet
// Also COM8116(T)/8126(T)/8136(T)/8146(T) and Synertek sy2661-3 and GI AY-5-8116(T)-000 and GI AY-5-8136(T)-000 and WD WD-1943-00 (aka WD8136-00)
+// and Motorola K1135A/B (K1135B outputs undivided 5.0688 MHz on pin 1)
// SMC/COM8156(T) is the same chip but clocked twice as fast and 32x clocks per baud instead of 16x
// baud rates are 50, 75, 110, 134.5, 150, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200, 9600, 19200
const int com8116_device::divisors_16X_5_0688MHz[] =
@@ -39,6 +40,7 @@ const int com8116_device::divisors_16X_6_01835MHz[] =
// SMC/COM5016(T)-5 and WD WD-1943-05; Synertek SY2661-1 and 2 are NOT the same as this despite using same clock speed, see below
// SMC/COM8156(T)-5 is the same chip but clocked twice as fast and 32x clocks per baud instead of 16x
+// Motorola K1135C is similar, with undivided 4.9152 MHz on pin 1
// baud rates are 50, 75, 110, 134.5, 150, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200, 9600, 19200
const int com8116_device::divisors_16X_4_9152MHz[] =
{ 6144, 4096, 2793, 2284, 2048, 1024, 512, 256, 171, 154, 128, 85, 64, 43, 32, 16 };
diff --git a/src/devices/machine/ds1204.cpp b/src/devices/machine/ds1204.cpp
index 17ff81b8c0b..cd722bc8929 100644
--- a/src/devices/machine/ds1204.cpp
+++ b/src/devices/machine/ds1204.cpp
@@ -29,7 +29,7 @@ inline void ATTR_PRINTF( 3, 4 ) ds1204_device::verboselog( int n_level, const ch
}
// device type definition
-DEFINE_DEVICE_TYPE(DS1204, ds1204_device, "ds1204", "DS1204")
+DEFINE_DEVICE_TYPE(DS1204, ds1204_device, "ds1204", "DS1204 Electronic Key")
ds1204_device::ds1204_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock )
: device_t(mconfig, DS1204, tag, owner, clock),
diff --git a/src/devices/machine/ds128x.cpp b/src/devices/machine/ds128x.cpp
index 7c824aba638..7a397aab303 100644
--- a/src/devices/machine/ds128x.cpp
+++ b/src/devices/machine/ds128x.cpp
@@ -3,8 +3,6 @@
#include "emu.h"
#include "ds128x.h"
-/// TODO: Only DV2/DV1/DV0 == 0/1/0 is supported as the chip only has a 15 stage divider and not 22.
-
DEFINE_DEVICE_TYPE(DS12885, ds12885_device, "ds12885", "DS12885 RTC/NVRAM")
//-------------------------------------------------
@@ -15,3 +13,13 @@ ds12885_device::ds12885_device(const machine_config &mconfig, const char *tag, d
: mc146818_device(mconfig, DS12885, tag, owner, clock)
{
}
+
+int ds12885_device::get_timer_bypass()
+{
+ if( !( m_data[REG_A] & REG_A_DV0 ) ) //DV0 must be 0 for timekeeping
+ {
+ return 7; // Fixed at 1 Hz with clock at 32768Hz
+ }
+
+ return 22; // No tick
+}
diff --git a/src/devices/machine/ds128x.h b/src/devices/machine/ds128x.h
index e74227e58b1..e525d7a79d3 100644
--- a/src/devices/machine/ds128x.h
+++ b/src/devices/machine/ds128x.h
@@ -18,6 +18,7 @@ public:
protected:
virtual int data_size() override { return 128; }
+ virtual int get_timer_bypass() override;
};
// device type definition
diff --git a/src/devices/machine/ds1315.cpp b/src/devices/machine/ds1315.cpp
index 7be26b1a19e..6b37957f9eb 100644
--- a/src/devices/machine/ds1315.cpp
+++ b/src/devices/machine/ds1315.cpp
@@ -1,12 +1,16 @@
// license:BSD-3-Clause
-// copyright-holders:Tim Lindner
+// copyright-holders:Tim Lindner, R. Belmont
/*****************************************************************************************
- ds1315.c
+ ds1315.cpp
Dallas Semiconductor's Phantom Time Chip DS1315.
NOTE: writes are decoded, but the host's time will always be returned when asked.
+ November 2017: R. Belmont added capability to emulate DS1216 and other DS121x
+ parts where the clock sits in the same place as a ROM. The backing callback
+ returns the ROM contents when the RTC is locked.
+
April 2015: chip enable / chip reset / phantom writes by Karl-Ludwig Deisenhofer
November 2001: implementation by Tim Lindner
@@ -29,7 +33,10 @@
DEFINE_DEVICE_TYPE(DS1315, ds1315_device, "ds1315", "Dallas DS1315 Phantom Time Chip")
ds1315_device::ds1315_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, DS1315, tag, owner, clock), m_mode(), m_count(0)
+ : device_t(mconfig, DS1315, tag, owner, clock),
+ m_backing_read(*this),
+ m_mode(),
+ m_count(0)
{
}
@@ -39,6 +46,8 @@ ds1315_device::ds1315_device(const machine_config &mconfig, const char *tag, dev
void ds1315_device::device_start()
{
+ m_backing_read.resolve_safe(0xff);
+
save_item(NAME(m_count));
save_item(NAME(m_mode));
save_item(NAME(m_raw_data));
@@ -77,6 +86,37 @@ static const uint8_t ds1315_pattern[] =
IMPLEMENTATION
***************************************************************************/
+// automated read, does all the work the real Dallas chip does
+READ8_MEMBER( ds1315_device::read )
+{
+ if (m_mode == DS_SEEK_MATCHING)
+ {
+ if (offset & 1)
+ {
+ read_1(space, 0);
+ }
+ else
+ {
+ read_0(space, 0);
+ }
+
+ if (offset & 4)
+ {
+ m_count = 0;
+ m_mode = DS_SEEK_MATCHING;
+ }
+
+ return m_backing_read(offset);
+ }
+ else if (m_mode == DS_CALENDAR_IO)
+ {
+ return read_data(space, offset);
+ }
+
+ return 0xff; // shouldn't happen, but compilers don't know that
+}
+
+
/*-------------------------------------------------
read_0 (actual data)
-------------------------------------------------*/
@@ -92,7 +132,6 @@ READ8_MEMBER( ds1315_device::read_0 )
m_mode = DS_CALENDAR_IO;
fill_raw_data();
}
-
return 0;
}
@@ -246,7 +285,7 @@ void ds1315_device::input_raw_data()
raw[6] = bcd_2_dec(raw[6]); // month
raw[7] = bcd_2_dec(raw[7]); // year (two digits)
- printf("\nDS1315 RTC INPUT (WILL BE IGNORED) mm/dd/yy hh:mm:ss - %02d/%02d/%02d %02d/%02d/%02d",
+ logerror("\nDS1315 RTC INPUT (WILL BE IGNORED) mm/dd/yy hh:mm:ss - %02d/%02d/%02d %02d/%02d/%02d",
raw[6], raw[5], raw[7], raw[3], raw[2], raw[1]
);
}
diff --git a/src/devices/machine/ds1315.h b/src/devices/machine/ds1315.h
index 0468232cf31..132056e46b3 100644
--- a/src/devices/machine/ds1315.h
+++ b/src/devices/machine/ds1315.h
@@ -15,7 +15,15 @@
#pragma once
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+#define MCFG_DS1315_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, DS1315, 0)
+
+#define MCFG_DS1315_BACKING_HANDLER(_devcb) \
+ devcb = &ds1315_device::set_backing_handler(*device, DEVCB_##_devcb);
/***************************************************************************
MACROS
@@ -27,6 +35,9 @@ public:
ds1315_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
~ds1315_device() {}
+ // this handler automates the bits 0/2 stuff
+ DECLARE_READ8_MEMBER(read);
+
DECLARE_READ8_MEMBER(read_0);
DECLARE_READ8_MEMBER(read_1);
DECLARE_READ8_MEMBER(read_data);
@@ -35,12 +46,17 @@ public:
bool chip_enable();
void chip_reset();
+ template <class Object> static devcb_base &set_backing_handler(device_t &device, Object &&cb)
+ { return downcast<ds1315_device &>(device).m_backing_read.set_callback(std::forward<Object>(cb)); }
+
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
private:
+ devcb_read8 m_backing_read;
+
enum mode_t : u8
{
DS_SEEK_MATCHING,
@@ -61,12 +77,4 @@ ALLOW_SAVE_TYPE(ds1315_device::mode_t);
DECLARE_DEVICE_TYPE(DS1315, ds1315_device)
-/***************************************************************************
- DEVICE CONFIGURATION MACROS
-***************************************************************************/
-
-#define MCFG_DS1315_ADD(_tag) \
- MCFG_DEVICE_ADD(_tag, DS1315, 0)
-
-
#endif // MAME_MACHINE_DS1315_H
diff --git a/src/devices/machine/eeprom.cpp b/src/devices/machine/eeprom.cpp
index 48bed1af960..2ed8e3fa65c 100644
--- a/src/devices/machine/eeprom.cpp
+++ b/src/devices/machine/eeprom.cpp
@@ -258,8 +258,8 @@ void eeprom_base_device::nvram_default()
fatalerror("eeprom region '%s' wrong size (expected size = 0x%X)\n", tag(), eeprom_bytes);
if (m_data_bits == 8 && m_region->bytewidth() != 1)
fatalerror("eeprom region '%s' needs to be an 8-bit region\n", tag());
- if (m_data_bits == 16 && (m_region->bytewidth() != 2 || m_region->endianness() != ENDIANNESS_BIG))
- fatalerror("eeprom region '%s' needs to be a 16-bit big-endian region\n", tag());
+ if (m_data_bits == 16 && m_region->bytewidth() != 2)
+ fatalerror("eeprom region '%s' needs to be a 16-bit region\n", tag());
osd_printf_verbose("Loading data from EEPROM region '%s'\n", tag());
memcpy(&m_data[0], m_region->base(), eeprom_bytes);
@@ -317,8 +317,8 @@ void eeprom_base_device::internal_write(offs_t address, uint32_t data)
{
if (m_data_bits == 16)
{
- m_data[address*2] = data;
- m_data[address*2+1] = data >> 8;
+ m_data[address * 2] = data;
+ m_data[address * 2 + 1] = data >> 8;
} else
m_data[address] = data;
}
diff --git a/src/devices/machine/eepromser.cpp b/src/devices/machine/eepromser.cpp
index 74d51e980f3..5e6e08a6a2b 100644
--- a/src/devices/machine/eepromser.cpp
+++ b/src/devices/machine/eepromser.cpp
@@ -90,6 +90,18 @@
93Cxx has ERASE command; this maps to WRITE on ER5911
93Cxx has WRITEALL command; no equivalent on ER5911
+ The Xicor X2444 NOVRAM (static RAM/EEPROM overlay) has a pin-compatible
+ serial interface, but its commands follow a rather different format:
+
+ Start Address Opcode Command
+ 1 aaaa 011 WRITE data
+ 1 aaaa 11x READ data
+ 1 xxxx 000 WRDS = WRite DiSable
+ 1 xxxx 001 STO = STOre RAM data in EEPROM
+ 1 xxxx 010 SLEEP mode (not in CMOS version)
+ 1 xxxx 100 WREN = WRite ENable
+ 1 xxxx 101 RCL = ReCaLl RAM data from EEPROM
+
****************************************************************************
Issues with:
@@ -148,6 +160,7 @@ eeprom_serial_base_device::eeprom_serial_base_device(const machine_config &mconf
m_command_address_bits(0),
m_streaming_enabled(false),
m_output_on_falling_clock_enabled(false),
+ m_do_cb(*this),
m_state(STATE_IN_RESET),
m_cs_state(CLEAR_LINE),
m_last_cs_rising_edge_time(attotime::zero),
@@ -211,6 +224,9 @@ void eeprom_serial_base_device::device_start()
// start the base class
eeprom_base_device::device_start();
+ // resolve callback
+ m_do_cb.resolve_safe();
+
// save the current state
save_item(NAME(m_state));
save_item(NAME(m_cs_state));
@@ -368,6 +384,9 @@ void eeprom_serial_base_device::set_state(eeprom_state newstate)
// switch to the new state
m_state = newstate;
+
+ // set DO high (actually high impedance; pullup assumed)
+ m_do_cb(1);
}
@@ -443,6 +462,9 @@ void eeprom_serial_base_device::handle_event(eeprom_event event)
m_shift_register = read((m_address + m_bits_accum / m_data_bits) & ((1 << m_address_bits) - 1)) << (32 - m_data_bits);
else
m_shift_register = (m_shift_register << 1) | 1;
+
+ // update DO
+ m_do_cb(BIT(m_shift_register, 31));
}
else if (event == EVENT_CS_FALLING_EDGE)
{
@@ -785,12 +807,8 @@ eeprom_serial_x24c44_device::eeprom_serial_x24c44_device(const machine_config &m
void eeprom_serial_x24c44_device::device_start()
{
- // if no command address bits set, just inherit from the address bits
- if (m_command_address_bits == 0)
- m_command_address_bits = m_address_bits;
-
// start the base class
- eeprom_base_device::device_start();
+ eeprom_serial_base_device::device_start();
int16_t i=0;
m_ram_length=0xf;
@@ -800,18 +818,8 @@ void eeprom_serial_x24c44_device::device_start()
}
m_reading=0;
m_store_latch=0;
+
// save the current state
- save_item(NAME(m_state));
- save_item(NAME(m_cs_state));
- save_item(NAME(m_oe_state));
- save_item(NAME(m_clk_state));
- save_item(NAME(m_di_state));
- save_item(NAME(m_locked));
- save_item(NAME(m_bits_accum));
- save_item(NAME(m_command_address_accum));
- save_item(NAME(m_command));
- save_item(NAME(m_address));
- save_item(NAME(m_shift_register));
save_item(NAME(m_ram_data));
save_item(NAME(m_reading));
save_item(NAME(m_store_latch));
@@ -1012,6 +1020,9 @@ void eeprom_serial_x24c44_device::handle_event(eeprom_event event)
m_shift_register = (m_shift_register << 1) | 1;
}
+
+ // update DO
+ m_do_cb(BIT(m_shift_register, 31));
}
else if (event == EVENT_CS_FALLING_EDGE)
{
diff --git a/src/devices/machine/eepromser.h b/src/devices/machine/eepromser.h
index 0eefba08276..d0d662948d8 100644
--- a/src/devices/machine/eepromser.h
+++ b/src/devices/machine/eepromser.h
@@ -91,6 +91,8 @@
#define MCFG_EEPROM_SERIAL_DATA MCFG_EEPROM_DATA
#define MCFG_EEPROM_SERIAL_DEFAULT_VALUE MCFG_EEPROM_DEFAULT_VALUE
+#define MCFG_EEPROM_SERIAL_DO_CALLBACK(_devcb) \
+ devcb = &eeprom_serial_base_device::static_set_do_callback(*device, DEVCB_##_devcb);
//**************************************************************************
@@ -107,6 +109,10 @@ public:
static void static_set_address_bits(device_t &device, int addrbits);
static void static_enable_streaming(device_t &device);
static void static_enable_output_on_falling_clock(device_t &device);
+ template<class Object> static devcb_base &static_set_do_callback(device_t &device, Object &&object)
+ {
+ return downcast<eeprom_serial_base_device &>(device).m_do_cb.set_callback(std::forward<Object>(object));
+ }
protected:
// construction/destruction
@@ -174,6 +180,7 @@ protected:
uint8_t m_command_address_bits; // number of address bits in a command
bool m_streaming_enabled; // true if streaming is enabled
bool m_output_on_falling_clock_enabled; // true if the output pin is updated on the falling edge of the clock
+ devcb_write_line m_do_cb; // callback to push state of DO line
// runtime state
eeprom_state m_state; // current internal state
@@ -314,7 +321,7 @@ DECLARE_SERIAL_EEPROM_DEVICE(93cxx, s29190, S29190, 16)
DECLARE_SERIAL_EEPROM_DEVICE(93cxx, s29290, S29290, 16)
DECLARE_SERIAL_EEPROM_DEVICE(93cxx, s29390, S29390, 16)
-// X24c44 8 bit 32byte ram/eeprom combo
+// X24c44 16 bit 32byte ram/eeprom combo
DECLARE_SERIAL_EEPROM_DEVICE(x24c44, x24c44, X24C44, 16)
#endif // MAME_MACHINE_EEPROMSER_H
diff --git a/src/devices/machine/genpc.cpp b/src/devices/machine/genpc.cpp
index 12457166cc6..74180d23423 100644
--- a/src/devices/machine/genpc.cpp
+++ b/src/devices/machine/genpc.cpp
@@ -164,6 +164,11 @@ WRITE_LINE_MEMBER(ibm5160_mb_device::pc_speaker_set_spkrdata)
m_speaker->level_w(m_pc_spkrdata & m_pit_out2);
}
+WRITE_LINE_MEMBER(ibm5160_mb_device::pic_int_w)
+{
+ m_maincpu->set_input_line(0, state);
+}
+
/*************************************************************
*
@@ -427,7 +432,7 @@ MACHINE_CONFIG_MEMBER( ibm5160_mb_device::device_add_mconfig )
MCFG_I8237_OUT_DACK_3_CB(WRITELINE(ibm5160_mb_device, pc_dack3_w))
MCFG_DEVICE_ADD("pic8259", PIC8259, 0)
- MCFG_PIC8259_OUT_INT_CB(INPUTLINE(":maincpu", 0))
+ MCFG_PIC8259_OUT_INT_CB(WRITELINE(ibm5160_mb_device, pic_int_w))
MCFG_DEVICE_ADD("ppi8255", I8255A, 0)
MCFG_I8255_IN_PORTA_CB(READ8(ibm5160_mb_device, pc_ppi_porta_r))
@@ -494,7 +499,7 @@ ioport_constructor ibm5160_mb_device::device_input_ports() const
void ibm5160_mb_device::static_set_cputag(device_t &device, const char *tag)
{
ibm5160_mb_device &board = downcast<ibm5160_mb_device &>(device);
- board.m_cputag = tag;
+ board.m_maincpu.set_tag(tag);
}
//**************************************************************************
@@ -517,7 +522,7 @@ ibm5160_mb_device::ibm5160_mb_device(
device_t *owner,
uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
- , m_maincpu(*owner, "maincpu")
+ , m_maincpu(*this, finder_base::DUMMY_TAG)
, m_pic8259(*this, "pic8259")
, m_pit8253(*this, "pit8253")
, m_dma8237(*this, "dma8237")
diff --git a/src/devices/machine/genpc.h b/src/devices/machine/genpc.h
index c3e53283e75..22e53130bf1 100644
--- a/src/devices/machine/genpc.h
+++ b/src/devices/machine/genpc.h
@@ -25,7 +25,9 @@
#define MCFG_IBM5160_MOTHERBOARD_ADD(_tag, _cputag) \
MCFG_DEVICE_ADD(_tag, IBM5160_MOTHERBOARD, 0) \
- ibm5160_mb_device::static_set_cputag(*device, _cputag);
+ ibm5160_mb_device::static_set_cputag(*device, "^" _cputag); \
+ isa8_device::static_set_cputag(*device->subdevice("isa"), "^^" _cputag);
+
// ======================> ibm5160_mb_device
class ibm5160_mb_device : public device_t
{
@@ -48,6 +50,8 @@ public:
DECLARE_WRITE_LINE_MEMBER( pc_pit8253_out1_changed );
DECLARE_WRITE_LINE_MEMBER( pc_pit8253_out2_changed );
+ DECLARE_WRITE_LINE_MEMBER( pic_int_w );
+
protected:
ibm5160_mb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
@@ -116,8 +120,6 @@ protected:
DECLARE_WRITE_LINE_MEMBER( pc_dack2_w );
DECLARE_WRITE_LINE_MEMBER( pc_dack3_w );
- const char *m_cputag;
-
void pc_select_dma_channel(int channel, bool state);
};
@@ -128,7 +130,8 @@ DECLARE_DEVICE_TYPE(IBM5160_MOTHERBOARD, ibm5160_mb_device)
#define MCFG_IBM5150_MOTHERBOARD_ADD(_tag, _cputag) \
MCFG_DEVICE_ADD(_tag, IBM5150_MOTHERBOARD, 0) \
- ibm5150_mb_device::static_set_cputag(*device, _cputag);
+ ibm5150_mb_device::static_set_cputag(*device, "^" _cputag); \
+ isa8_device::static_set_cputag(*device->subdevice("isa"), "^^" _cputag);
// ======================> ibm5150_mb_device
class ibm5150_mb_device : public ibm5160_mb_device
@@ -160,7 +163,8 @@ DECLARE_DEVICE_TYPE(IBM5150_MOTHERBOARD, ibm5150_mb_device)
#define MCFG_EC1841_MOTHERBOARD_ADD(_tag, _cputag) \
MCFG_DEVICE_ADD(_tag, EC1841_MOTHERBOARD, 0) \
- ec1841_mb_device::static_set_cputag(*device, _cputag);
+ ec1841_mb_device::static_set_cputag(*device, "^" _cputag); \
+ isa8_device::static_set_cputag(*device->subdevice("isa"), "^^" _cputag);
class ec1841_mb_device : public ibm5160_mb_device
{
@@ -185,7 +189,8 @@ DECLARE_DEVICE_TYPE(EC1841_MOTHERBOARD, ec1841_mb_device)
#define MCFG_PCNOPPI_MOTHERBOARD_ADD(_tag, _cputag) \
MCFG_DEVICE_ADD(_tag, PCNOPPI_MOTHERBOARD, 0) \
- pc_noppi_mb_device::static_set_cputag(*device, _cputag);
+ pc_noppi_mb_device::static_set_cputag(*device, "^" _cputag); \
+ isa8_device::static_set_cputag(*device->subdevice("isa"), "^^" _cputag);
class pc_noppi_mb_device : public ibm5160_mb_device
{
diff --git a/src/devices/machine/i8155.cpp b/src/devices/machine/i8155.cpp
index fcb44bafe30..8fbcf1462ce 100644
--- a/src/devices/machine/i8155.cpp
+++ b/src/devices/machine/i8155.cpp
@@ -1,8 +1,18 @@
// license:BSD-3-Clause
-// copyright-holders:Curt Coder
+// copyright-holders:Curt Coder,AJR
/**********************************************************************
- Intel 8155/8156 - 2048-Bit Static MOS RAM with I/O Ports and Timer emulation
+ Intel 8155/8156 - 2048-Bit Static MOS RAM with I/O Ports and Timer
+
+ The timer primarily functions as a square-wave generator, but can
+ also be programmed for a single-cycle low pulse on terminal count.
+
+ The only difference between 8155 and 8156 is that pin 8 (CE) is
+ active low on the former device and active high on the latter.
+
+ National's NSC810 RAM-I/O-Timer is pin-compatible with the Intel
+ 8156, but has different I/O registers (including a second timer)
+ with incompatible mapping.
**********************************************************************/
@@ -10,7 +20,8 @@
TODO:
- - strobed mode
+ - ALT 3 and ALT 4 strobed port modes
+ - optional NVRAM backup for CMOS versions
*/
@@ -19,15 +30,18 @@
// device type definitions
-DEFINE_DEVICE_TYPE(I8155, i8155_device, "i8155", "Intel 8155 RIOT")
-const device_type I8156 = I8155;
+DEFINE_DEVICE_TYPE(I8155, i8155_device, "i8155", "Intel 8155 RAM, I/O & Timer")
+DEFINE_DEVICE_TYPE(I8156, i8156_device, "i8156", "Intel 8156 RAM, I/O & Timer")
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
-#define LOG 0
+#define LOG_PORT (1U << 0)
+#define LOG_TIMER (1U << 1)
+#define VERBOSE (LOG_PORT | LOG_TIMER)
+#include "logmacro.h"
enum
{
@@ -86,10 +100,8 @@ enum
#define STATUS_TIMER 0x40
#define TIMER_MODE_MASK 0xc0
-#define TIMER_MODE_LOW 0x00
-#define TIMER_MODE_SQUARE_WAVE 0x40
-#define TIMER_MODE_SINGLE_PULSE 0x80
-#define TIMER_MODE_AUTOMATIC_RELOAD 0xc0
+#define TIMER_MODE_AUTO_RELOAD 0x40
+#define TIMER_MODE_TC_PULSE 0x80
@@ -110,20 +122,70 @@ ADDRESS_MAP_END
inline uint8_t i8155_device::get_timer_mode()
{
- return (m_count_length >> 8) & TIMER_MODE_MASK;
+ return (m_count_loaded >> 8) & TIMER_MODE_MASK;
}
-inline void i8155_device::timer_output()
+inline void i8155_device::timer_output(int to)
{
- m_out_to_cb(m_to);
+ if (to == m_to)
+ return;
- if (LOG) logerror("8155 Timer Output: %u\n", m_to);
+ m_to = to;
+ m_out_to_cb(to);
+
+ LOGMASKED(LOG_TIMER, "Timer output: %u\n", to);
}
-inline void i8155_device::pulse_timer_output()
+inline void i8155_device::timer_stop_count()
{
- m_to = 0; timer_output();
- m_to = 1; timer_output();
+ // stop counting
+ m_timer->enable(0);
+
+ // clear timer output
+ timer_output(1);
+}
+
+inline void i8155_device::timer_reload_count()
+{
+ m_count_loaded = m_count_length;
+
+ // valid counts range from 2 to 3FFF
+ if ((m_count_length & 0x3fff) < 2)
+ {
+ timer_stop_count();
+ return;
+ }
+
+ // begin the odd half of the count, with one extra cycle if count is odd
+ m_counter = (m_count_loaded & 0x3ffe) | 1;
+ m_count_extra = BIT(m_count_loaded, 0);
+
+ // set up our timer
+ m_timer->adjust(attotime::zero, 0, clocks_to_attotime(1));
+ timer_output(1);
+
+ switch (get_timer_mode())
+ {
+ case 0:
+ // puts out LOW during second half of count
+ LOGMASKED(LOG_TIMER, "Timer loaded with %d (Mode: LOW)\n", m_count_loaded & 0x3fff);
+ break;
+
+ case TIMER_MODE_AUTO_RELOAD:
+ // square wave, i.e. the period of the square wave equals the count length programmed with automatic reload at terminal count
+ LOGMASKED(LOG_TIMER, "Timer loaded with %d (Mode: Square wave)\n", m_count_loaded & 0x3fff);
+ break;
+
+ case TIMER_MODE_TC_PULSE:
+ // single pulse upon TC being reached
+ LOGMASKED(LOG_TIMER, "Timer loaded with %d (Mode: Single pulse)\n", m_count_loaded & 0x3fff);
+ break;
+
+ case TIMER_MODE_TC_PULSE | TIMER_MODE_AUTO_RELOAD:
+ // automatic reload, i.e. single pulse every time TC is reached
+ LOGMASKED(LOG_TIMER, "Timer loaded with %d (Mode: Automatic reload)\n", m_count_loaded & 0x3fff);
+ break;
+ }
}
inline int i8155_device::get_port_mode(int port)
@@ -203,7 +265,12 @@ inline void i8155_device::write_port(int port, uint8_t data)
//-------------------------------------------------
i8155_device::i8155_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, I8155, tag, owner, clock),
+ : i8155_device(mconfig, I8155, tag, owner, clock)
+{
+}
+
+i8155_device::i8155_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock),
device_memory_interface(mconfig, *this),
m_in_pa_cb(*this),
m_in_pb_cb(*this),
@@ -214,12 +281,27 @@ i8155_device::i8155_device(const machine_config &mconfig, const char *tag, devic
m_out_to_cb(*this),
m_command(0),
m_status(0),
+ m_count_length(0),
+ m_count_loaded(0),
+ m_counter(0),
+ m_count_extra(false),
+ m_to(0),
m_space_config("ram", ENDIANNESS_LITTLE, 8, 8, 0, nullptr, *ADDRESS_MAP_NAME(i8155))
{
}
//-------------------------------------------------
+// i8156_device - constructor
+//-------------------------------------------------
+
+i8156_device::i8156_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : i8155_device(mconfig, I8156, tag, owner, clock)
+{
+}
+
+
+//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
@@ -244,6 +326,8 @@ void i8155_device::device_start()
save_item(NAME(m_status));
save_item(NAME(m_output));
save_item(NAME(m_count_length));
+ save_item(NAME(m_count_loaded));
+ save_item(NAME(m_count_extra));
save_item(NAME(m_counter));
save_item(NAME(m_to));
}
@@ -266,12 +350,8 @@ void i8155_device::device_reset()
// clear timer flag
m_status &= ~STATUS_TIMER;
- // stop counting
- m_timer->enable(0);
-
- // clear timer output
- m_to = 1;
- timer_output();
+ // stop timer
+ timer_stop_count();
}
@@ -281,60 +361,52 @@ void i8155_device::device_reset()
void i8155_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
- // count down
- m_counter--;
-
- if (get_timer_mode() == TIMER_MODE_LOW)
+ if (m_count_extra)
{
- // pulse on every count
- pulse_timer_output();
+ m_count_extra = false;
+ return;
}
- if (m_counter == 0)
+ // count down by twos
+ m_counter -= 2;
+
+ if (m_counter == 1)
{
- if (LOG) logerror("8155 Timer Count Reached\n");
+ LOGMASKED(LOG_TIMER, "Timer count half finished\n");
- switch (get_timer_mode())
- {
- case TIMER_MODE_LOW:
- case TIMER_MODE_SQUARE_WAVE:
- // toggle timer output
- m_to = !m_to;
- timer_output();
- break;
+ // reload the even half of the count
+ m_counter = m_count_loaded & 0x3ffe;
- case TIMER_MODE_SINGLE_PULSE:
- case TIMER_MODE_AUTOMATIC_RELOAD:
- // pulse upon TC being reached
- pulse_timer_output();
- break;
+ // square wave modes produce a low output in the second half of the counting period
+ if ((get_timer_mode() & TIMER_MODE_TC_PULSE) == 0)
+ timer_output(0);
+ }
+ else if (m_counter == 2)
+ {
+ if ((get_timer_mode() & TIMER_MODE_TC_PULSE) != 0)
+ {
+ // pulse low on TC being reached
+ timer_output(0);
}
// set timer flag
m_status |= STATUS_TIMER;
+ }
+ else if (m_counter == 0)
+ {
+ timer_output(1);
- if ((m_command & COMMAND_TM_MASK) == COMMAND_TM_START)
- {
- // load new timer counter
- m_counter = m_count_length & 0x3fff;
-
- if (LOG) logerror("8155 Timer New Start\n");
- }
- else if ((m_command & COMMAND_TM_MASK) == COMMAND_TM_STOP_AFTER_TC || get_timer_mode() == TIMER_MODE_SINGLE_PULSE)
+ if ((get_timer_mode() & TIMER_MODE_AUTO_RELOAD) == 0 || (m_command & COMMAND_TM_MASK) == COMMAND_TM_STOP_AFTER_TC)
{
// stop timer
- m_timer->enable(0);
-
- if (LOG) logerror("8155 Timer Stopped\n");
+ timer_stop_count();
+ LOGMASKED(LOG_TIMER, "Timer stopped\n");
}
else
{
// automatically reload the counter
- m_counter = m_count_length & 0x3fff;
+ timer_reload_count();
}
-
- // clear timer command
- m_command &= ~COMMAND_TM_MASK;
}
}
@@ -405,28 +477,28 @@ void i8155_device::register_w(int offset, uint8_t data)
case REGISTER_COMMAND:
m_command = data;
- if (LOG) logerror("8155 Port A Mode: %s\n", (data & COMMAND_PA) ? "output" : "input");
- if (LOG) logerror("8155 Port B Mode: %s\n", (data & COMMAND_PB) ? "output" : "input");
+ LOGMASKED(LOG_PORT, "Port A Mode: %s\n", (data & COMMAND_PA) ? "output" : "input");
+ LOGMASKED(LOG_PORT, "Port B Mode: %s\n", (data & COMMAND_PB) ? "output" : "input");
- if (LOG) logerror("8155 Port A Interrupt: %s\n", (data & COMMAND_IEA) ? "enabled" : "disabled");
- if (LOG) logerror("8155 Port B Interrupt: %s\n", (data & COMMAND_IEB) ? "enabled" : "disabled");
+ LOGMASKED(LOG_PORT, "Port A Interrupt: %s\n", (data & COMMAND_IEA) ? "enabled" : "disabled");
+ LOGMASKED(LOG_PORT, "Port B Interrupt: %s\n", (data & COMMAND_IEB) ? "enabled" : "disabled");
switch (data & COMMAND_PC_MASK)
{
case COMMAND_PC_ALT_1:
- if (LOG) logerror("8155 Port C Mode: Alt 1\n");
+ LOGMASKED(LOG_PORT, "Port C Mode: Alt 1\n");
break;
case COMMAND_PC_ALT_2:
- if (LOG) logerror("8155 Port C Mode: Alt 2\n");
+ LOGMASKED(LOG_PORT, "Port C Mode: Alt 2\n");
break;
case COMMAND_PC_ALT_3:
- if (LOG) logerror("8155 Port C Mode: Alt 3\n");
+ LOGMASKED(LOG_PORT, "Port C Mode: Alt 3\n");
break;
case COMMAND_PC_ALT_4:
- if (LOG) logerror("8155 Port C Mode: Alt 4\n");
+ LOGMASKED(LOG_PORT, "Port C Mode: Alt 4\n");
break;
}
@@ -438,19 +510,17 @@ void i8155_device::register_w(int offset, uint8_t data)
case COMMAND_TM_STOP:
// NOP if timer has not started, stop counting if the timer is running
- if (LOG) logerror("8155 Timer Command: Stop\n");
- m_to = 1;
- timer_output();
- m_timer->enable(0);
+ LOGMASKED(LOG_PORT, "Timer Command: Stop\n");
+ timer_stop_count();
break;
case COMMAND_TM_STOP_AFTER_TC:
// stop immediately after present TC is reached (NOP if timer has not started)
- if (LOG) logerror("8155 Timer Command: Stop after TC\n");
+ LOGMASKED(LOG_PORT, "Timer Command: Stop after TC\n");
break;
case COMMAND_TM_START:
- if (LOG) logerror("8155 Timer Command: Start\n");
+ LOGMASKED(LOG_PORT, "Timer Command: Start\n");
if (m_timer->enabled())
{
@@ -459,12 +529,7 @@ void i8155_device::register_w(int offset, uint8_t data)
else
{
// load mode and CNT length and start immediately after loading (if timer is not running)
- m_counter = m_count_length & 0x3fff;
- if (clock() > 0) // a clock of 0 causes MAME to freeze.
- m_timer->adjust(attotime::zero, 0, attotime::from_hz(clock()));
-
- // clear timer command so this won't execute twice
- m_command &= ~COMMAND_TM_MASK;
+ timer_reload_count();
}
break;
}
@@ -484,35 +549,10 @@ void i8155_device::register_w(int offset, uint8_t data)
case REGISTER_TIMER_LOW:
m_count_length = (m_count_length & 0xff00) | data;
- if (LOG) logerror("8155 Count Length Low: %04x\n", m_count_length);
break;
case REGISTER_TIMER_HIGH:
m_count_length = (data << 8) | (m_count_length & 0xff);
- if (LOG) logerror("8155 Count Length High: %04x\n", m_count_length);
-
- switch (data & TIMER_MODE_MASK)
- {
- case TIMER_MODE_LOW:
- // puts out LOW during second half of count
- if (LOG) logerror("8155 Timer Mode: LOW\n");
- break;
-
- case TIMER_MODE_SQUARE_WAVE:
- // square wave, i.e. the period of the square wave equals the count length programmed with automatic reload at terminal count
- if (LOG) logerror("8155 Timer Mode: Square wave\n");
- break;
-
- case TIMER_MODE_SINGLE_PULSE:
- // single pulse upon TC being reached
- if (LOG) logerror("8155 Timer Mode: Single pulse\n");
- break;
-
- case TIMER_MODE_AUTOMATIC_RELOAD:
- // automatic reload, i.e. single pulse every time TC is reached
- if (LOG) logerror("8155 Timer Mode: Automatic reload\n");
- break;
- }
break;
}
}
diff --git a/src/devices/machine/i8155.h b/src/devices/machine/i8155.h
index 88c08f57082..f4220baa546 100644
--- a/src/devices/machine/i8155.h
+++ b/src/devices/machine/i8155.h
@@ -2,8 +2,7 @@
// copyright-holders:Curt Coder
/**********************************************************************
- Intel 8155/8156 - 2048-Bit Static MOS RAM with I/O Ports and Timer emulation
- 8156 is the same as 8155, except that chip enable is active high instead of low
+ Intel 8155/8156 - 2048-Bit Static MOS RAM with I/O Ports and Timer
**********************************************************************
_____ _____
@@ -95,6 +94,8 @@ public:
DECLARE_WRITE8_MEMBER( write );
protected:
+ i8155_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
@@ -103,8 +104,9 @@ protected:
virtual space_config_vector memory_space_config() const override;
inline uint8_t get_timer_mode();
- inline void timer_output();
- inline void pulse_timer_output();
+ inline void timer_output(int to);
+ inline void timer_stop_count();
+ inline void timer_reload_count();
inline int get_port_mode(int port);
inline uint8_t read_port(int port);
inline void write_port(int port, uint8_t data);
@@ -125,16 +127,18 @@ private:
// CPU interface
int m_io_m; // I/O or memory select
- uint8_t m_ad; // address
+ uint8_t m_ad; // address
// registers
- uint8_t m_command; // command register
- uint8_t m_status; // status register
- uint8_t m_output[3]; // output latches
+ uint8_t m_command; // command register
+ uint8_t m_status; // status register
+ uint8_t m_output[3]; // output latches
// counter
- uint16_t m_count_length; // count length register
- uint16_t m_counter; // counter register
+ uint16_t m_count_length; // count length register (assigned)
+ uint16_t m_count_loaded; // count length register (loaded)
+ uint16_t m_counter; // counter register
+ bool m_count_extra; // extra cycle when count is odd
int m_to; // timer output
// timers
@@ -144,9 +148,16 @@ private:
};
+class i8156_device : public i8155_device
+{
+public:
+ // construction/destruction
+ i8156_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
// device type definition
DECLARE_DEVICE_TYPE(I8155, i8155_device)
-extern const device_type I8156;
+DECLARE_DEVICE_TYPE(I8156, i8156_device)
#endif
diff --git a/src/devices/machine/input_merger.cpp b/src/devices/machine/input_merger.cpp
index de2790bff1b..28652cdb66f 100644
--- a/src/devices/machine/input_merger.cpp
+++ b/src/devices/machine/input_merger.cpp
@@ -15,6 +15,9 @@
#include <algorithm>
#include <iterator>
+//#define VERBOSE 1
+#include "logmacro.h"
+
//**************************************************************************
// DEVICE DEFINITIONS
@@ -79,6 +82,7 @@ TIMER_CALLBACK_MEMBER(input_merger_device::update_state)
{
if (BIT(m_state, param >> 1) != BIT(param, 0))
{
+ LOG("state[%d] = %d\n", param >> 1, BIT(param, 0));
m_state ^= u32(1) << (param >> 1);
m_output_handler((m_state ^ m_xorval) ? m_active : !m_active);
}
diff --git a/src/devices/machine/mc14411.cpp b/src/devices/machine/mc14411.cpp
index 6494a12630f..d8ce720a0f3 100644
--- a/src/devices/machine/mc14411.cpp
+++ b/src/devices/machine/mc14411.cpp
@@ -56,7 +56,7 @@
***************************************************************************/
const int mc14411_device::s_counter_divider[16] = {
- ////////// X64 /////// X16 /////// X8 //////// X1 ////////
+ ////////// X64 /////// X16 /////// X8 //////// X1 ////////
3, // F1: 614.4 kHz 153.6 kHz 76800 Hz 9600 Hz
4, // F2: 460.8 kHz 115.2 kHz 57600 Hz 7200 Hz
6, // F3: 307.2 kHz 76800 Hz 36400 Hz 4800 Hz
diff --git a/src/devices/machine/mc146818.cpp b/src/devices/machine/mc146818.cpp
index 4cdb4f37b5e..afe912a7d0d 100644
--- a/src/devices/machine/mc146818.cpp
+++ b/src/devices/machine/mc146818.cpp
@@ -412,31 +412,7 @@ void mc146818_device::update_timer()
{
int bypass;
- switch (m_data[REG_A] & (REG_A_DV2 | REG_A_DV1 | REG_A_DV0))
- {
- case 0:
- bypass = 0;
- break;
-
- case REG_A_DV0:
- bypass = 2;
- break;
-
- case REG_A_DV1:
- bypass = 7;
- break;
-
- case REG_A_DV2 | REG_A_DV1:
- case REG_A_DV2 | REG_A_DV1 | REG_A_DV0:
- bypass = 22;
- break;
-
- default:
- // TODO: other combinations of divider bits are used for test purposes only
- bypass = 22;
- break;
- }
-
+ bypass = get_timer_bypass();
attotime update_period = attotime::never;
attotime update_interval = attotime::never;
@@ -472,6 +448,41 @@ void mc146818_device::update_timer()
m_periodic_timer->adjust(periodic_period, 0, periodic_interval);
}
+//---------------------------------------------------------------
+// get_timer_bypass - get main clock divisor based on A register
+//---------------------------------------------------------------
+
+int mc146818_device::get_timer_bypass()
+{
+ int bypass;
+
+ switch (m_data[REG_A] & (REG_A_DV2 | REG_A_DV1 | REG_A_DV0))
+ {
+ case 0:
+ bypass = 0;
+ break;
+
+ case REG_A_DV0:
+ bypass = 2;
+ break;
+
+ case REG_A_DV1:
+ bypass = 7;
+ break;
+
+ case REG_A_DV2 | REG_A_DV1:
+ case REG_A_DV2 | REG_A_DV1 | REG_A_DV0:
+ bypass = 22;
+ break;
+
+ default:
+ // TODO: other combinations of divider bits are used for test purposes only
+ bypass = 22;
+ break;
+ }
+
+ return bypass;
+}
//-------------------------------------------------
// update_irq - Update irq based on B & C register
diff --git a/src/devices/machine/mc146818.h b/src/devices/machine/mc146818.h
index 408f9f717d8..85256106abf 100644
--- a/src/devices/machine/mc146818.h
+++ b/src/devices/machine/mc146818.h
@@ -91,7 +91,6 @@ protected:
virtual int data_size() { return 64; }
-private:
enum
{
REG_SECONDS = 0,
@@ -153,7 +152,7 @@ private:
void set_base_datetime();
void update_irq();
void update_timer();
-
+ virtual int get_timer_bypass();
int get_seconds();
void set_seconds(int seconds);
int get_minutes();
diff --git a/src/devices/machine/mc68681.cpp b/src/devices/machine/mc68681.cpp
index da860478f6d..f288a76971c 100644
--- a/src/devices/machine/mc68681.cpp
+++ b/src/devices/machine/mc68681.cpp
@@ -11,12 +11,22 @@
Improved interrupt handling by R. Belmont
Rewrite and modernization in progress by R. Belmont
Addition of the duart compatible 68340 serial module support by Edstrom
+
+ The main incompatibility between the 2681 and 68681 (Signetics and Motorola each
+ manufactured both versions of the chip) is that the 68681 has a R/W input and
+ generates a 68000-compatible DTACK signal, instead of using generic RD and WR
+ strobes as the 2681 does. The 68681 also adds a programmable interrupt vector,
+ with an IACK input replacing IP6.
+
+ The command register addresses should never be read from. Doing so may place
+ the baud rate generator into a test mode which drives the parallel outputs with
+ internal counters and causes serial ports to operate at uncontrollable rates.
*/
#include "emu.h"
#include "mc68681.h"
-//#define VERBOSE 1
+#define VERBOSE 1
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"
@@ -60,17 +70,18 @@ static const int baud_rate_ACR_1[] = { 75, 110, 134, 150, 300, 600, 1200, 2000,
#define CHAND_TAG "chd"
// device type definition
+DEFINE_DEVICE_TYPE(SCN2681, scn2681_device, "scn2681", "SCN2681 DUART")
DEFINE_DEVICE_TYPE(MC68681, mc68681_device, "mc68681", "MC68681 DUART")
DEFINE_DEVICE_TYPE(SC28C94, sc28c94_device, "sc28c94", "SC28C94 QUART")
DEFINE_DEVICE_TYPE(MC68340_DUART, mc68340_duart_device, "mc68340duart", "MC68340 DUART Device")
-DEFINE_DEVICE_TYPE(MC68681_CHANNEL, mc68681_channel, "mc68681_channel", "MC68681 DUART channel")
+DEFINE_DEVICE_TYPE(DUART_CHANNEL, duart_channel, "duart_channel", "DUART channel")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
-mc68681_base_device::mc68681_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+duart_base_device::duart_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock),
m_chanA(*this, CHANA_TAG),
m_chanB(*this, CHANB_TAG),
@@ -88,18 +99,23 @@ mc68681_base_device::mc68681_base_device(const machine_config &mconfig, device_t
ip5clk(0),
ip6clk(0),
ACR(0),
- m_read_vector(false),
IP_last_state(0)
{
}
+scn2681_device::scn2681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : duart_base_device(mconfig, SCN2681, tag, owner, clock)
+{
+}
+
mc68681_device::mc68681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : mc68681_base_device(mconfig, MC68681, tag, owner, clock)
+ : duart_base_device(mconfig, MC68681, tag, owner, clock),
+ m_read_vector(false)
{
}
sc28c94_device::sc28c94_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : mc68681_base_device(mconfig, SC28C94, tag, owner, clock)
+ : duart_base_device(mconfig, SC28C94, tag, owner, clock)
{
}
@@ -111,7 +127,7 @@ sc28c94_device::sc28c94_device(const machine_config &mconfig, const char *tag, d
// TODO: A lot of subtle differences and also detect misuse of unavailable registers as they should be ignored
//--------------------------------------------------------------------------------------------------------------------
mc68340_duart_device::mc68340_duart_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
- : mc68681_base_device(mconfig, type, tag, owner, clock)
+ : duart_base_device(mconfig, type, tag, owner, clock)
{
}
@@ -125,9 +141,9 @@ mc68340_duart_device::mc68340_duart_device(const machine_config &mconfig, const
// the external clocks
//-------------------------------------------------
-void mc68681_base_device::static_set_clocks(device_t &device, int clk3, int clk4, int clk5, int clk6)
+void duart_base_device::static_set_clocks(device_t &device, int clk3, int clk4, int clk5, int clk6)
{
- mc68681_base_device &duart = downcast<mc68681_base_device &>(device);
+ duart_base_device &duart = downcast<duart_base_device &>(device);
duart.ip3clk = clk3;
duart.ip4clk = clk4;
duart.ip5clk = clk5;
@@ -138,7 +154,7 @@ void mc68681_base_device::static_set_clocks(device_t &device, int clk3, int clk4
device start callback
-------------------------------------------------*/
-void mc68681_base_device::device_start()
+void duart_base_device::device_start()
{
write_irq.resolve_safe();
write_a_tx.resolve_safe();
@@ -148,56 +164,70 @@ void mc68681_base_device::device_start()
read_inport.resolve();
write_outport.resolve_safe();
- duart_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mc68681_base_device::duart_timer_callback),this), nullptr);
+ duart_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(duart_base_device::duart_timer_callback),this), nullptr);
save_item(NAME(ACR));
save_item(NAME(IMR));
save_item(NAME(ISR));
- save_item(NAME(IVR));
save_item(NAME(OPCR));
save_item(NAME(CTR));
save_item(NAME(IP_last_state));
save_item(NAME(half_period));
}
+void mc68681_device::device_start()
+{
+ duart_base_device::device_start();
+
+ save_item(NAME(m_read_vector));
+ save_item(NAME(IVR));
+}
+
/*-------------------------------------------------
device reset callback
-------------------------------------------------*/
-void mc68681_base_device::device_reset()
+void duart_base_device::device_reset()
{
ACR = 0; /* Interrupt Vector Register */
- IVR = 0x0f; /* Interrupt Vector Register */
IMR = 0; /* Interrupt Mask Register */
ISR = 0; /* Interrupt Status Register */
OPCR = 0; /* Output Port Conf. Register */
OPR = 0; /* Output Port Register */
CTR.d = 0; /* Counter/Timer Preset Value */
- m_read_vector = false;
// "reset clears internal registers (SRA, SRB, IMR, ISR, OPR, OPCR) puts OP0-7 in the high state, stops the counter/timer, and puts channels a/b in the inactive state"
IPCR = 0;
+ write_irq(CLEAR_LINE);
write_outport(OPR ^ 0xff);
}
-MACHINE_CONFIG_MEMBER( mc68681_base_device::device_add_mconfig )
- MCFG_DEVICE_ADD(CHANA_TAG, MC68681_CHANNEL, 0)
- MCFG_DEVICE_ADD(CHANB_TAG, MC68681_CHANNEL, 0)
+void mc68681_device::device_reset()
+{
+ duart_base_device::device_reset();
+
+ IVR = 0x0f; /* Interrupt Vector Register */
+ m_read_vector = false;
+}
+
+MACHINE_CONFIG_MEMBER( duart_base_device::device_add_mconfig )
+ MCFG_DEVICE_ADD(CHANA_TAG, DUART_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHANB_TAG, DUART_CHANNEL, 0)
MACHINE_CONFIG_END
MACHINE_CONFIG_MEMBER( sc28c94_device::device_add_mconfig )
- MCFG_DEVICE_ADD(CHANA_TAG, MC68681_CHANNEL, 0)
- MCFG_DEVICE_ADD(CHANB_TAG, MC68681_CHANNEL, 0)
- MCFG_DEVICE_ADD(CHANC_TAG, MC68681_CHANNEL, 0)
- MCFG_DEVICE_ADD(CHAND_TAG, MC68681_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHANA_TAG, DUART_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHANB_TAG, DUART_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHANC_TAG, DUART_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHAND_TAG, DUART_CHANNEL, 0)
MACHINE_CONFIG_END
MACHINE_CONFIG_MEMBER( mc68340_duart_device::device_add_mconfig )
- MCFG_DEVICE_ADD(CHANA_TAG, MC68681_CHANNEL, 0)
- MCFG_DEVICE_ADD(CHANB_TAG, MC68681_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHANA_TAG, DUART_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHANB_TAG, DUART_CHANNEL, 0)
MACHINE_CONFIG_END
-void mc68681_base_device::update_interrupts()
+void duart_base_device::update_interrupts()
{
/* update SR state and update interrupt ISR state for the following bits:
SRn: bits 7-4: handled elsewhere.
@@ -223,7 +253,6 @@ void mc68681_base_device::update_interrupts()
{
LOG( "68681: Interrupt line not active (IMR & ISR = %02X)\n", ISR & IMR);
write_irq(CLEAR_LINE);
- m_read_vector = false; // clear IACK too
}
if(OPCR & 0xf0)
{
@@ -259,7 +288,15 @@ void mc68681_base_device::update_interrupts()
}
}
-double mc68681_base_device::duart68681_get_ct_rate()
+void mc68681_device::update_interrupts()
+{
+ duart_base_device::update_interrupts();
+
+ if (!irq_pending())
+ m_read_vector = false; // clear IACK too
+}
+
+double duart_base_device::get_ct_rate()
{
double rate = 0.0f;
@@ -301,19 +338,19 @@ double mc68681_base_device::duart68681_get_ct_rate()
return rate;
}
-uint16_t mc68681_base_device::duart68681_get_ct_count()
+uint16_t duart_base_device::get_ct_count()
{
- double clock = duart68681_get_ct_rate();
+ double clock = get_ct_rate();
return (duart_timer->remaining() * clock).as_double();
}
-void mc68681_base_device::duart68681_start_ct(int count)
+void duart_base_device::start_ct(int count)
{
- double clock = duart68681_get_ct_rate();
+ double clock = get_ct_rate();
duart_timer->adjust(attotime::from_hz(clock) * count, 0);
}
-TIMER_CALLBACK_MEMBER( mc68681_base_device::duart_timer_callback )
+TIMER_CALLBACK_MEMBER( duart_base_device::duart_timer_callback )
{
if (ACR & 0x40)
{
@@ -353,22 +390,37 @@ TIMER_CALLBACK_MEMBER( mc68681_base_device::duart_timer_callback )
}
if (!half_period)
- {
- ISR |= INT_COUNTER_READY;
- update_interrupts();
- }
+ set_ISR_bits(INT_COUNTER_READY);
int count = std::max(CTR.w.l, uint16_t(1));
- duart68681_start_ct(count);
+ start_ct(count);
}
else
{
// Counter mode
- ISR |= INT_COUNTER_READY;
- update_interrupts();
- duart68681_start_ct(0xffff);
+ set_ISR_bits(INT_COUNTER_READY);
+ start_ct(0xffff);
+ }
+
+}
+
+READ8_MEMBER( mc68681_device::read )
+{
+ if (offset == 0x0c)
+ return IVR;
+
+ uint8_t r = duart_base_device::read(space, offset, mem_mask);
+
+ if (offset == 0x0d)
+ {
+ // bit 6 is /IACK (note the active-low)
+ if (m_read_vector)
+ r &= ~0x40;
+ else
+ r |= 0x40;
}
+ return r;
}
READ8_MEMBER( mc68340_duart_device::read )
@@ -390,7 +442,7 @@ READ8_MEMBER( mc68340_duart_device::read )
r = m_chanB->read_MR2();
break;
default:
- r = mc68681_base_device::read(space, offset, mem_mask);
+ r = duart_base_device::read(space, offset, mem_mask);
}
return r;
}
@@ -402,7 +454,7 @@ READ8_MEMBER( sc28c94_device::read )
if (offset < 0x10)
{
- return mc68681_base_device::read(space, offset, mem_mask);
+ return duart_base_device::read(space, offset, mem_mask);
}
switch (offset)
@@ -423,7 +475,7 @@ READ8_MEMBER( sc28c94_device::read )
return r;
}
-READ8_MEMBER( mc68681_base_device::read )
+READ8_MEMBER( duart_base_device::read )
{
uint8_t r = 0xff;
@@ -445,8 +497,7 @@ READ8_MEMBER( mc68681_base_device::read )
// reading this clears all the input change bits
IPCR &= 0x0f;
- ISR &= ~INT_INPUT_PORT_CHANGE;
- update_interrupts();
+ clear_ISR_bits(INT_INPUT_PORT_CHANGE);
}
break;
@@ -455,11 +506,11 @@ READ8_MEMBER( mc68681_base_device::read )
break;
case 0x06: /* CUR */
- r = duart68681_get_ct_count() >> 8;
+ r = get_ct_count() >> 8;
break;
case 0x07: /* CLR */
- r = duart68681_get_ct_count() & 0xff;
+ r = get_ct_count() & 0xff;
break;
case 0x08: /* MR1B/MR2B */
@@ -483,16 +534,6 @@ READ8_MEMBER( mc68681_base_device::read )
}
r |= 0x80; // bit 7 is always set
-
- // bit 6 is /IACK (note the active-low)
- if (m_read_vector)
- {
- r &= ~0x40;
- }
- else
- {
- r |= 0x40;
- }
break;
case 0x0e: /* Start counter command */
@@ -504,18 +545,17 @@ READ8_MEMBER( mc68681_base_device::read )
}
int count = std::max(CTR.w.l, uint16_t(1));
- duart68681_start_ct(count);
+ start_ct(count);
break;
}
case 0x0f: /* Stop counter command */
- ISR &= ~INT_COUNTER_READY;
+ clear_ISR_bits(INT_COUNTER_READY);
// Stop the counter only
if (!(ACR & 0x40))
duart_timer->adjust(attotime::never);
- update_interrupts();
break;
default:
@@ -527,6 +567,14 @@ READ8_MEMBER( mc68681_base_device::read )
return r;
}
+WRITE8_MEMBER( mc68681_device::write )
+{
+ if (offset == 0x0c)
+ IVR = data;
+ else
+ duart_base_device::write(space, offset, data, mem_mask);
+}
+
WRITE8_MEMBER( mc68340_duart_device::write )
{
//printf("Duart write %02x -> %02x\n", data, offset);
@@ -546,7 +594,7 @@ WRITE8_MEMBER( mc68340_duart_device::write )
m_chanB->write_MR2(data);
break;
default:
- mc68681_base_device::write(space, offset, data, mem_mask);
+ duart_base_device::write(space, offset, data, mem_mask);
}
}
@@ -556,7 +604,7 @@ WRITE8_MEMBER( sc28c94_device::write )
if (offset < 0x10)
{
- mc68681_base_device::write(space, offset, data, mem_mask);
+ duart_base_device::write(space, offset, data, mem_mask);
}
switch(offset)
@@ -577,7 +625,7 @@ WRITE8_MEMBER( sc28c94_device::write )
}
}
-WRITE8_MEMBER( mc68681_base_device::write )
+WRITE8_MEMBER( duart_base_device::write )
{
offset &= 0x0f;
LOG( "Writing 68681 (%s) reg %x (%s) with %04x\n", tag(), offset, duart68681_reg_write_names[offset], data );
@@ -605,7 +653,7 @@ WRITE8_MEMBER( mc68681_base_device::write )
uint16_t count = std::max(CTR.w.l, uint16_t(1));
half_period = 0;
- duart68681_start_ct(count);
+ start_ct(count);
}
else
{
@@ -616,15 +664,12 @@ WRITE8_MEMBER( mc68681_base_device::write )
// check for pending input port delta interrupts
if ((((IPCR>>4) & data) & 0x0f) != 0)
- {
- ISR |= INT_INPUT_PORT_CHANGE;
- }
+ set_ISR_bits(INT_INPUT_PORT_CHANGE);
m_chanA->ACR_updated();
m_chanB->ACR_updated();
m_chanA->update_interrupts();
m_chanB->update_interrupts();
- update_interrupts();
break;
}
case 0x05: /* IMR */
@@ -647,10 +692,6 @@ WRITE8_MEMBER( mc68681_base_device::write )
m_chanB->write_chan_reg(offset&3, data);
break;
- case 0x0c: /* IVR */
- IVR = data;
- break;
-
case 0x0d: /* OPCR */
if (((data & 0xf) != 0x00) && ((data & 0xc) != 0x4))
logerror( "68681 (%s): Unhandled OPCR value: %02x\n", tag(), data);
@@ -669,7 +710,7 @@ WRITE8_MEMBER( mc68681_base_device::write )
}
}
-WRITE_LINE_MEMBER( mc68681_base_device::ip0_w )
+WRITE_LINE_MEMBER( duart_base_device::ip0_w )
{
uint8_t newIP = (IP_last_state & ~0x01) | ((state == ASSERT_LINE) ? 1 : 0);
@@ -680,16 +721,13 @@ WRITE_LINE_MEMBER( mc68681_base_device::ip0_w )
IPCR |= 0x10;
if (ACR & 1)
- {
- ISR |= INT_INPUT_PORT_CHANGE;
- update_interrupts();
- }
+ set_ISR_bits(INT_INPUT_PORT_CHANGE);
}
IP_last_state = newIP;
}
-WRITE_LINE_MEMBER( mc68681_base_device::ip1_w )
+WRITE_LINE_MEMBER( duart_base_device::ip1_w )
{
uint8_t newIP = (IP_last_state & ~0x02) | ((state == ASSERT_LINE) ? 2 : 0);
@@ -700,16 +738,13 @@ WRITE_LINE_MEMBER( mc68681_base_device::ip1_w )
IPCR |= 0x20;
if (ACR & 2)
- {
- ISR |= INT_INPUT_PORT_CHANGE;
- update_interrupts();
- }
+ set_ISR_bits(INT_INPUT_PORT_CHANGE);
}
IP_last_state = newIP;
}
-WRITE_LINE_MEMBER( mc68681_base_device::ip2_w )
+WRITE_LINE_MEMBER( duart_base_device::ip2_w )
{
uint8_t newIP = (IP_last_state & ~0x04) | ((state == ASSERT_LINE) ? 4 : 0);
@@ -720,16 +755,13 @@ WRITE_LINE_MEMBER( mc68681_base_device::ip2_w )
IPCR |= 0x40;
if (ACR & 4)
- {
- ISR |= INT_INPUT_PORT_CHANGE;
- update_interrupts();
- }
+ set_ISR_bits(INT_INPUT_PORT_CHANGE);
}
IP_last_state = newIP;
}
-WRITE_LINE_MEMBER( mc68681_base_device::ip3_w )
+WRITE_LINE_MEMBER( duart_base_device::ip3_w )
{
uint8_t newIP = (IP_last_state & ~0x08) | ((state == ASSERT_LINE) ? 8 : 0);
@@ -740,30 +772,34 @@ WRITE_LINE_MEMBER( mc68681_base_device::ip3_w )
IPCR |= 0x80;
if (ACR & 8)
- {
- ISR |= INT_INPUT_PORT_CHANGE;
- update_interrupts();
- }
+ set_ISR_bits(INT_INPUT_PORT_CHANGE);
}
IP_last_state = newIP;
}
-WRITE_LINE_MEMBER( mc68681_base_device::ip4_w )
+WRITE_LINE_MEMBER( duart_base_device::ip4_w )
{
uint8_t newIP = (IP_last_state & ~0x10) | ((state == ASSERT_LINE) ? 0x10 : 0);
// TODO: special mode for ip4 (Ch. A Rx clock)
IP_last_state = newIP;
}
-WRITE_LINE_MEMBER( mc68681_base_device::ip5_w )
+WRITE_LINE_MEMBER( duart_base_device::ip5_w )
{
uint8_t newIP = (IP_last_state & ~0x20) | ((state == ASSERT_LINE) ? 0x20 : 0);
// TODO: special mode for ip5 (Ch. B Tx clock)
IP_last_state = newIP;
}
-mc68681_channel *mc68681_base_device::get_channel(int chan)
+WRITE_LINE_MEMBER( duart_base_device::ip6_w )
+{
+ uint8_t newIP = (IP_last_state & ~0x40) | ((state == ASSERT_LINE) ? 0x40 : 0);
+// TODO: special mode for ip6 (Ch. B Rx clock)
+ IP_last_state = newIP;
+}
+
+duart_channel *duart_base_device::get_channel(int chan)
{
if (chan == 0)
{
@@ -773,7 +809,7 @@ mc68681_channel *mc68681_base_device::get_channel(int chan)
return m_chanB;
}
-int mc68681_base_device::calc_baud(int ch, uint8_t data)
+int duart_base_device::calc_baud(int ch, uint8_t data)
{
int baud_rate;
@@ -818,20 +854,28 @@ int mc68681_base_device::calc_baud(int ch, uint8_t data)
return baud_rate;
}
-void mc68681_base_device::clear_ISR_bits(int mask)
+void duart_base_device::clear_ISR_bits(int mask)
{
- ISR &= ~mask;
+ if ((ISR & mask) != 0)
+ {
+ ISR &= ~mask;
+ update_interrupts();
+ }
}
-void mc68681_base_device::set_ISR_bits(int mask)
+void duart_base_device::set_ISR_bits(int mask)
{
- ISR |= mask;
+ if ((~ISR & mask) != 0)
+ {
+ ISR |= mask;
+ update_interrupts();
+ }
}
// DUART channel class stuff
-mc68681_channel::mc68681_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, MC68681_CHANNEL, tag, owner, clock),
+duart_channel::duart_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, DUART_CHANNEL, tag, owner, clock),
device_serial_interface(mconfig, *this),
MR1(0),
MR2(0),
@@ -842,9 +886,9 @@ mc68681_channel::mc68681_channel(const machine_config &mconfig, const char *tag,
{
}
-void mc68681_channel::device_start()
+void duart_channel::device_start()
{
- m_uart = downcast<mc68681_base_device *>(owner());
+ m_uart = downcast<duart_base_device *>(owner());
m_ch = m_uart->get_ch(this); // get our channel number
save_item(NAME(CR));
@@ -865,7 +909,7 @@ void mc68681_channel::device_start()
save_item(NAME(tx_ready));
}
-void mc68681_channel::device_reset()
+void duart_channel::device_reset()
{
write_CR(0x10); // reset MR
write_CR(0x20); // reset Rx
@@ -879,7 +923,7 @@ void mc68681_channel::device_reset()
}
// serial device virtual overrides
-void mc68681_channel::rcv_complete()
+void duart_channel::rcv_complete()
{
receive_register_extract();
@@ -903,7 +947,7 @@ void mc68681_channel::rcv_complete()
}
}
-void mc68681_channel::tra_complete()
+void duart_channel::tra_complete()
{
//printf("%s ch %d Tx complete\n", tag(), m_ch);
tx_ready = 1;
@@ -936,7 +980,7 @@ void mc68681_channel::tra_complete()
update_interrupts();
}
-void mc68681_channel::tra_callback()
+void duart_channel::tra_callback()
{
// don't actually send in loopback mode
if ((MR2&0xC0) != 0x80)
@@ -966,7 +1010,7 @@ void mc68681_channel::tra_callback()
}
}
-void mc68681_channel::update_interrupts()
+void duart_channel::update_interrupts()
{
if (rx_enabled)
{
@@ -1060,12 +1104,10 @@ void mc68681_channel::update_interrupts()
}
}
- m_uart->update_interrupts();
-
//logerror("DEBUG: 68681 int check: after receiver test, SR%c is %02X, ISR is %02X\n", (ch+0x41), duart68681->channel[ch].SR, duart68681->ISR);
}
-uint8_t mc68681_channel::read_rx_fifo()
+uint8_t duart_channel::read_rx_fifo()
{
uint8_t rv;
@@ -1092,7 +1134,7 @@ uint8_t mc68681_channel::read_rx_fifo()
return rv;
}
-uint8_t mc68681_channel::read_chan_reg(int reg)
+uint8_t duart_channel::read_chan_reg(int reg)
{
uint8_t rv = 0xff;
@@ -1125,7 +1167,7 @@ uint8_t mc68681_channel::read_chan_reg(int reg)
return rv;
}
-void mc68681_channel::write_chan_reg(int reg, uint8_t data)
+void duart_channel::write_chan_reg(int reg, uint8_t data)
{
switch (reg)
{
@@ -1152,7 +1194,7 @@ void mc68681_channel::write_chan_reg(int reg, uint8_t data)
}
}
-void mc68681_channel::write_MR(uint8_t data)
+void duart_channel::write_MR(uint8_t data)
{
if ( MR_ptr == 0 )
{
@@ -1167,7 +1209,7 @@ void mc68681_channel::write_MR(uint8_t data)
update_interrupts();
}
-void mc68681_channel::recalc_framing()
+void duart_channel::recalc_framing()
{
parity_t parity = PARITY_NONE;
switch ((MR1>>3) & 3)
@@ -1227,7 +1269,7 @@ void mc68681_channel::recalc_framing()
set_data_frame(1, (MR1 & 3)+5, parity, stopbits);
}
-void mc68681_channel::write_CR(uint8_t data)
+void duart_channel::write_CR(uint8_t data)
{
CR = data;
@@ -1305,7 +1347,7 @@ void mc68681_channel::write_CR(uint8_t data)
update_interrupts();
}
-void mc68681_channel::write_TX(uint8_t data)
+void duart_channel::write_TX(uint8_t data)
{
tx_data = data;
@@ -1330,12 +1372,12 @@ void mc68681_channel::write_TX(uint8_t data)
update_interrupts();
}
-void mc68681_channel::ACR_updated()
+void duart_channel::ACR_updated()
{
write_chan_reg(1, CSR);
}
-uint8_t mc68681_channel::get_chan_CSR()
+uint8_t duart_channel::get_chan_CSR()
{
return CSR;
}
diff --git a/src/devices/machine/mc68681.h b/src/devices/machine/mc68681.h
index ebc29c98fd1..452b67cb64f 100644
--- a/src/devices/machine/mc68681.h
+++ b/src/devices/machine/mc68681.h
@@ -6,30 +6,24 @@
#pragma once
-#define MCFG_MC68681_ADD(_tag, _clock) \
- MCFG_DEVICE_ADD(_tag, MC68681, _clock)
-
-#define MCFG_MC68681_REPLACE(_tag, _clock) \
- MCFG_DEVICE_REPLACE(_tag, MC68681, _clock)
-
#define MCFG_MC68681_IRQ_CALLBACK(_cb) \
- devcb = &mc68681_base_device::set_irq_cb(*device, DEVCB_##_cb);
+ devcb = &duart_base_device::set_irq_cb(*device, DEVCB_##_cb);
#define MCFG_MC68681_A_TX_CALLBACK(_cb) \
- devcb = &mc68681_base_device::set_a_tx_cb(*device, DEVCB_##_cb);
+ devcb = &duart_base_device::set_a_tx_cb(*device, DEVCB_##_cb);
#define MCFG_MC68681_B_TX_CALLBACK(_cb) \
- devcb = &mc68681_base_device::set_b_tx_cb(*device, DEVCB_##_cb);
+ devcb = &duart_base_device::set_b_tx_cb(*device, DEVCB_##_cb);
// deprecated: use ipX_w() instead
#define MCFG_MC68681_INPORT_CALLBACK(_cb) \
- devcb = &mc68681_base_device::set_inport_cb(*device, DEVCB_##_cb);
+ devcb = &duart_base_device::set_inport_cb(*device, DEVCB_##_cb);
#define MCFG_MC68681_OUTPORT_CALLBACK(_cb) \
- devcb = &mc68681_base_device::set_outport_cb(*device, DEVCB_##_cb);
+ devcb = &duart_base_device::set_outport_cb(*device, DEVCB_##_cb);
#define MCFG_MC68681_SET_EXTERNAL_CLOCKS(_a, _b, _c, _d) \
- mc68681_base_device::static_set_clocks(*device, _a, _b, _c, _d);
+ duart_base_device::static_set_clocks(*device, _a, _b, _c, _d);
// SC28C94 specific callbacks
#define MCFG_SC28C94_ADD(_tag, _clock) \
@@ -48,13 +42,13 @@
#define MC68681_RX_FIFO_SIZE 3
// forward declaration
-class mc68681_base_device;
+class duart_base_device;
-// mc68681_channel class
-class mc68681_channel : public device_t, public device_serial_interface
+// duart_channel class
+class duart_channel : public device_t, public device_serial_interface
{
public:
- mc68681_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ duart_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// device-level overrides
virtual void device_start() override;
@@ -107,7 +101,7 @@ private:
uint8_t tx_data;
uint8_t tx_ready;
- mc68681_base_device *m_uart;
+ duart_base_device *m_uart;
void write_MR(uint8_t data);
void write_CR(uint8_t data);
@@ -115,15 +109,15 @@ private:
void recalc_framing();
};
-class mc68681_base_device : public device_t
+class duart_base_device : public device_t
{
- friend class mc68681_channel;
+ friend class duart_channel;
public:
- required_device<mc68681_channel> m_chanA;
- required_device<mc68681_channel> m_chanB;
- optional_device<mc68681_channel> m_chanC;
- optional_device<mc68681_channel> m_chanD;
+ required_device<duart_channel> m_chanA;
+ required_device<duart_channel> m_chanB;
+ optional_device<duart_channel> m_chanC;
+ optional_device<duart_channel> m_chanD;
// inline configuration helpers
static void static_set_clocks(device_t &device, int clk3, int clk4, int clk5, int clk6);
@@ -131,16 +125,15 @@ public:
// API
virtual DECLARE_READ8_MEMBER(read);
virtual DECLARE_WRITE8_MEMBER(write);
- uint8_t get_irq_vector() { m_read_vector = true; return IVR; }
DECLARE_WRITE_LINE_MEMBER( rx_a_w ) { m_chanA->device_serial_interface::rx_w((uint8_t)state); }
DECLARE_WRITE_LINE_MEMBER( rx_b_w ) { m_chanB->device_serial_interface::rx_w((uint8_t)state); }
- template <class Object> static devcb_base &set_irq_cb(device_t &device, Object &&cb) { return downcast<mc68681_base_device &>(device).write_irq.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_a_tx_cb(device_t &device, Object &&cb) { return downcast<mc68681_base_device &>(device).write_a_tx.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_b_tx_cb(device_t &device, Object &&cb) { return downcast<mc68681_base_device &>(device).write_b_tx.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_inport_cb(device_t &device, Object &&cb) { return downcast<mc68681_base_device &>(device).read_inport.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_outport_cb(device_t &device, Object &&cb) { return downcast<mc68681_base_device &>(device).write_outport.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_irq_cb(device_t &device, Object &&cb) { return downcast<duart_base_device &>(device).write_irq.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_a_tx_cb(device_t &device, Object &&cb) { return downcast<duart_base_device &>(device).write_a_tx.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_b_tx_cb(device_t &device, Object &&cb) { return downcast<duart_base_device &>(device).write_b_tx.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_inport_cb(device_t &device, Object &&cb) { return downcast<duart_base_device &>(device).read_inport.set_callback(std::forward<Object>(cb)); }
+ template <class Object> static devcb_base &set_outport_cb(device_t &device, Object &&cb) { return downcast<duart_base_device &>(device).write_outport.set_callback(std::forward<Object>(cb)); }
// new-style push handlers for input port bits
DECLARE_WRITE_LINE_MEMBER( ip0_w );
@@ -149,9 +142,12 @@ public:
DECLARE_WRITE_LINE_MEMBER( ip3_w );
DECLARE_WRITE_LINE_MEMBER( ip4_w );
DECLARE_WRITE_LINE_MEMBER( ip5_w );
+ DECLARE_WRITE_LINE_MEMBER( ip6_w );
+
+ bool irq_pending() const { return (ISR & IMR) != 0; }
protected:
- mc68681_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+ duart_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
// device-level overrides
virtual void device_start() override;
@@ -163,6 +159,9 @@ protected:
devcb_write8 write_outport;
int32_t ip3clk, ip4clk, ip5clk, ip6clk;
+protected:
+ virtual void update_interrupts();
+
private:
TIMER_CALLBACK_MEMBER( duart_timer_callback );
@@ -170,14 +169,11 @@ private:
uint8_t ACR; /* Auxiliary Control Register */
uint8_t IMR; /* Interrupt Mask Register */
uint8_t ISR; /* Interrupt Status Register */
- uint8_t IVR; /* Interrupt Vector Register */
uint8_t OPCR; /* Output Port Conf. Register */
uint8_t OPR; /* Output Port Register */
PAIR CTR; /* Counter/Timer Preset Value */
uint8_t IPCR; /* Input Port Control Register */
- bool m_read_vector; // if this is read and IRQ is active, it counts as pulling IACK
-
/* state */
uint8_t IP_last_state; /* last state of IP bits */
@@ -185,15 +181,14 @@ private:
uint8_t half_period;
emu_timer *duart_timer;
- double duart68681_get_ct_rate();
- uint16_t duart68681_get_ct_count();
- void duart68681_start_ct(int count);
+ double get_ct_rate();
+ uint16_t get_ct_count();
+ void start_ct(int count);
int calc_baud(int ch, uint8_t data);
void clear_ISR_bits(int mask);
void set_ISR_bits(int mask);
- void update_interrupts();
- int get_ch(mc68681_channel *ch)
+ int get_ch(duart_channel *ch)
{
if (ch == m_chanA)
{
@@ -211,16 +206,36 @@ private:
return 3;
}
- mc68681_channel *get_channel(int chan);
+ duart_channel *get_channel(int chan);
+};
+
+class scn2681_device : public duart_base_device
+{
+public:
+ scn2681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
-class mc68681_device : public mc68681_base_device
+class mc68681_device : public duart_base_device
{
public:
mc68681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual DECLARE_READ8_MEMBER(read) override;
+ virtual DECLARE_WRITE8_MEMBER(write) override;
+ uint8_t get_irq_vector() { m_read_vector = true; return IVR; }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void update_interrupts() override;
+
+private:
+ bool m_read_vector; // if this is read and IRQ is active, it counts as pulling IACK
+
+ uint8_t IVR; /* Interrupt Vector Register */
};
-class sc28c94_device : public mc68681_base_device
+class sc28c94_device : public duart_base_device
{
public:
sc28c94_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
@@ -240,10 +255,10 @@ protected:
private:
};
-class mc68340_duart_device : public mc68681_base_device
+class mc68340_duart_device : public duart_base_device
{
public:
- mc68340_duart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ mc68340_duart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
virtual DECLARE_READ8_MEMBER(read) override;
virtual DECLARE_WRITE8_MEMBER(write) override;
@@ -253,9 +268,10 @@ protected:
mc68340_duart_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
};
+DECLARE_DEVICE_TYPE(SCN2681, scn2681_device)
DECLARE_DEVICE_TYPE(MC68681, mc68681_device)
DECLARE_DEVICE_TYPE(SC28C94, sc28c94_device)
DECLARE_DEVICE_TYPE(MC68340_DUART, mc68340_duart_device)
-DECLARE_DEVICE_TYPE(MC68681_CHANNEL, mc68681_channel)
+DECLARE_DEVICE_TYPE(DUART_CHANNEL, duart_channel)
#endif // MAME_MACHINE_MC68681_H
diff --git a/src/devices/machine/mc68901.cpp b/src/devices/machine/mc68901.cpp
index 8da39d3a4bd..02011fe447f 100644
--- a/src/devices/machine/mc68901.cpp
+++ b/src/devices/machine/mc68901.cpp
@@ -211,10 +211,6 @@ inline void mc68901_device::rx_error()
{
take_interrupt(IR_RCV_ERROR);
}
- else
- {
- rx_buffer_full();
- }
}
inline void mc68901_device::timer_count(int index)
@@ -414,7 +410,7 @@ void mc68901_device::device_start()
save_item(NAME(m_transmit_buffer));
save_item(NAME(m_transmit_pending));
save_item(NAME(m_receive_buffer));
- save_item(NAME(m_receive_pending));
+ save_item(NAME(m_overrun_pending));
save_item(NAME(m_gpio_input));
save_item(NAME(m_gpio_output));
save_item(NAME(m_rsr_read));
@@ -429,7 +425,8 @@ void mc68901_device::device_start()
void mc68901_device::device_reset()
{
m_tsr = 0;
- m_transmit_pending = 0;
+ m_transmit_pending = false;
+ m_overrun_pending = false;
// Avoid read-before-write
m_ipr = m_imr = 0;
@@ -496,7 +493,7 @@ void mc68901_device::tra_complete()
if (m_transmit_pending)
{
transmit_register_setup(m_transmit_buffer);
- m_transmit_pending = 0;
+ m_transmit_pending = false;
m_tsr |= TSR_BUFFER_EMPTY;
if (m_ier & IR_XMIT_BUFFER_EMPTY)
@@ -524,10 +521,16 @@ void mc68901_device::tra_complete()
void mc68901_device::rcv_complete()
{
receive_register_extract();
- m_receive_buffer = get_received_char();
- //if (m_receive_pending) TODO: error?
-
- m_receive_pending = 1;
+ if (m_rsr & RSR_BUFFER_FULL)
+ {
+ m_overrun_pending = true;
+ }
+ else
+ {
+ m_receive_buffer = get_received_char();
+ m_rsr |= RSR_BUFFER_FULL;
+ LOG("Received Character: %02x\n", m_receive_buffer);
+ }
rx_buffer_full();
}
@@ -565,19 +568,34 @@ READ8_MEMBER( mc68901_device::read )
case REGISTER_SCR: return m_scr;
case REGISTER_UCR: return m_ucr;
- case REGISTER_RSR: return m_rsr;
+ case REGISTER_RSR:
+ {
+ uint8_t rsr = m_rsr;
+ if (!machine().side_effect_disabled())
+ m_rsr &= ~RSR_OVERRUN_ERROR;
+ return rsr;
+ }
case REGISTER_TSR:
{
/* clear UE bit (in reality, this won't be cleared until one full clock cycle of the transmitter has passed since the bit was set) */
uint8_t tsr = m_tsr;
- m_tsr &= ~TSR_UNDERRUN_ERROR;
-
+ if (!machine().side_effect_disabled())
+ m_tsr &= ~TSR_UNDERRUN_ERROR;
return tsr;
}
case REGISTER_UDR:
- m_receive_pending = 0;
+ if (!machine().side_effect_disabled())
+ {
+ m_rsr &= ~RSR_BUFFER_FULL;
+ if (m_overrun_pending)
+ {
+ m_overrun_pending = false;
+ m_rsr |= RSR_OVERRUN_ERROR;
+ rx_error();
+ }
+ }
return m_receive_buffer;
default: return 0;
@@ -966,6 +984,7 @@ void mc68901_device::register_w(offs_t offset, uint8_t data)
}
set_data_frame(start_bits, data_bit_count, parity, stop_bits);
+ receive_register_reset();
m_ucr = data;
}
@@ -1052,22 +1071,23 @@ void mc68901_device::register_w(offs_t offset, uint8_t data)
if (m_transmit_pending && is_transmit_register_empty())
{
transmit_register_setup(m_transmit_buffer);
- m_transmit_pending = 0;
- m_tsr |= TSR_BUFFER_EMPTY;
+ m_transmit_pending = false;
}
+ if (!m_transmit_pending)
+ m_tsr |= TSR_BUFFER_EMPTY;
}
break;
case REGISTER_UDR:
LOG("MC68901 UDR %x\n", data);
m_transmit_buffer = data;
- m_transmit_pending = 1;
+ m_transmit_pending = true;
m_tsr &= ~TSR_BUFFER_EMPTY;
if ((m_tsr & TSR_XMIT_ENABLE) && is_transmit_register_empty())
{
transmit_register_setup(m_transmit_buffer);
- m_transmit_pending = 0;
+ m_transmit_pending = false;
m_tsr |= TSR_BUFFER_EMPTY;
}
break;
diff --git a/src/devices/machine/mc68901.h b/src/devices/machine/mc68901.h
index b98d2de8e5c..cccd257b2ee 100644
--- a/src/devices/machine/mc68901.h
+++ b/src/devices/machine/mc68901.h
@@ -279,9 +279,9 @@ private:
uint8_t m_tsr; /* transmitter status register */
uint8_t m_rsr; /* receiver status register */
uint8_t m_transmit_buffer; /* USART data register */
- int m_transmit_pending;
+ bool m_transmit_pending;
uint8_t m_receive_buffer;
- int m_receive_pending;
+ bool m_overrun_pending;
uint8_t m_gpio_input;
uint8_t m_gpio_output;
diff --git a/src/devices/machine/netlist.cpp b/src/devices/machine/netlist.cpp
index 60c814ee45e..30ef04433f2 100644
--- a/src/devices/machine/netlist.cpp
+++ b/src/devices/machine/netlist.cpp
@@ -1021,7 +1021,7 @@ void netlist_mame_cpu_device::device_start()
}
else
{
- state_add(i*2+1, n->name().c_str(), *downcast<netlist::analog_net_t *>(n)->Q_Analog_state_ptr()).formatstr("%20s");
+ state_add(i*2+1, n->name().c_str(), *downcast<netlist::analog_net_t *>(n)->Q_Analog_state_ptr());
}
}
@@ -1045,23 +1045,6 @@ ATTR_COLD uint64_t netlist_mame_cpu_device::execute_cycles_to_clocks(uint64_t cy
return cycles;
}
-ATTR_COLD offs_t netlist_mame_cpu_device::disasm_disassemble(std::ostream &stream, offs_t pc, const uint8_t *oprom, const uint8_t *opram, uint32_t options)
-{
- //char tmp[16];
- unsigned startpc = pc;
- int relpc = pc - m_genPC;
- if (relpc >= 0 && relpc < netlist().queue().size())
- {
- int dpc = netlist().queue().size() - relpc - 1;
- // FIXME: 50 below fixes crash in mame-debugger. It's based on try on error.
- util::stream_format(stream, "%c %s @%10.7f", (relpc == 0) ? '*' : ' ', netlist().queue()[dpc].m_object->name().c_str(),
- netlist().queue()[dpc].m_exec_time.as_double());
- }
-
- pc+=1;
- return (pc - startpc);
-}
-
ATTR_HOT void netlist_mame_cpu_device::execute_run()
{
bool check_debugger = ((device_t::machine().debug_flags & DEBUG_FLAG_ENABLED) != 0);
@@ -1085,6 +1068,35 @@ ATTR_HOT void netlist_mame_cpu_device::execute_run()
}
}
+util::disasm_interface *netlist_mame_cpu_device::create_disassembler()
+{
+ return new netlist_disassembler(this);
+}
+
+netlist_disassembler::netlist_disassembler(netlist_mame_cpu_device *dev) : m_dev(dev)
+{
+}
+
+u32 netlist_disassembler::opcode_alignment() const
+{
+ return 1;
+}
+
+offs_t netlist_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
+{
+ unsigned startpc = pc;
+ int relpc = pc - m_dev->genPC();
+ if (relpc >= 0 && relpc < m_dev->netlist().queue().size())
+ {
+ int dpc = m_dev->netlist().queue().size() - relpc - 1;
+ util::stream_format(stream, "%c %s @%10.7f", (relpc == 0) ? '*' : ' ', m_dev->netlist().queue()[dpc].m_object->name().c_str(),
+ m_dev->netlist().queue()[dpc].m_exec_time.as_double());
+ }
+
+ pc+=1;
+ return (pc - startpc);
+}
+
// ----------------------------------------------------------------------------------------
// netlist_mame_sound_device
// ----------------------------------------------------------------------------------------
diff --git a/src/devices/machine/netlist.h b/src/devices/machine/netlist.h
index bf6fd084056..18a13d31f41 100644
--- a/src/devices/machine/netlist.h
+++ b/src/devices/machine/netlist.h
@@ -151,6 +151,21 @@ private:
// netlist_mame_cpu_device
// ----------------------------------------------------------------------------------------
+class netlist_mame_cpu_device;
+
+class netlist_disassembler : public util::disasm_interface
+{
+public:
+ netlist_disassembler(netlist_mame_cpu_device *dev);
+ virtual ~netlist_disassembler() = default;
+
+ virtual u32 opcode_alignment() const override;
+ virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
+
+private:
+ netlist_mame_cpu_device *m_dev;
+};
+
class netlist_mame_cpu_device : public netlist_mame_device,
public device_execute_interface,
public device_state_interface,
@@ -161,6 +176,8 @@ public:
// construction/destruction
netlist_mame_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ offs_t genPC() const { return m_genPC; }
+
protected:
// netlist_mame_device
virtual void nl_register_devices() override;
@@ -175,9 +192,7 @@ protected:
ATTR_HOT virtual void execute_run() override;
// device_disasm_interface overrides
- ATTR_COLD virtual uint32_t disasm_min_opcode_bytes() const override { return 1; }
- ATTR_COLD virtual uint32_t disasm_max_opcode_bytes() const override { return 1; }
- ATTR_COLD virtual offs_t disasm_disassemble(std::ostream &stream, offs_t pc, const uint8_t *oprom, const uint8_t *opram, uint32_t options) override;
+ virtual util::disasm_interface *create_disassembler() override;
// device_memory_interface overrides
virtual space_config_vector memory_space_config() const override;
@@ -188,7 +203,7 @@ protected:
address_space_config m_program_config;
private:
- int m_genPC;
+ offs_t m_genPC;
};
// ----------------------------------------------------------------------------------------
diff --git a/src/devices/machine/phi.cpp b/src/devices/machine/phi.cpp
index 42cede7e6d8..e86a7bb72ce 100644
--- a/src/devices/machine/phi.cpp
+++ b/src/devices/machine/phi.cpp
@@ -1246,7 +1246,7 @@ bool phi_device::if_cmd_received(uint8_t byte)
}
} else {
// command is a secondary address
- if (m_t_state == PHI_T_ID1 && my_addr) {
+ if (m_t_state == PHI_T_ID1 && (m_l_state == PHI_L_LADS) == !!lon_msg() && my_addr) {
// Start IDENTIFY sequence
m_t_state = PHI_T_ID2;
} else if (m_t_state >= PHI_T_ID2 && m_t_state <= PHI_T_ID6 && !my_addr) {
diff --git a/src/devices/machine/sdlc.cpp b/src/devices/machine/sdlc.cpp
new file mode 100644
index 00000000000..e3cc8cc99ed
--- /dev/null
+++ b/src/devices/machine/sdlc.cpp
@@ -0,0 +1,302 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+#include "emu.h"
+#include "sdlc.h"
+
+#include <sstream>
+
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_RXBIT (1U << 1)
+#define LOG_RXFLAG (1U << 2)
+#define LOG_LINESTATE (1U << 3)
+#define LOG_FRAMING (1U << 4)
+
+//#define VERBOSE (LOG_GENERAL | LOG_RXBIT | LOG_RXFLAG | LOG_LINESTATE | LOG_FRAMING)
+#include "logmacro.h"
+
+#define LOGRXBIT(...) LOGMASKED(LOG_RXBIT, __VA_ARGS__)
+#define LOGRXFLAG(...) LOGMASKED(LOG_RXFLAG, __VA_ARGS__)
+#define LOGLINESTATE(...) LOGMASKED(LOG_LINESTATE, __VA_ARGS__)
+#define LOGFRAMING(...) LOGMASKED(LOG_FRAMING, __VA_ARGS__)
+
+
+DEFINE_DEVICE_TYPE(SDLC_LOGGER, sdlc_logger_device, "sdlc_logger", "SDLC/HDLC logger")
+
+
+constexpr std::uint16_t device_sdlc_consumer_interface::POLY_SDLC;
+
+
+device_sdlc_consumer_interface::device_sdlc_consumer_interface(machine_config const &mconfig, device_t &device) :
+ device_interface(device, "sdlc_consumer"),
+ m_line_active(0U),
+ m_discard_bits(0U),
+ m_in_frame(0U),
+ m_shift_register(0xffffU),
+ m_frame_check(0xffffU)
+{
+}
+
+void device_sdlc_consumer_interface::interface_post_start()
+{
+ device().save_item(NAME(m_line_active));
+ device().save_item(NAME(m_discard_bits));
+ device().save_item(NAME(m_in_frame));
+ device().save_item(NAME(m_shift_register));
+ device().save_item(NAME(m_frame_check));
+}
+
+void device_sdlc_consumer_interface::rx_bit(bool state)
+{
+ LOGRXBIT("Received bit %u\n", state ? 1U : 0U);
+
+ m_shift_register = (m_shift_register >> 1) | (state ? 0x8000U : 0x0000U);
+ if (!state && !m_line_active)
+ {
+ // any zero bit means the line has become active
+ LOGLINESTATE("Line became active\n");
+ m_line_active = 1U;
+ line_active();
+ }
+
+ if ((m_shift_register & 0xff00U) == 0x7e00U)
+ {
+ // a flag opens and closes frames
+ LOGRXFLAG("Received flag\n");
+ if (m_in_frame)
+ {
+ LOGFRAMING("End of frame\n");
+ m_in_frame = 0U;
+ frame_end();
+ }
+ m_discard_bits = 8U;
+ m_frame_check = 0xffffU;
+ }
+ else if ((m_shift_register & 0xfffeU) == 0xfffeU)
+ {
+ // fifteen consecutive ones is an inactive line condition
+ if (m_line_active)
+ {
+ LOGLINESTATE("Line became inactive\n");
+ m_line_active = 0U;
+ line_inactive();
+ }
+ }
+ else if ((m_shift_register & 0xfe00U) == 0xfe00U)
+ {
+ // seven consecutive ones is a frame abort
+ if (m_in_frame || m_discard_bits)
+ {
+ LOGFRAMING("Received frame abort\n");
+ m_in_frame = 0U;
+ m_discard_bits = 0U;
+ frame_abort();
+ }
+ }
+ else
+ {
+ // discard the flag as it shifts off
+ if (m_discard_bits && !--m_discard_bits)
+ {
+ LOGFRAMING("Start of frame\n");
+ m_in_frame = 1U;
+ frame_start();
+ }
+
+ // discard a zero after five consecutive ones
+ if (m_in_frame && ((m_shift_register & 0x01f8U) != 0x00f8U))
+ {
+ bool const bit(BIT(m_shift_register, 8));
+ m_frame_check = update_frame_check(POLY_SDLC, m_frame_check, bit);
+ data_bit(bit);
+ }
+ }
+}
+
+void device_sdlc_consumer_interface::rx_reset()
+{
+ LOG("Receive reset\n");
+
+ m_line_active = 0U;
+ m_in_frame = 0U;
+ m_discard_bits = 0U;
+ m_shift_register = 0xffffU;
+ m_frame_check = 0xffffU;
+}
+
+
+sdlc_logger_device::sdlc_logger_device(machine_config const &mconfig, char const *tag, device_t *owner, std::uint32_t clock) :
+ device_t(mconfig, SDLC_LOGGER, tag, owner, clock),
+ device_sdlc_consumer_interface(mconfig, *this),
+ m_data_nrzi(0U),
+ m_clock_active(1U),
+ m_current_data(1U),
+ m_last_data(1U),
+ m_current_clock(1U),
+ m_frame_bits(0U),
+ m_expected_fcs(0U),
+ m_buffer()
+{
+}
+
+WRITE_LINE_MEMBER(sdlc_logger_device::clock_w)
+{
+ if (bool(state) != bool(m_current_clock))
+ {
+ m_current_clock = state ? 1U : 0U;
+ if (m_current_clock == m_clock_active)
+ {
+ bool const bit(m_data_nrzi ? (m_current_data == m_last_data) : m_current_data);
+ LOGRXBIT("Received bit: %u (%u -> %u)\n", bit ? 1U : 0U, m_last_data, m_current_data);
+ m_last_data = m_current_data;
+ rx_bit(bit);
+ }
+ }
+}
+
+void sdlc_logger_device::device_start()
+{
+ m_buffer.reset(new std::uint8_t[BUFFER_BYTES]);
+
+ save_item(NAME(m_data_nrzi));
+ save_item(NAME(m_clock_active));
+ save_item(NAME(m_current_data));
+ save_item(NAME(m_last_data));
+ save_item(NAME(m_current_clock));
+ save_item(NAME(m_frame_bits));
+ save_item(NAME(m_expected_fcs));
+ save_pointer(NAME(m_buffer.get()), BUFFER_BYTES);
+}
+
+void sdlc_logger_device::device_reset()
+{
+}
+
+void sdlc_logger_device::frame_start()
+{
+ m_frame_bits = 0U;
+ m_expected_fcs = 0xffffU;
+}
+
+void sdlc_logger_device::frame_end()
+{
+ shift_residual_bits();
+ log_frame(false);
+ m_frame_bits = 0;
+}
+
+void sdlc_logger_device::frame_abort()
+{
+ logerror("Frame aborted!\n");
+ shift_residual_bits();
+ log_frame(true);
+ m_frame_bits = 0U;
+}
+
+void sdlc_logger_device::data_bit(bool value)
+{
+ if (BUFFER_BITS > m_frame_bits)
+ {
+ m_buffer[m_frame_bits >> 3] >>= 1;
+ m_buffer[m_frame_bits >> 3] |= value ? 0x80U : 0x00U;
+ }
+ else if (BUFFER_BITS == m_frame_bits)
+ {
+ logerror("Frame buffer overrun!\n");
+ }
+
+ if ((16U <= m_frame_bits) && ((BUFFER_BITS + 16U) > m_frame_bits))
+ m_expected_fcs = update_frame_check(POLY_SDLC, m_expected_fcs, BIT(m_buffer[(m_frame_bits - 16U) >> 3], m_frame_bits & 0x0007U));
+
+ ++m_frame_bits;
+}
+
+void sdlc_logger_device::shift_residual_bits()
+{
+ if (BUFFER_BITS > m_frame_bits)
+ {
+ uint32_t const residual_bits(m_frame_bits & 0x0007U);
+ if (residual_bits)
+ m_buffer[m_frame_bits >> 3] >>= 8 - residual_bits;
+ }
+}
+
+void sdlc_logger_device::log_frame(bool partial) const
+{
+ if (m_frame_bits)
+ {
+ std::ostringstream msg;
+ std::uint32_t const frame_bytes(m_frame_bits >> 3);
+ std::uint32_t const residual_bits(m_frame_bits & 0x0007U);
+ util::stream_format(msg, "Received %u-bit %sframe (%u bytes + %u bits)", m_frame_bits, partial ? "partial " : "", frame_bytes, residual_bits);
+
+ if (8U <= m_frame_bits)
+ {
+ std::uint8_t const addr(m_buffer[0]);
+ util::stream_format(msg, " A=%02X%s", addr, (0xffU == addr) ? " (broadcast)" : !addr ? " (no station)" : "");
+ }
+
+ if (16U <= m_frame_bits)
+ {
+ std::uint8_t const ctrl(m_buffer[1]);
+ if (!BIT(ctrl, 0))
+ {
+ msg << " I";
+ }
+ else if (!BIT(ctrl, 1))
+ {
+ msg << " S";
+ switch (ctrl & 0x0cU)
+ {
+ case 0x00U: msg << " RR"; break;
+ case 0x04U: msg << " RNR"; break;
+ case 0x08U: msg << " REJ"; break;
+ }
+ }
+ else
+ {
+ msg << " U";
+ switch (ctrl & 0xecU)
+ {
+ case 0x00U: msg << " UI"; break;
+ case 0x04U: msg << " RIM/SIM"; break;
+ case 0x0cU: msg << " DM"; break;
+ case 0x20U: msg << " UP"; break;
+ case 0x40U: msg << " DISC/RD"; break;
+ case 0x60U: msg << " UA"; break;
+ case 0x80U: msg << " SNRM"; break;
+ case 0x84U: msg << " FRMR"; break;
+ case 0x9cU: msg << " XID"; break;
+ case 0xc4U: msg << " CFGR"; break;
+ case 0xccU: msg << " SNRME"; break;
+ case 0xe0U: msg << " TEST"; break;
+ case 0xecU: msg << " BCN"; break;
+ }
+ }
+
+ if (!partial && (BUFFER_BITS >= m_frame_bits))
+ {
+ std::uint16_t fcs;
+ fcs = std::uint16_t(m_buffer[frame_bytes - 2]) >> residual_bits;
+ fcs |= std::uint16_t(m_buffer[frame_bytes - 1]) << (8 - residual_bits);
+ if (residual_bits)
+ fcs |= (std::uint16_t(m_buffer[frame_bytes]) & ((1U << residual_bits) - 1U)) << (16 - residual_bits);
+ fcs = ~BITSWAP16(fcs, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
+ util::stream_format(msg, " FCS=%04X", fcs);
+ if (!is_frame_check_good())
+ util::stream_format(msg, " (expected %04X)", m_expected_fcs);
+ }
+ }
+
+ if (!partial)
+ msg << (is_frame_check_good() ? " (good)" : " (bad)");
+
+ for (std::uint32_t i = 0U; (frame_bytes > i) && (BUFFER_BYTES > i); ++i)
+ util::stream_format(msg, (i & 0x000fU) ? " %02X" : "\n %02X", m_buffer[i]);
+ if (residual_bits && (BUFFER_BITS >= m_frame_bits))
+ util::stream_format(msg, (residual_bits > 4) ? "%s %02X&%02X" : "%s %01X&%01X", (frame_bytes & 0x000fU) ? "" : "\n ", m_buffer[frame_bytes], (1U << residual_bits) - 1);
+
+ logerror("%s\n", msg.str());
+ }
+}
diff --git a/src/devices/machine/sdlc.h b/src/devices/machine/sdlc.h
new file mode 100644
index 00000000000..1b9b8efa832
--- /dev/null
+++ b/src/devices/machine/sdlc.h
@@ -0,0 +1,116 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+#ifndef MAME_MACHINE_SDLC_H
+#define MAME_MACHINE_SDLC_H
+
+#pragma once
+
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+
+#define MCFG_SDLC_LOGGER_DATA_NRZL \
+ downcast<sdlc_logger_device &>(*device).clock_active(0);
+
+#define MCFG_SDLC_LOGGER_DATA_NRZI \
+ downcast<sdlc_logger_device &>(*device).clock_active(1);
+
+#define MCFG_SDLC_LOGGER_CLOCK_ACTIVE_RISING \
+ downcast<sdlc_logger_device &>(*device).clock_active(1);
+
+#define MCFG_SDLC_LOGGER_CLOCK_ACTIVE_FALLING \
+ downcast<sdlc_logger_device &>(*device).clock_active(0);
+
+
+class device_sdlc_consumer_interface : public device_interface
+{
+public:
+ static constexpr std::uint16_t POLY_SDLC = 0x1021U;
+
+ template <typename T>
+ static constexpr u16 update_frame_check(u16 poly, u16 current, T bit)
+ {
+ return (current << 1) ^ ((bool(bit) != bool(BIT(current, 15))) ? poly : 0U);
+ }
+
+protected:
+ device_sdlc_consumer_interface(machine_config const &mconfig, device_t &device);
+
+ virtual void interface_post_start() override;
+
+ void rx_bit(bool state);
+ void rx_reset();
+
+ bool is_line_active() const { return bool(m_line_active); }
+ uint16_t get_frame_check() const { return m_frame_check; }
+ bool is_frame_check_good() const { return 0x1d0fU == m_frame_check; }
+
+private:
+ template <typename... Params> void logerror(Params &&... args) { device().logerror(std::forward<Params>(args)...); }
+
+ virtual void frame_start() { }
+ virtual void frame_end() { }
+ virtual void frame_abort() { }
+ virtual void line_active() { }
+ virtual void line_inactive() { }
+ virtual void data_bit(bool value) { }
+
+ std::uint8_t m_line_active;
+ std::uint8_t m_discard_bits;
+ std::uint8_t m_in_frame;
+ std::uint16_t m_shift_register;
+ std::uint16_t m_frame_check;
+};
+
+
+class sdlc_logger_device : public device_t, public device_sdlc_consumer_interface
+{
+public:
+ sdlc_logger_device(machine_config const &mconfig, char const *tag, device_t *owner, std::uint32_t clock);
+
+ // input signals
+ DECLARE_WRITE_LINE_MEMBER(data_w) { m_current_data = state ? 1U : 0U; }
+ DECLARE_WRITE_LINE_MEMBER(clock_w);
+
+ // input format configuration
+ DECLARE_WRITE_LINE_MEMBER(data_nrzi) { m_data_nrzi = state ? 1U : 0U; }
+ DECLARE_WRITE_LINE_MEMBER(clock_active) { m_clock_active = state ? 1U : 0U; }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ using device_t::logerror;
+
+private:
+ enum : std::size_t
+ {
+ BUFFER_BYTES = 1024U,
+ BUFFER_BITS = BUFFER_BYTES << 3
+ };
+
+ virtual void frame_start() override;
+ virtual void frame_end() override;
+ virtual void frame_abort() override;
+ virtual void data_bit(bool value) override;
+
+ void shift_residual_bits();
+ void log_frame(bool partial) const;
+
+ std::uint8_t m_data_nrzi;
+ std::uint8_t m_clock_active;
+
+ std::uint8_t m_current_data;
+ std::uint8_t m_last_data;
+ std::uint8_t m_current_clock;
+
+ std::uint32_t m_frame_bits;
+ std::uint16_t m_expected_fcs;
+ std::unique_ptr<std::uint8_t []> m_buffer;
+};
+
+
+DECLARE_DEVICE_TYPE(SDLC_LOGGER, sdlc_logger_device)
+
+#endif // MAME_MACHINE_SDLC_H
diff --git a/src/devices/machine/sega_scu.cpp b/src/devices/machine/sega_scu.cpp
new file mode 100644
index 00000000000..71ed1f03e04
--- /dev/null
+++ b/src/devices/machine/sega_scu.cpp
@@ -0,0 +1,808 @@
+// license:LGPL-2.1+
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+ Sega System Control Unit (c) 1995 Sega
+
+ TODO:
+ - make a screen_device subclass, add pixel timers & irq callbacks to it;
+ - A-Bus external interrupts;
+ - Pad signal (lightgun I presume);
+ - Improve DMA, use DRQ model and timers, make them subdevices?
+ (old DMA TODO)
+ - Remove CD transfer DMA hack (tied with CD block bug(s)?)
+ - Add timings(but how fast are each DMA?).
+ - Add level priority & DMA status register.
+
+***************************************************************************/
+/**********************************************************************************
+SCU Register Table
+offset,relative address
+Registers are in long words.
+===================================================================================
+0 0000 Level 0 DMA Set Register
+1 0004
+2 0008
+3 000c
+4 0010
+5 0014
+6 0018
+7 001c
+8 0020 Level 1 DMA Set Register
+9 0024
+10 0028
+11 002c
+12 0030
+13 0034
+14 0038
+15 003c
+16 0040 Level 2 DMA Set Register
+17 0044
+18 0048
+19 004c
+20 0050
+21 0054
+22 0058
+23 005c
+24 0060 DMA Forced Stop
+25 0064
+26 0068
+27 006c
+28 0070 <Free>
+29 0074
+30 0078
+31 007c DMA Status Register
+32 0080 DSP Program Control Port
+33 0084 DSP Program RAM Data Port
+34 0088 DSP Data RAM Address Port
+35 008c DSP Data RAM Data Port
+36 0090 Timer 0 Compare Register
+37 0094 Timer 1 Set Data Register
+38 0098 Timer 1 Mode Register
+39 009c <Free>
+40 00a0 Interrupt Mask Register
+41 00a4 Interrupt Status Register
+42 00a8 A-Bus Interrupt Acknowledge
+43 00ac <Free>
+44 00b0 A-Bus Set Register
+45 00b4
+46 00b8 A-Bus Refresh Register
+47 00bc <Free>
+48 00c0
+49 00c4 SCU SDRAM Select Register
+50 00c8 SCU Version Register
+51 00cc <Free>
+52 00cf
+===================================================================================
+DMA Status Register(32-bit):
+xxxx xxxx x--- xx-- xx-- xx-- xx-- xx-- UNUSED
+---- ---- -x-- ---- ---- ---- ---- ---- DMA DSP-Bus access
+---- ---- --x- ---- ---- ---- ---- ---- DMA B-Bus access
+---- ---- ---x ---- ---- ---- ---- ---- DMA A-Bus access
+---- ---- ---- --x- ---- ---- ---- ---- DMA lv 1 interrupt
+---- ---- ---- ---x ---- ---- ---- ---- DMA lv 0 interrupt
+---- ---- ---- ---- --x- ---- ---- ---- DMA lv 2 in stand-by
+---- ---- ---- ---- ---x ---- ---- ---- DMA lv 2 in operation
+---- ---- ---- ---- ---- --x- ---- ---- DMA lv 1 in stand-by
+---- ---- ---- ---- ---- ---x ---- ---- DMA lv 1 in operation
+---- ---- ---- ---- ---- ---- --x- ---- DMA lv 0 in stand-by
+---- ---- ---- ---- ---- ---- ---x ---- DMA lv 0 in operation
+---- ---- ---- ---- ---- ---- ---- --x- DSP side DMA in stand-by
+---- ---- ---- ---- ---- ---- ---- ---x DSP side DMA in operation
+
+**********************************************************************************/
+
+#include "emu.h"
+#include "sega_scu.h"
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+DEFINE_DEVICE_TYPE(SEGA_SCU, sega_scu_device, "sega_scu", "Sega System Control Unit")
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//AM_RANGE(0x0000, 0x0003) src
+//AM_RANGE(0x0004, 0x0007) dst
+//AM_RANGE(0x0008, 0x000b) size
+//AM_RANGE(0x000c, 0x000f) src/dst add values
+//AM_RANGE(0x0010, 0x0013) DMA enable
+//AM_RANGE(0x0014, 0x0017) DMA start factor
+
+DEVICE_ADDRESS_MAP_START( regs_map, 32, sega_scu_device )
+ AM_RANGE(0x0000, 0x0017) AM_READWRITE(dma_lv0_r,dma_lv0_w)
+ AM_RANGE(0x0020, 0x0037) AM_READWRITE(dma_lv1_r,dma_lv1_w)
+ AM_RANGE(0x0040, 0x0057) AM_READWRITE(dma_lv2_r,dma_lv2_w)
+ // Super Major League and Shin Megami Tensei - Akuma Zensho reads from there (undocumented), DMA status mirror?
+ AM_RANGE(0x005c, 0x005f) AM_READ(dma_status_r)
+// AM_RANGE(0x0060, 0x0063) AM_WRITE(dma_force_stop_w)
+ AM_RANGE(0x007c, 0x007f) AM_READ(dma_status_r)
+ AM_RANGE(0x0080, 0x0083) AM_DEVREADWRITE("scudsp",scudsp_cpu_device, program_control_r,program_control_w)
+ AM_RANGE(0x0084, 0x0087) AM_DEVWRITE("scudsp", scudsp_cpu_device, program_w)
+ AM_RANGE(0x0088, 0x008b) AM_DEVWRITE("scudsp", scudsp_cpu_device, ram_address_control_w)
+ AM_RANGE(0x008c, 0x008f) AM_DEVREADWRITE("scudsp", scudsp_cpu_device, ram_address_r, ram_address_w)
+ AM_RANGE(0x0090, 0x0093) AM_WRITE(t0_compare_w)
+ AM_RANGE(0x0094, 0x0097) AM_WRITE(t1_setdata_w)
+ AM_RANGE(0x0098, 0x009b) AM_WRITE16(t1_mode_w,0x0000ffff)
+ AM_RANGE(0x00a0, 0x00a3) AM_READWRITE(irq_mask_r,irq_mask_w)
+ AM_RANGE(0x00a4, 0x00a7) AM_READWRITE(irq_status_r,irq_status_w)
+// AM_RANGE(0x00a8, 0x00ab) AM_WRITE(abus_irqack_w)
+// AM_RANGE(0x00b0, 0x00b7) AM_READWRITE(abus_set_r,abus_set_w)
+// AM_RANGE(0x00b8, 0x00bb) AM_READWRITE(abus_refresh_r,abus_refresh_w)
+// AM_RANGE(0x00c4, 0x00c7) AM_READWRITE(sdram_r,sdram_w)
+ AM_RANGE(0x00c8, 0x00cb) AM_READ(version_r) // returns 4 for stock Saturn
+ADDRESS_MAP_END
+
+//-------------------------------------------------
+// sega_scu_device - constructor
+//-------------------------------------------------
+
+sega_scu_device::sega_scu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, SEGA_SCU, tag, owner, clock),
+ m_scudsp(*this, "scudsp")
+{
+}
+
+void sega_scu_device::static_set_hostcpu(device_t &device, const char *cputag)
+{
+ sega_scu_device &dev = downcast<sega_scu_device &>(device);
+ dev.m_hostcpu_tag = cputag;
+}
+
+
+//-------------------------------------------------
+// add_device_mconfig - device-specific machine
+// configuration addiitons
+//-------------------------------------------------
+
+READ16_MEMBER(sega_scu_device::scudsp_dma_r)
+{
+ //address_space &program = m_maincpu->space(AS_PROGRAM);
+ offs_t addr = offset;
+
+// printf("%08x\n",addr);
+
+ return m_hostspace->read_word(addr,mem_mask);
+}
+
+
+WRITE16_MEMBER(sega_scu_device::scudsp_dma_w)
+{
+ //address_space &program = m_maincpu->space(AS_PROGRAM);
+ offs_t addr = offset;
+
+// printf("%08x %02x\n",addr,data);
+
+ m_hostspace->write_word(addr, data,mem_mask);
+}
+
+MACHINE_CONFIG_MEMBER(sega_scu_device::device_add_mconfig)
+ MCFG_CPU_ADD("scudsp", SCUDSP, XTAL_57_2727MHz/4) // 14 MHz
+ MCFG_SCUDSP_OUT_IRQ_CB(DEVWRITELINE(DEVICE_SELF, sega_scu_device, scudsp_end_w))
+ MCFG_SCUDSP_IN_DMA_CB(READ16(sega_scu_device, scudsp_dma_r))
+ MCFG_SCUDSP_OUT_DMA_CB(WRITE16(sega_scu_device, scudsp_dma_w))
+MACHINE_CONFIG_END
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void sega_scu_device::device_start()
+{
+ save_item(NAME(m_ist));
+ save_item(NAME(m_ism));
+ save_item(NAME(m_t0c));
+ save_item(NAME(m_t1s));
+ save_item(NAME(m_t1md));
+
+ save_item(NAME(m_dma[0].src));
+ save_item(NAME(m_dma[0].dst));
+ save_item(NAME(m_dma[0].src_add));
+ save_item(NAME(m_dma[0].dst_add));
+ save_item(NAME(m_dma[0].size));
+ save_item(NAME(m_dma[0].index));
+ save_item(NAME(m_dma[0].start_factor));
+ save_item(NAME(m_dma[0].enable_mask));
+ save_item(NAME(m_dma[0].indirect_mode));
+ save_item(NAME(m_dma[0].rup));
+ save_item(NAME(m_dma[0].wup));
+ save_item(NAME(m_dma[1].src));
+ save_item(NAME(m_dma[1].dst));
+ save_item(NAME(m_dma[1].src_add));
+ save_item(NAME(m_dma[1].dst_add));
+ save_item(NAME(m_dma[1].size));
+ save_item(NAME(m_dma[1].index));
+ save_item(NAME(m_dma[1].start_factor));
+ save_item(NAME(m_dma[1].enable_mask));
+ save_item(NAME(m_dma[1].indirect_mode));
+ save_item(NAME(m_dma[1].rup));
+ save_item(NAME(m_dma[1].wup));
+ save_item(NAME(m_dma[2].src));
+ save_item(NAME(m_dma[2].dst));
+ save_item(NAME(m_dma[2].src_add));
+ save_item(NAME(m_dma[2].dst_add));
+ save_item(NAME(m_dma[2].size));
+ save_item(NAME(m_dma[2].index));
+ save_item(NAME(m_dma[2].start_factor));
+ save_item(NAME(m_dma[2].enable_mask));
+ save_item(NAME(m_dma[2].indirect_mode));
+ save_item(NAME(m_dma[2].rup));
+ save_item(NAME(m_dma[2].wup));
+
+ m_hostcpu = machine().device<sh2_device>(m_hostcpu_tag);
+ m_hostspace = &m_hostcpu->space(AS_PROGRAM);
+
+ m_dma_timer[0] = timer_alloc(DMALV0_ID);
+ m_dma_timer[1] = timer_alloc(DMALV1_ID);
+ m_dma_timer[2] = timer_alloc(DMALV2_ID);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void sega_scu_device::device_reset()
+{
+ m_ism = 0xbfff;
+ m_ist = 0;
+
+ for(int i=0;i<3;i++)
+ {
+ m_dma[i].start_factor = 7;
+ m_dma_timer[i]->reset();
+ }
+
+ m_status = 0;
+
+}
+
+//-------------------------------------------------
+// device_reset_after_children
+//-------------------------------------------------
+
+void sega_scu_device::device_reset_after_children()
+{
+ m_scudsp->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
+}
+
+
+void sega_scu_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+
+ switch(id)
+ {
+ case DMALV0_ID:
+ case DMALV1_ID:
+ case DMALV2_ID:
+ {
+ const int irqlevel = id == 0 ? 5 : 6;
+ const int irqvector = 0x4b - id;
+ const uint16_t irqmask = 1 << (11-id);
+
+ if(!(m_ism & irqmask))
+ m_hostcpu->set_input_line_and_vector(irqlevel, HOLD_LINE, irqvector);
+ else
+ m_ist |= (irqmask);
+
+ update_dma_status(id,false);
+ machine().scheduler().synchronize(); // force resync
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+//**************************************************************************
+// DMA
+//**************************************************************************
+
+uint32_t sega_scu_device::dma_common_r(uint8_t offset,uint8_t level)
+{
+ switch(offset)
+ {
+ case 0x00/4: // source
+ return m_dma[level].src;
+ case 0x04/4: // destination
+ return m_dma[level].dst;
+ case 0x08/4: // size
+ return m_dma[level].size;
+ case 0x0c/4:
+ return 0; // DxAD, write only
+ case 0x10/4:
+ return 0; // DxEN / DxGO, write only
+ case 0x14/4:
+ return 0; // DxMOD / DxRUP / DxWUP / DxFT, write only
+ }
+
+ // can't happen anyway
+ return 0;
+}
+
+void sega_scu_device::dma_common_w(uint8_t offset,uint8_t level,uint32_t data)
+{
+ switch(offset)
+ {
+ case 0x00/4: // source
+ m_dma[level].src = data & 0x07ffffff;
+ break;
+ case 0x04/4: // destination
+ m_dma[level].dst = data & 0x07ffffff;
+ break;
+ case 0x08/4: // size, lv0 is bigger than the others
+ m_dma[level].size = data & ((level == 0) ? 0x000fffff : 0xfff);
+ break;
+ case 0x0c/4: // DxAD
+ m_dma[level].src_add = (data & 0x100) ? 4 : 0;
+ m_dma[level].dst_add = 1 << (data & 7);
+ if(m_dma[level].dst_add == 1) { m_dma[level].dst_add = 0; }
+ break;
+ case 0x10/4: // DxEN / DxGO
+ m_dma[level].enable_mask = BIT(data,8);
+
+ // check if DxGO is enabled for start factor = 7
+ if(m_dma[level].enable_mask == true && data & 1 && m_dma[level].start_factor == 7)
+ {
+ if(m_dma[level].indirect_mode == true)
+ handle_dma_indirect(level);
+ else
+ handle_dma_direct(level);
+ }
+ break;
+ case 0x14/4: // DxMOD / DxRUP / DxWUP / DxFT, write only
+ m_dma[level].indirect_mode = BIT(data,24);
+ m_dma[level].rup = BIT(data,16);
+ m_dma[level].wup = BIT(data,8);
+ m_dma[level].start_factor = data & 7;
+ if(m_dma[level].indirect_mode == true)
+ {
+ //if(LOG_SCU) logerror("Indirect Mode DMA lv %d set\n",level);
+ if(m_dma[level].wup == false)
+ m_dma[level].index = m_dma[level].dst;
+ }
+
+ break;
+ }
+}
+
+inline void sega_scu_device::update_dma_status(uint8_t level,bool state)
+{
+ if(state)
+ m_status|=(0x10 << 4 * level);
+ else
+ m_status&=~(0x10 << 4 * level);
+}
+
+void sega_scu_device::handle_dma_direct(uint8_t level)
+{
+ uint32_t tmp_src,tmp_dst,total_size;
+ uint8_t cd_transfer_flag;
+
+ #if 0
+ if(m_dma[level].src_add == 0 || (m_dma[level].dst_add != 2 && m_dma[level].dst_add != 4))
+ {
+ if(LOG_SCU) printf("DMA lv %d transfer START\n"
+ "Start %08x End %08x Size %04x\n",dma_ch,m_scu.src[dma_ch],m_scu.dst[dma_ch],m_scu.size[dma_ch]);
+ if(LOG_SCU) printf("Start Add %04x Destination Add %04x\n",m_scu.src_add[dma_ch],m_scu.dst_add[dma_ch]);
+ }
+ #endif
+
+ // Game Basic, World Cup 98 and Batman Forever trips this, according to the docs the SCU can't transfer from BIOS area (can't communicate from/to that bus)
+ if((m_dma[level].src & 0x07f00000) == 0)
+ {
+ //popmessage("Warning: SCU transfer from BIOS area, contact MAMEdev");
+ if(!(m_ism & IRQ_DMAILL))
+ m_hostcpu->set_input_line_and_vector(3, HOLD_LINE, 0x4c);
+ else
+ m_ist |= (IRQ_DMAILL);
+ return;
+ }
+
+ update_dma_status(level,true);
+
+ /* max size */
+ if(m_dma[level].size == 0) { m_dma[level].size = (level == 0) ? 0x00100000 : 0x1000; }
+
+ tmp_src = tmp_dst = 0;
+
+ total_size = m_dma[level].size ;
+ if(m_dma[level].rup == false) tmp_src = m_dma[level].src;
+ if(m_dma[level].wup == false) tmp_dst = m_dma[level].dst;
+
+ cd_transfer_flag = m_dma[level].src_add == 0 && m_dma[level].src == 0x05818000;
+
+ /* TODO: Many games directly accesses CD-ROM register 0x05818000, it must be a dword access with current implementation otherwise it won't work */
+ if(cd_transfer_flag)
+ {
+ int i;
+ if((m_dma[level].dst & 0x07000000) == 0x06000000)
+ m_dma[level].dst_add = 4;
+ else
+ m_dma[level].dst_add <<= 1;
+
+ for (i = 0; i < m_dma[level].size;i+=m_dma[level].dst_add)
+ {
+ m_hostspace->write_dword(m_dma[level].dst,m_hostspace->read_dword(m_dma[level].src));
+ if(m_dma[level].dst_add == 8)
+ m_hostspace->write_dword(m_dma[level].dst+4,m_hostspace->read_dword(m_dma[level].src));
+
+ m_dma[level].src += m_dma[level].src_add;
+ m_dma[level].dst += m_dma[level].dst_add;
+ }
+ }
+ else
+ {
+ int i;
+ uint8_t src_shift;
+
+ src_shift = ((m_dma[level].src & 2) >> 1) ^ 1;
+
+ for (i = 0; i < m_dma[level].size; i+=2)
+ {
+ dma_single_transfer(m_dma[level].src, m_dma[level].dst, &src_shift);
+
+ if(src_shift)
+ m_dma[level].src+= m_dma[level].src_add ;
+
+ /* if target is Work RAM H, the add value is fixed, behaviour confirmed by Final Romance 2, Virtual Mahjong and Burning Rangers */
+ m_dma[level].dst += ((m_dma[level].dst & 0x07000000) == 0x06000000) ? 2 : m_dma[level].dst_add;
+ }
+ }
+
+ /* Burning Rangers doesn't agree with this. */
+// m_scu.size[dma_ch] = 0;
+ if(m_dma[level].rup == false) m_dma[level].src = tmp_src;
+ if(m_dma[level].wup == false) m_dma[level].dst = tmp_dst;
+
+ // TODO: Timing is a guess.
+ m_dma_timer[level]->adjust(m_hostcpu->cycles_to_attotime(total_size/4));
+}
+
+void sega_scu_device::handle_dma_indirect(uint8_t level)
+{
+ /*Helper to get out of the cycle*/
+ uint8_t job_done = 0;
+ /*temporary storage for the transfer data*/
+ uint32_t tmp_src;
+ uint32_t indirect_src,indirect_dst;
+ int32_t indirect_size;
+ uint32_t total_size = 0;
+
+ update_dma_status(level,true);
+
+ m_dma[level].index = m_dma[level].dst;
+
+ do{
+ tmp_src = m_dma[level].index;
+
+ indirect_size = m_hostspace->read_dword(m_dma[level].index);
+ indirect_dst = m_hostspace->read_dword(m_dma[level].index+4);
+ indirect_src = m_hostspace->read_dword(m_dma[level].index+8);
+
+ /*Indirect Mode end factor*/
+ if(indirect_src & 0x80000000)
+ job_done = 1;
+
+ #if 0
+ if(m_dma[level].src_add == 0 || (m_dma[level].dst_add != 2))
+ {
+ if(LOG_SCU) printf("DMA lv %d indirect mode transfer START\n"
+ "Index %08x Start %08x End %08x Size %04x\n",dma_ch,tmp_src,indirect_src,indirect_dst,indirect_size);
+ if(LOG_SCU) printf("Start Add %04x Destination Add %04x\n",m_scu.src_add[dma_ch],m_scu.dst_add[dma_ch]);
+ }
+ #endif
+
+ indirect_src &=0x07ffffff;
+ indirect_dst &=0x07ffffff;
+ indirect_size &= ((level == 0) ? 0xfffff : 0x3ffff); //TODO: Guardian Heroes sets up a 0x23000 transfer for the FMV?
+
+ if(indirect_size == 0) { indirect_size = (level == 0) ? 0x00100000 : 0x2000; }
+
+ {
+ int i;
+ uint8_t src_shift;
+
+ src_shift = ((indirect_src & 2) >> 1) ^ 1;
+
+ for (i = 0; i < indirect_size;i+=2)
+ {
+ dma_single_transfer(indirect_src,indirect_dst,&src_shift);
+
+ if(src_shift)
+ indirect_src+=m_dma[level].src_add;
+
+ indirect_dst += ((m_dma[level].dst & 0x07000000) == 0x06000000) ? 2 : m_dma[level].dst_add;
+ }
+ }
+
+ /* Guess: Size + data acquire (1 cycle for src/dst/size) */
+ total_size += indirect_size + 3*4;
+
+ //if(DRUP(0)) space.write_dword(tmp_src+8,m_scu.src[0]|job_done ? 0x80000000 : 0);
+ //if(DWUP(0)) space.write_dword(tmp_src+4,m_scu.dst[0]);
+
+ m_dma[level].index = tmp_src+0xc;
+
+ }while(job_done == 0);
+
+ // TODO: Timing is a guess.
+ m_dma_timer[level]->adjust(m_hostcpu->cycles_to_attotime(total_size/4));
+}
+
+
+inline void sega_scu_device::dma_single_transfer(uint32_t src, uint32_t dst,uint8_t *src_shift)
+{
+ uint32_t src_data;
+
+ if(src & 1)
+ {
+ /* Road Blaster does a work ram h to color ram with offsetted source address, do some data rotation */
+ src_data = ((m_hostspace->read_dword(src & 0x07fffffc) & 0x00ffffff)<<8);
+ src_data |= ((m_hostspace->read_dword((src & 0x07fffffc)+4) & 0xff000000) >> 24);
+ src_data >>= (*src_shift)*16;
+ }
+ else
+ src_data = m_hostspace->read_dword(src & 0x07fffffc) >> (*src_shift)*16;
+
+ m_hostspace->write_word(dst,src_data);
+
+ *src_shift ^= 1;
+}
+
+inline void sega_scu_device::dma_start_factor_ack(uint8_t event)
+{
+ int i;
+
+ for(i=0;i<3;i++)
+ {
+ if(m_dma[i].enable_mask == true && m_dma[i].start_factor == event)
+ {
+ if(m_dma[i].indirect_mode == true) { handle_dma_indirect(i); }
+ else { handle_dma_direct(i); }
+ }
+ }
+}
+
+READ32_MEMBER(sega_scu_device::dma_lv0_r) { return dma_common_r(offset,0); }
+WRITE32_MEMBER(sega_scu_device::dma_lv0_w) { dma_common_w(offset,0,data); }
+READ32_MEMBER(sega_scu_device::dma_lv1_r) { return dma_common_r(offset,1); }
+WRITE32_MEMBER(sega_scu_device::dma_lv1_w) { dma_common_w(offset,1,data); }
+READ32_MEMBER(sega_scu_device::dma_lv2_r) { return dma_common_r(offset,2); }
+WRITE32_MEMBER(sega_scu_device::dma_lv2_w) { dma_common_w(offset,2,data); }
+
+READ32_MEMBER(sega_scu_device::dma_status_r)
+{
+ return m_status;
+}
+
+//**************************************************************************
+// Timers
+//**************************************************************************
+
+WRITE32_MEMBER(sega_scu_device::t0_compare_w )
+{
+ COMBINE_DATA(&m_t0c);
+}
+
+WRITE32_MEMBER(sega_scu_device::t1_setdata_w )
+{
+ COMBINE_DATA(&m_t1s);
+}
+
+/*
+ ---- ---x ---- ---- T1MD Timer 1 mode (0=each line, 1=only at timer 0 lines)
+ ---- ---- ---- ---x TENB Timers enable
+ */
+WRITE16_MEMBER(sega_scu_device::t1_mode_w )
+{
+ m_t1md = BIT(data,8);
+ m_tenb = BIT(data,0);
+}
+
+
+void sega_scu_device::check_scanline_timers(int scanline,int y_step)
+{
+ if(m_tenb == false)
+ return;
+
+ int timer0_compare = (m_t0c & 0x3ff)*y_step;
+
+ // Timer 0
+ if(scanline == timer0_compare)
+ {
+ if(!(m_ism & IRQ_TIMER_0))
+ {
+ m_hostcpu->set_input_line_and_vector(0xc, HOLD_LINE, 0x43 );
+ dma_start_factor_ack(3);
+ }
+ else
+ m_ist |= (IRQ_TIMER_0);
+ }
+
+ // Timer 1
+ // TODO: should happen after some time when hblank-in is received then counts down t1s
+ if(
+ ((m_t1md == false) && ((scanline % y_step) == 0)) ||
+ ((m_t1md == true) && (scanline == timer0_compare)) )
+ {
+ if(!(m_ism & IRQ_TIMER_1))
+ {
+ m_hostcpu->set_input_line_and_vector(0xb, HOLD_LINE, 0x44 );
+ dma_start_factor_ack(4);
+ }
+ else
+ m_ist |= (IRQ_TIMER_1);
+ }
+
+}
+
+
+//**************************************************************************
+// Interrupt
+//**************************************************************************
+
+READ32_MEMBER(sega_scu_device::irq_mask_r)
+{
+ return m_ism;
+}
+
+READ32_MEMBER(sega_scu_device::irq_status_r)
+{
+ return m_ist;
+}
+
+WRITE32_MEMBER(sega_scu_device::irq_mask_w)
+{
+ COMBINE_DATA(&m_ism);
+ test_pending_irqs();
+}
+
+WRITE32_MEMBER(sega_scu_device::irq_status_w)
+{
+ if(mem_mask != 0xffffffff)
+ logerror("%s IST write %08x with %08x\n",this->tag(),data,mem_mask);
+
+ m_ist &= data;
+ test_pending_irqs();
+}
+
+void sega_scu_device::test_pending_irqs()
+{
+ int i;
+ const int irq_level[32] = { 0xf, 0xe, 0xd, 0xc,
+ 0xb, 0xa, 0x9, 0x8,
+ 0x8, 0x6, 0x6, 0x5,
+ 0x3, 0x2, -1, -1,
+ 0x7, 0x7, 0x7, 0x7,
+ 0x4, 0x4, 0x4, 0x4,
+ 0x1, 0x1, 0x1, 0x1,
+ 0x1, 0x1, 0x1, 0x1 };
+
+ for(i=0;i<32;i++)
+ {
+ if((!(m_ism & 1 << i)) && (m_ist & 1 << i))
+ {
+ if(irq_level[i] != -1) /* TODO: cheap check for undefined irqs */
+ {
+ m_hostcpu->set_input_line_and_vector(irq_level[i], HOLD_LINE, 0x40 + i);
+ m_ist &= ~(1 << i);
+ return; /* avoid spurious irqs, correct? */
+ }
+ }
+ }
+}
+
+WRITE_LINE_MEMBER(sega_scu_device::vblank_out_w)
+{
+ if(!state)
+ return;
+
+ if(!(m_ism & IRQ_VBLANK_OUT))
+ {
+ m_hostcpu->set_input_line_and_vector(0xe, HOLD_LINE, 0x41);
+ dma_start_factor_ack(1);
+ }
+ else
+ m_ist |= (IRQ_VBLANK_OUT);
+}
+
+WRITE_LINE_MEMBER(sega_scu_device::vblank_in_w)
+{
+ if(!state)
+ return;
+
+ if(!(m_ism & IRQ_VBLANK_IN))
+ {
+ m_hostcpu->set_input_line_and_vector(0xf, HOLD_LINE ,0x40);
+ dma_start_factor_ack(0);
+ }
+ else
+ m_ist |= (IRQ_VBLANK_IN);
+}
+
+WRITE_LINE_MEMBER(sega_scu_device::hblank_in_w)
+{
+ if(!state)
+ return;
+
+ if(!(m_ism & IRQ_HBLANK_IN))
+ {
+ m_hostcpu->set_input_line_and_vector(0xd, HOLD_LINE, 0x42);
+ dma_start_factor_ack(2);
+ }
+ else
+ m_ist |= (IRQ_HBLANK_IN);
+}
+
+WRITE_LINE_MEMBER(sega_scu_device::vdp1_end_w)
+{
+ if(!state)
+ return;
+
+ if(!(m_ism & IRQ_VDP1_END))
+ {
+ m_hostcpu->set_input_line_and_vector(0x2, HOLD_LINE, 0x4d);
+ dma_start_factor_ack(6);
+ }
+ else
+ m_ist |= (IRQ_VDP1_END);
+}
+
+WRITE_LINE_MEMBER(sega_scu_device::sound_req_w)
+{
+ if(!state)
+ return;
+
+ if(!(m_ism & IRQ_SOUND_REQ))
+ {
+ m_hostcpu->set_input_line_and_vector(9, HOLD_LINE, 0x46);
+ dma_start_factor_ack(5);
+ }
+ else
+ m_ist |= (IRQ_SOUND_REQ);
+}
+
+WRITE_LINE_MEMBER(sega_scu_device::smpc_irq_w)
+{
+ if(!state)
+ return;
+
+ if(!(m_ism & IRQ_SMPC))
+ m_hostcpu->set_input_line_and_vector(8, HOLD_LINE, 0x47);
+ else
+ m_ist |= (IRQ_SMPC);
+}
+
+WRITE_LINE_MEMBER(sega_scu_device::scudsp_end_w)
+{
+ if(!state)
+ return;
+
+ if(!(m_ism & IRQ_DSP_END))
+ m_hostcpu->set_input_line_and_vector(0xa, HOLD_LINE, 0x45);
+ else
+ m_ist |= (IRQ_DSP_END);
+
+}
+
+//**************************************************************************
+// Miscellanea
+//**************************************************************************
+
+READ32_MEMBER(sega_scu_device::version_r)
+{
+ return 4; // correct for stock Saturn at least
+}
diff --git a/src/devices/machine/sega_scu.h b/src/devices/machine/sega_scu.h
new file mode 100644
index 00000000000..519a15ed3fb
--- /dev/null
+++ b/src/devices/machine/sega_scu.h
@@ -0,0 +1,156 @@
+// license:LGPL-2.1+
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+ Sega System Control Unit (c) 1995 Sega
+
+***************************************************************************/
+
+#ifndef MAME_MACHINE_SEGA_SCU_H
+#define MAME_MACHINE_SEGA_SCU_H
+
+#pragma once
+
+#include "cpu/sh/sh2.h"
+#include "cpu/scudsp/scudsp.h"
+
+#define IRQ_VBLANK_IN 1 << 0
+#define IRQ_VBLANK_OUT 1 << 1
+#define IRQ_HBLANK_IN 1 << 2
+#define IRQ_TIMER_0 1 << 3
+#define IRQ_TIMER_1 1 << 4
+#define IRQ_DSP_END 1 << 5
+#define IRQ_SOUND_REQ 1 << 6
+#define IRQ_SMPC 1 << 7
+#define IRQ_PAD 1 << 8
+#define IRQ_DMALV2 1 << 9
+#define IRQ_DMALV1 1 << 10
+#define IRQ_DMALV0 1 << 11
+#define IRQ_DMAILL 1 << 12
+#define IRQ_VDP1_END 1 << 13
+#define IRQ_ABUS 1 << 15
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SEGA_SCU_ADD(tag) \
+ MCFG_DEVICE_ADD((tag), SEGA_SCU, (0))
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> sega_scu_device
+
+class sega_scu_device : public device_t
+{
+public:
+ // construction/destruction
+ sega_scu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ // I/O operations
+ DECLARE_ADDRESS_MAP(regs_map, 32);
+
+ // DMA
+ DECLARE_READ32_MEMBER(dma_lv0_r);
+ DECLARE_WRITE32_MEMBER(dma_lv0_w);
+ DECLARE_READ32_MEMBER(dma_lv1_r);
+ DECLARE_WRITE32_MEMBER(dma_lv1_w);
+ DECLARE_READ32_MEMBER(dma_lv2_r);
+ DECLARE_WRITE32_MEMBER(dma_lv2_w);
+ DECLARE_READ32_MEMBER(dma_status_r);
+
+ // Timers
+ DECLARE_WRITE32_MEMBER(t0_compare_w);
+ DECLARE_WRITE32_MEMBER(t1_setdata_w);
+ DECLARE_WRITE16_MEMBER(t1_mode_w);
+ // Interrupt
+ DECLARE_READ32_MEMBER(irq_mask_r);
+ DECLARE_READ32_MEMBER(irq_status_r);
+ DECLARE_WRITE32_MEMBER(irq_mask_w);
+ DECLARE_WRITE32_MEMBER(irq_status_w);
+ DECLARE_READ32_MEMBER(version_r);
+
+ DECLARE_WRITE_LINE_MEMBER(vblank_out_w);
+ DECLARE_WRITE_LINE_MEMBER(vblank_in_w);
+ DECLARE_WRITE_LINE_MEMBER(hblank_in_w);
+ DECLARE_WRITE_LINE_MEMBER(vdp1_end_w);
+ void check_scanline_timers(int scanline,int y_step);
+ DECLARE_WRITE_LINE_MEMBER(sound_req_w);
+ DECLARE_WRITE_LINE_MEMBER(smpc_irq_w);
+ DECLARE_WRITE_LINE_MEMBER(scudsp_end_w);
+ DECLARE_READ16_MEMBER(scudsp_dma_r);
+ DECLARE_WRITE16_MEMBER(scudsp_dma_w);
+
+ static void static_set_hostcpu(device_t &device, const char *cputag);
+
+protected:
+ // device-level overrides
+ //virtual void device_validity_check(validity_checker &valid) const override;
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_reset_after_children() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+private:
+ required_device<scudsp_cpu_device> m_scudsp;
+
+ enum {
+ DMALV0_ID = 0,
+ DMALV1_ID,
+ DMALV2_ID
+ };
+
+ emu_timer *m_dma_timer[3];
+ uint32_t m_ism;
+ uint32_t m_ist;
+ uint32_t m_t0c;
+ uint32_t m_t1s;
+ uint32_t m_status;
+ bool m_t1md;
+ bool m_tenb;
+
+ const char *m_hostcpu_tag;
+ sh2_device *m_hostcpu;
+ address_space *m_hostspace;
+ void test_pending_irqs();
+
+ struct {
+ uint32_t src; /* Source DMA lv n address*/
+ uint32_t dst; /* Destination DMA lv n address*/
+ uint32_t src_add; /* Source Addition for DMA lv n*/
+ uint32_t dst_add; /* Destination Addition for DMA lv n*/
+ uint32_t size; /* Transfer DMA size lv n*/
+ uint32_t index;
+ uint8_t start_factor;
+ bool enable_mask;
+ bool indirect_mode;
+ bool rup;
+ bool wup;
+ }m_dma[3];
+
+ uint32_t dma_common_r(uint8_t offset,uint8_t level);
+ void dma_common_w(uint8_t offset,uint8_t level,uint32_t data);
+ void handle_dma_direct(uint8_t level);
+ void handle_dma_indirect(uint8_t level);
+ void update_dma_status(uint8_t level,bool state);
+ void dma_single_transfer(uint32_t src, uint32_t dst,uint8_t *src_shift);
+ void dma_start_factor_ack(uint8_t event);
+};
+
+
+// device type definition
+DECLARE_DEVICE_TYPE(SEGA_SCU, sega_scu_device)
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+
+#endif // MAME_MACHINE_SEGA_SCU_H
diff --git a/src/devices/machine/smc91c9x.cpp b/src/devices/machine/smc91c9x.cpp
index ee5214d2323..b94f7a29d1f 100644
--- a/src/devices/machine/smc91c9x.cpp
+++ b/src/devices/machine/smc91c9x.cpp
@@ -4,12 +4,12 @@
SMC91C9X ethernet controller implementation
- by Aaron Giles
+ by Aaron Giles, Jean-François DEL NERO
***************************************************************************
Notes:
- * only loopback mode really works
+ * Connected mode working
**************************************************************************/
@@ -22,7 +22,9 @@
DEBUGGING
***************************************************************************/
-#define LOG_ETHERNET (0)
+//#define VERBOSE 1
+#include "logmacro.h"
+
#define DISPLAY_STATS (0)
@@ -68,14 +70,15 @@
#define EREG_ERCV (3*8 + 6)
/* Ethernet MMU commands */
-#define ECMD_NOP 0
-#define ECMD_ALLOCATE 1
-#define ECMD_RESET_MMU 2
-#define ECMD_REMOVE 3
-#define ECMD_REMOVE_RELEASE 4
-#define ECMD_RELEASE_PACKET 5
-#define ECMD_ENQUEUE_PACKET 6
-#define ECMD_RESET_FIFOS 7
+#define ECMD_NOP 0
+#define ECMD_ALLOCATE 2
+#define ECMD_RESET_MMU 4
+#define ECMD_REMOVE_TOPFRAME_RX 6
+#define ECMD_REMOVE_TOPFRAME_TX 7
+#define ECMD_REMOVE_RELEASE_TOPFRAME_RX 8
+#define ECMD_RELEASE_PACKET 10
+#define ECMD_ENQUEUE_PACKET 12
+#define ECMD_RESET_FIFOS 14
/* Ethernet interrupt bits */
#define EINT_RCV 0x01
@@ -99,16 +102,15 @@ static const char *const ethernet_regname[64] =
"(7.0)", "(7.1)", "(7.2)", "(7.3)", "(7.4)", "(7.5)", "(7.6)", "BANK"
};
-
-
/***************************************************************************
DEVICE INTERFACE
***************************************************************************/
smc91c9x_device::smc91c9x_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_network_interface(mconfig, *this, 10.0f)
, m_irq_handler(*this)
- , m_link_unconnected(true)
+ , m_link_unconnected(false)
{
}
@@ -119,15 +121,12 @@ smc91c9x_device::smc91c9x_device(const machine_config &mconfig, device_type type
void smc91c9x_device::device_start()
{
m_irq_handler.resolve_safe();
- // TX timer
- m_tx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(smc91c9x_device::finish_enqueue), this));
/* register ide states */
save_item(NAME(m_reg));
save_item(NAME(m_regmask));
save_item(NAME(m_irq_state));
save_item(NAME(m_alloc_count));
- save_item(NAME(m_fifo_count));
save_item(NAME(m_rx));
save_item(NAME(m_tx));
save_item(NAME(m_sent));
@@ -140,14 +139,39 @@ void smc91c9x_device::device_start()
void smc91c9x_device::device_reset()
{
- memset(m_reg, 0, sizeof(m_reg));
- memset(m_regmask, 0, sizeof(m_regmask));
+ std::fill(std::begin(m_reg), std::end(m_reg), 0);
+
+ std::fill(std::begin(m_rx), std::end(m_rx), 0);
+ std::fill(std::begin(m_tx), std::end(m_tx), 0);
+
+ std::fill(std::begin(m_regmask), std::end(m_regmask), 0);
+
m_irq_state = 0;
m_alloc_count = 0;
- m_fifo_count = 0;
+ rx_fifo_out = 0;
+ rx_fifo_in = 0;
+
+ tx_fifo_out = 0;
+ tx_fifo_in = 0;
+
m_sent = 0;
m_recd = 0;
+ osd_list_network_adapters();
+
+ unsigned char const *const mac = (const unsigned char *)get_mac();
+
+ if (VERBOSE & LOG_GENERAL)
+ {
+ logerror("MAC : ");
+ for (int i = 0; i < ETHERNET_ADDR_SIZE; i++)
+ logerror("%.2X", mac[i]);
+
+ logerror("\n");
+ }
+
+ set_promisc(true);
+
m_reg[EREG_TCR] = 0x0000; m_regmask[EREG_TCR] = 0x3d87;
m_reg[EREG_EPH_STATUS] = 0x0000; m_regmask[EREG_EPH_STATUS] = 0x0000;
m_reg[EREG_RCR] = 0x0000; m_regmask[EREG_RCR] = 0xc307;
@@ -158,9 +182,17 @@ void smc91c9x_device::device_reset()
m_reg[EREG_CONFIG] = 0x0030; m_regmask[EREG_CONFIG] = 0x17c6;
m_reg[EREG_BASE] = 0x1866; m_regmask[EREG_BASE] = 0xfffe;
- m_reg[EREG_IA0_1] = 0x0000; m_regmask[EREG_IA0_1] = 0xffff;
- m_reg[EREG_IA2_3] = 0x0000; m_regmask[EREG_IA2_3] = 0xffff;
- m_reg[EREG_IA4_5] = 0x0000; m_regmask[EREG_IA4_5] = 0xffff;
+
+ // Default MAC
+ m_reg[EREG_IA0_1] = 0x1300; m_regmask[EREG_IA0_1] = 0xffff;
+ m_reg[EREG_IA2_3] = 0x12F7; m_regmask[EREG_IA2_3] = 0xffff;
+ m_reg[EREG_IA4_5] = 0x5634; m_regmask[EREG_IA4_5] = 0xffff;
+
+ // Interface MAC
+ m_reg[EREG_IA0_1] = mac[0] | (mac[1]<<8);
+ m_reg[EREG_IA2_3] = mac[2] | (mac[3]<<8);
+ m_reg[EREG_IA4_5] = mac[4] | (mac[5]<<8);
+
m_reg[EREG_GENERAL_PURP] = 0x0000; m_regmask[EREG_GENERAL_PURP] = 0xffff;
m_reg[EREG_CONTROL] = 0x0100; m_regmask[EREG_CONTROL] = 0x68e7;
@@ -181,7 +213,6 @@ void smc91c9x_device::device_reset()
m_reg[EREG_ERCV] = 0x331f; m_regmask[EREG_ERCV] = 0x009f;
update_ethernet_irq();
- m_tx_timer->adjust(attotime::never);
}
@@ -192,7 +223,6 @@ smc91c94_device::smc91c94_device(const machine_config &mconfig, const char *tag,
{
}
-
DEFINE_DEVICE_TYPE(SMC91C96, smc91c96_device, "smc91c96", "SMC91C96 Ethernet Controller")
smc91c96_device::smc91c96_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
@@ -200,6 +230,182 @@ smc91c96_device::smc91c96_device(const machine_config &mconfig, const char *tag,
{
}
+void smc91c9x_device::clear_tx_fifo()
+{
+ tx_fifo_in = 0;
+ tx_fifo_out = 0;
+ std::fill(std::begin(m_tx), std::end(m_tx), 0);
+}
+
+void smc91c9x_device::clear_rx_fifo()
+{
+ rx_fifo_in = 0;
+ rx_fifo_out = 0;
+ std::fill(std::begin(m_rx), std::end(m_rx), 0);
+}
+
+int smc91c9x_device::is_broadcast(uint8_t mac_address[])
+{
+ int i;
+
+ i = 0;
+
+ while(mac_address[i] == 0xFF)
+ {
+ i++;
+ }
+
+ if ( i == 6 )
+ return 1;
+
+ return 0;
+}
+
+
+int smc91c9x_device::ethernet_packet_is_for_me(const uint8_t mac_address[])
+{
+ // tcpdump -i eth0 -q ether host 08:00:1e:01:ae:a5 or ether broadcast or ether dst 09:00:1e:00:00:00 or ether dst 09:00:1e:00:00:01
+ // wireshark filter: eth.addr eq 08:00:1e:01:ae:a5 or eth.dst eq ff:ff:ff:ff:ff:ff or eth.dst eq 09:00:1e:00:00:00 or eth.dst eq 09:00:1e:00:00:01
+
+ int i;
+ uint8_t local_address[ETHERNET_ADDR_SIZE];
+
+ LOG("\n");
+
+ local_address[0] = (m_reg[EREG_IA0_1]>>0) & 0xFF;
+ local_address[1] = (m_reg[EREG_IA0_1]>>8) & 0xFF;
+ local_address[2] = (m_reg[EREG_IA2_3]>>0) & 0xFF;
+ local_address[3] = (m_reg[EREG_IA2_3]>>8) & 0xFF;
+ local_address[4] = (m_reg[EREG_IA4_5]>>0) & 0xFF;
+ local_address[5] = (m_reg[EREG_IA4_5]>>8) & 0xFF;
+
+ if (VERBOSE & LOG_GENERAL)
+ {
+ for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ )
+ {
+ logerror("%.2X",local_address[i]);
+ }
+ logerror("=");
+ for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ )
+ {
+ logerror("%.2X",mac_address[i]);
+ }
+ logerror("?");
+ }
+
+ // skip Ethernet broadcast packets if RECV_BROAD is not set
+ if (is_broadcast((uint8_t *)mac_address))
+ {
+ LOG(" -- Broadcast rx\n");
+ return 2;
+ }
+
+ if (memcmp(mac_address, local_address, ETHERNET_ADDR_SIZE) == 0)
+ {
+ LOG(" -- Address Match\n");
+ return 1;
+ }
+
+ LOG(" -- Not Matching\n");
+
+ return 0;
+}
+
+/***************************************************************************
+ recv_cb - receive callback - receive and process an ethernet packet
+ ***************************************************************************/
+
+void smc91c9x_device::recv_cb(uint8_t *data, int length)
+{
+ LOG("recv_cb : %d/0x%x\n",length,length);
+
+ int const isforme = ethernet_packet_is_for_me( data );
+
+ if (isforme==1 && (length >= ETHERNET_ADDR_SIZE) && (VERBOSE & LOG_GENERAL))
+ {
+ logerror("RX: ");
+ for (int i = 0; i < ETHERNET_ADDR_SIZE; i++)
+ logerror("%.2X", data[i]);
+
+ logerror(" ");
+
+ for (int i = 0; i < length-ETHERNET_ADDR_SIZE; i++)
+ logerror("%.2X", data[ETHERNET_ADDR_SIZE + i]);
+
+ logerror(" - IsForMe %d - %d/0x%x bytes\n", isforme, length, length);
+ }
+
+ if ( (length < ETHERNET_ADDR_SIZE || !isforme) && !(m_reg[EREG_RCR] & 0x0100) )
+ {
+ LOG("\n");
+
+ // skip packet
+ return;
+ }
+
+ /* signal a receive */
+
+ /* compute the packet length */
+
+ if ( ( length < ( ETHER_BUFFER_SIZE - ( 2+2+2 ) ) ) )
+ {
+ uint8_t *const packet = &m_rx[ ( rx_fifo_in & ( ETHER_RX_BUFFERS - 1 ) ) * ETHER_BUFFER_SIZE];
+
+ std::fill_n(packet, ETHER_BUFFER_SIZE, 0);
+
+ int dst = 0;
+
+ // build up the packet
+
+ // Status word
+ packet[dst++] = 0x00;
+
+ // set the broadcast flag
+ if ( isforme == 2 )
+ packet[dst++] |= 0x40;
+ else
+ packet[dst++] = 0x00;
+
+ //bytes count
+ packet[dst++] = 0x00;
+ packet[dst++] = 0x00;
+
+ memcpy(&packet[dst], data, length );
+ dst += length;
+
+ if ( dst & 1 )
+ {
+ // ODD Frame
+ packet[dst++] = 0x40 | 0x20; // Control
+ }
+ else
+ {
+ packet[dst++] = 0x00; // Pad
+ packet[dst++] = 0x40 | 0x00; // Control
+ }
+
+ dst += 2;
+
+ dst &= 0x7FF;
+
+ packet[2] = (dst&0xFF);
+ packet[3] = (dst) >> 8;
+
+ m_reg[EREG_INTERRUPT] |= EINT_RCV;
+ m_reg[EREG_FIFO_PORTS] &= ~0x8000;
+
+ rx_fifo_in = (rx_fifo_in + 1) & ( ETHER_RX_BUFFERS - 1 );
+ }
+ else
+ {
+ LOG("Rejected ! Fifo Full ?");
+ }
+
+ update_ethernet_irq();
+
+ LOG("\n");
+}
+
/***************************************************************************
INTERNAL HELPERS
***************************************************************************/
@@ -210,14 +416,12 @@ smc91c96_device::smc91c96_device(const machine_config &mconfig, const char *tag,
void smc91c9x_device::update_ethernet_irq()
{
- uint8_t mask = m_reg[EREG_INTERRUPT] >> 8;
- uint8_t state = m_reg[EREG_INTERRUPT] & 0xff;
+ uint8_t const mask = m_reg[EREG_INTERRUPT] >> 8;
+ uint8_t const state = m_reg[EREG_INTERRUPT] & 0xff;
/* update the IRQ state */
m_irq_state = ((mask & state) != 0);
- if (!m_irq_handler.isnull()) {
- m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE);
- }
+ m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE);
}
@@ -227,97 +431,84 @@ void smc91c9x_device::update_ethernet_irq()
void smc91c9x_device::update_stats()
{
- if (DISPLAY_STATS)
+ if ( DISPLAY_STATS )
popmessage("Sent:%d Rec'd:%d", m_sent, m_recd);
}
/*-------------------------------------------------
- finish_enqueue - complete an enqueued packet
+ send_frame - push a frame to the interface
-------------------------------------------------*/
-TIMER_CALLBACK_MEMBER(smc91c9x_device::finish_enqueue)
+int smc91c9x_device::send_frame()
{
- int is_broadcast = (m_tx[4] == 0xff && m_tx[5] == 0xff && m_tx[6] == 0xff &&
+ bool const is_broadcast = (m_tx[4] == 0xff && m_tx[5] == 0xff && m_tx[6] == 0xff &&
m_tx[7] == 0xff && m_tx[8] == 0xff && m_tx[9] == 0xff);
+ tx_fifo_in = ( tx_fifo_in + 1 ) & ( ETHER_TX_BUFFERS - 1 );
+
+ uint8_t *const tx_buffer = &m_tx[(tx_fifo_out & (ETHER_TX_BUFFERS-1))* ETHER_BUFFER_SIZE];
+ tx_fifo_out = ((tx_fifo_out + 1)& (ETHER_TX_BUFFERS-1));
+
/* update the EPH register and stuff it in the first transmit word */
m_reg[EREG_EPH_STATUS] = 0x0001;
+
if (is_broadcast)
m_reg[EREG_EPH_STATUS] |= 0x0040;
- m_tx[0] = m_reg[EREG_EPH_STATUS];
- m_tx[1] = m_reg[EREG_EPH_STATUS] >> 8;
+
+ tx_buffer[0] = m_reg[EREG_EPH_STATUS];
+ tx_buffer[1] = m_reg[EREG_EPH_STATUS] >> 8;
/* signal a transmit interrupt and mark the transmit buffer empty */
m_reg[EREG_INTERRUPT] |= EINT_TX;
m_reg[EREG_INTERRUPT] |= EINT_TX_EMPTY;
m_reg[EREG_FIFO_PORTS] |= 0x0080;
m_sent++;
+
update_stats();
- /* loopback? */
- if (m_reg[EREG_TCR] & 0x2002) {
- if (m_fifo_count < ETHER_RX_BUFFERS)
- {
- int buffer_len = ((m_tx[3] << 8) | m_tx[2]) & 0x7ff;
- uint8_t *packet = &m_rx[m_fifo_count++ * ETHER_BUFFER_SIZE];
- int packet_len;
-
- /* compute the packet length */
- packet_len = buffer_len - 6;
- if (packet[buffer_len - 1] & 0x20)
- packet_len++;
-
- /* build up the packet */
- packet[0] = 0x0000;
- packet[1] = 0x0000;
- packet[2] = buffer_len;
- packet[3] = buffer_len >> 8;
- memcpy(&packet[4], &m_tx[4], 6);
- memcpy(&packet[10], &m_tx[10], 6);
- memcpy(&packet[16], &m_tx[16], buffer_len - 16);
-
- /* set the broadcast flag */
- if (is_broadcast)
- packet[1] |= 0x40;
-
- /* pad? */
- if (m_reg[EREG_TCR & 0x0080])
- if (packet_len < 64)
- {
- memset(&packet[buffer_len], 0, 64 + 6 - buffer_len);
- packet[buffer_len - 1] = 0;
- buffer_len = 64 + 6;
- packet[2] = buffer_len;
- packet[3] = buffer_len >> 8;
- }
+ int buffer_len = ((tx_buffer[3] << 8) | tx_buffer[2]) & 0x7ff;
- /* signal a receive */
- m_reg[EREG_INTERRUPT] |= EINT_RCV;
- m_reg[EREG_FIFO_PORTS] &= ~0x8000;
- }
+ if (VERBOSE & LOG_GENERAL)
+ {
+ logerror("TX: ");
+ for (int i = 4; i < (4 + ETHERNET_ADDR_SIZE); i++)
+ logerror("%.2X", tx_buffer[i]);
+
+ logerror(" ");
+
+ for (int i = 0; i < (buffer_len - (ETHERNET_ADDR_SIZE + 4)); i++)
+ logerror("%.2X", tx_buffer[4 + ETHERNET_ADDR_SIZE + i]);
+
+ logerror("--- %d/0x%x bytes\n", buffer_len, buffer_len);
}
- else if (m_link_unconnected) {
- // Set lost carrier
- if (m_reg[EREG_TCR] & 0x0400) {
- m_reg[EREG_EPH_STATUS] |= 0x400;
- // Clear Tx Enable on error
- m_reg[EREG_TCR] &= ~0x1;
+
+ if ( buffer_len > 4 )
+ {
+ // odd or even sized frame ?
+ if (tx_buffer[buffer_len-1] & 0x20)
+ buffer_len--;
+ else
+ buffer_len -= 2;
+
+ if (!(m_reg[EREG_TCR] & 0x2002))
+ {
+ // No loopback... Send the frame
+ if ( !send(&tx_buffer[4], buffer_len-4) )
+ {
+ // FIXME: failed to send the Ethernet packet
+ //logerror("failed to send Ethernet packet\n");
+ //LOG(this,("read_command_port(): !!! failed to send Ethernet packet"));
+ }
}
- // Set signal quality error
- if (m_reg[EREG_TCR] & 0x1000) {
- m_reg[EREG_EPH_STATUS] |= 0x20;
- // Clear Tx Enable on error
- m_reg[EREG_TCR] &= ~0x1;
+ else
+ {
+ // TODO loopback mode : Push the frame to the RX FIFO.
}
- // signal a no transmit
- m_reg[EREG_INTERRUPT] &= ~EINT_TX;
- // Set a ethernet phy status interrupt
- m_reg[EREG_INTERRUPT] |= EINT_EPH;
}
- update_ethernet_irq();
-}
+ return 0;
+}
/*-------------------------------------------------
process_command - handle MMU commands
@@ -325,66 +516,114 @@ TIMER_CALLBACK_MEMBER(smc91c9x_device::finish_enqueue)
void smc91c9x_device::process_command(uint16_t data)
{
- switch ((data >> 5) & 7)
+ switch ((data >> 4) & 0xF)
{
case ECMD_NOP:
- if (LOG_ETHERNET)
- logerror(" NOP\n");
+ LOG(" NOP\n");
break;
case ECMD_ALLOCATE:
- if (LOG_ETHERNET)
- logerror(" ALLOCATE MEMORY FOR TX (%d)\n", (data & 7));
+ LOG(" ALLOCATE MEMORY FOR TX (%d)\n", (data & 7));
m_reg[EREG_PNR_ARR] &= ~0xff00;
- m_reg[EREG_PNR_ARR] |= m_alloc_count++ << 8;
+ m_reg[EREG_PNR_ARR] |= (m_alloc_count++ & 0x7F) << 8;
m_reg[EREG_INTERRUPT] |= 0x0008;
update_ethernet_irq();
break;
case ECMD_RESET_MMU:
- if (LOG_ETHERNET)
- logerror(" RESET MMU\n");
+ /*
+ 0100
+ - RESET MMU TO INITIAL STATE -
+ Frees all memory allocations, clears relevant
+ interrupts, resets packet FIFO pointers.
+ */
+
+ LOG(" RESET MMU\n");
+ // Flush fifos.
+ clear_tx_fifo();
+ clear_rx_fifo();
break;
- case ECMD_REMOVE:
- if (LOG_ETHERNET)
- logerror(" REMOVE FRAME FROM RX FIFO\n");
+ case ECMD_REMOVE_TOPFRAME_TX:
+ LOG(" REMOVE FRAME FROM TX FIFO\n");
break;
- case ECMD_REMOVE_RELEASE:
- if (LOG_ETHERNET)
- logerror(" REMOVE AND RELEASE FRAME FROM RX FIFO\n");
+ case ECMD_REMOVE_TOPFRAME_RX:
+ LOG(" REMOVE FRAME FROM RX FIFO\n");
+
+ case ECMD_REMOVE_RELEASE_TOPFRAME_RX:
+ LOG(" REMOVE AND RELEASE FRAME FROM RX FIFO (RXI=%d RXO=%d)\n", rx_fifo_in & (ETHER_RX_BUFFERS - 1), rx_fifo_out & (ETHER_RX_BUFFERS - 1));
+
m_reg[EREG_INTERRUPT] &= ~EINT_RCV;
- if (m_fifo_count > 0)
- m_fifo_count--;
- if (m_fifo_count > 0)
+
+ if ( (rx_fifo_in & ( ETHER_RX_BUFFERS - 1 ) ) != (rx_fifo_out & ( ETHER_RX_BUFFERS - 1 ) ) )
+ rx_fifo_out = ( (rx_fifo_out + 1) & ( ETHER_RX_BUFFERS - 1 ) );
+
+ if ( (rx_fifo_in & ( ETHER_RX_BUFFERS - 1 ) ) != (rx_fifo_out & ( ETHER_RX_BUFFERS - 1 ) ) )
{
- memmove(&m_rx[0], &m_rx[ETHER_BUFFER_SIZE], m_fifo_count * ETHER_BUFFER_SIZE);
m_reg[EREG_INTERRUPT] |= EINT_RCV;
m_reg[EREG_FIFO_PORTS] &= ~0x8000;
}
else
m_reg[EREG_FIFO_PORTS] |= 0x8000;
+
update_ethernet_irq();
m_recd++;
update_stats();
break;
case ECMD_RELEASE_PACKET:
- if (LOG_ETHERNET)
- logerror(" RELEASE SPECIFIC PACKET\n");
+ LOG(" RELEASE SPECIFIC PACKET\n");
break;
case ECMD_ENQUEUE_PACKET:
- if (LOG_ETHERNET)
- logerror(" ENQUEUE TX PACKET\n");
- // Set some delay before tranmit ends
- m_tx_timer->adjust(attotime::from_usec(100));
+ LOG(" ENQUEUE TX PACKET\n");
+
+ if ( m_link_unconnected )
+ {
+ // Set lost carrier
+ if ( m_reg[EREG_TCR] & 0x0400 )
+ {
+ m_reg[EREG_EPH_STATUS] |= 0x400;
+ // Clear Tx Enable on error
+ m_reg[EREG_TCR] &= ~0x1;
+ }
+
+ // Set signal quality error
+ if ( m_reg[EREG_TCR] & 0x1000 )
+ {
+ m_reg[EREG_EPH_STATUS] |= 0x20;
+ // Clear Tx Enable on error
+ m_reg[EREG_TCR] &= ~0x1;
+ }
+
+ // signal a no transmit
+ m_reg[EREG_INTERRUPT] &= ~EINT_TX;
+ // Set a ethernet phy status interrupt
+ m_reg[EREG_INTERRUPT] |= EINT_EPH;
+
+ // Flush fifos.
+ clear_tx_fifo();
+ clear_rx_fifo();
+ }
+ else
+ {
+ if ( m_reg[EREG_TCR] & 0x0001 ) // TX EN ?
+ {
+ send_frame();
+ }
+ }
+
+ update_ethernet_irq();
+
break;
case ECMD_RESET_FIFOS:
- if (LOG_ETHERNET)
- logerror(" RESET TX FIFOS\n");
+ LOG(" RESET TX FIFOS\n");
+ // Flush fifos.
+ clear_tx_fifo();
+ clear_rx_fifo();
+
break;
}
// Set Busy (clear on next read)
@@ -407,8 +646,9 @@ READ16_MEMBER( smc91c9x_device::read )
/* determine the effective register */
offset %= 8;
- if (offset != EREG_BANK)
+ if ( offset != EREG_BANK )
offset += 8 * (m_reg[EREG_BANK] & 7);
+
result = m_reg[offset];
switch (offset)
@@ -419,7 +659,7 @@ READ16_MEMBER( smc91c9x_device::read )
break;
case EREG_PNR_ARR:
- if (ACCESSING_BITS_8_15)
+ if ( ACCESSING_BITS_8_15 )
{
m_reg[EREG_INTERRUPT] &= ~0x0008;
update_ethernet_irq();
@@ -429,19 +669,29 @@ READ16_MEMBER( smc91c9x_device::read )
case EREG_DATA_0: /* data register */
case EREG_DATA_1: /* data register */
{
- uint8_t *buffer = (m_reg[EREG_POINTER] & 0x8000) ? m_rx : m_tx;
+ uint8_t *buffer;
int addr = m_reg[EREG_POINTER] & 0x7ff;
+
+ if ( m_reg[EREG_POINTER] & 0x8000 )
+ {
+ buffer = &m_rx[(rx_fifo_out & ( ETHER_RX_BUFFERS - 1 )) * ETHER_BUFFER_SIZE];
+ }
+ else
+ {
+ buffer = (uint8_t *)&m_tx[(tx_fifo_in & (ETHER_TX_BUFFERS-1))* ETHER_BUFFER_SIZE];;
+ }
+
result = buffer[addr++];
- if (ACCESSING_BITS_8_15)
+ if ( ACCESSING_BITS_8_15 )
result |= buffer[addr++] << 8;
- if (m_reg[EREG_POINTER] & 0x4000)
+ if ( m_reg[EREG_POINTER] & 0x4000 )
m_reg[EREG_POINTER] = (m_reg[EREG_POINTER] & ~0x7ff) | (addr & 0x7ff);
break;
}
}
- if (LOG_ETHERNET && offset != EREG_BANK)
- logerror("%s:smc91c9x_r(%s) = %04X & %04X\n", machine().describe_context(), ethernet_regname[offset], result, mem_mask);
+ if (offset != EREG_BANK)
+ LOG("%s:smc91c9x_r(%s) = %04X & %04X\n", machine().describe_context(), ethernet_regname[offset], result, mem_mask);
return result;
}
@@ -452,95 +702,91 @@ READ16_MEMBER( smc91c9x_device::read )
WRITE16_MEMBER( smc91c9x_device::write )
{
- // uint16_t olddata;
-
/* determine the effective register */
offset %= 8;
if (offset != EREG_BANK)
offset += 8 * (m_reg[EREG_BANK] & 7);
/* update the data generically */
- // olddata = m_reg[offset];
+
+ if (offset != 7 && offset < sizeof(m_reg))
+ LOG("%s:smc91c9x_w(%s) = [%04X]<-%04X & (%04X & %04X)\n", machine().describe_context(), ethernet_regname[offset], offset, data, mem_mask , m_regmask[offset]);
+
mem_mask &= m_regmask[offset];
COMBINE_DATA(&m_reg[offset]);
- if (LOG_ETHERNET && offset != 7)
- logerror("%s:smc91c9x_w(%s) = %04X & %04X\n", machine().describe_context(), ethernet_regname[offset], data, mem_mask);
-
/* handle it */
switch (offset)
{
case EREG_TCR: /* transmit control register */
// Setting Tx Enable clears some status and interrupts
- if (data & 0x1) {
+ if ( data & 0x1 ) {
m_reg[EREG_EPH_STATUS] &= ~0x420;
m_reg[EREG_INTERRUPT] &= ~EINT_EPH;
update_ethernet_irq();
}
- if (LOG_ETHERNET)
- {
- if (data & 0x2000) logerror(" EPH LOOP\n");
- if (data & 0x1000) logerror(" STP SQET\n");
- if (data & 0x0800) logerror(" FDUPLX\n");
- if (data & 0x0400) logerror(" MON_CSN\n");
- if (data & 0x0100) logerror(" NOCRC\n");
- if (data & 0x0080) logerror(" PAD_EN\n");
- if (data & 0x0004) logerror(" FORCOL\n");
- if (data & 0x0002) logerror(" LOOP\n");
- if (data & 0x0001) logerror(" TXENA\n");
- }
+
+ if (data & 0x2000) LOG(" EPH LOOP\n");
+ if (data & 0x1000) LOG(" STP SQET\n");
+ if (data & 0x0800) LOG(" FDUPLX\n");
+ if (data & 0x0400) LOG(" MON_CSN\n");
+ if (data & 0x0100) LOG(" NOCRC\n");
+ if (data & 0x0080) LOG(" PAD_EN\n");
+ if (data & 0x0004) LOG(" FORCOL\n");
+ if (data & 0x0002) LOG(" LOOP\n");
+ if (data & 0x0001) LOG(" TXENA\n");
break;
case EREG_RCR: /* receive control register */
- if (LOG_ETHERNET)
+
+ if ( data & 0x8000 )
{
- if (data & 0x8000) reset();
- if (data & 0x8000) logerror(" SOFT RST\n");
- if (data & 0x4000) logerror(" FILT_CAR\n");
- if (data & 0x0200) logerror(" STRIP CRC\n");
- if (data & 0x0100) logerror(" RXEN\n");
- if (data & 0x0004) logerror(" ALMUL\n");
- if (data & 0x0002) logerror(" PRMS\n");
- if (data & 0x0001) logerror(" RX_ABORT\n");
+ clear_rx_fifo();
+ clear_tx_fifo();
}
- break;
- case EREG_CONFIG: /* configuration register */
- if (LOG_ETHERNET)
+ if ( !(data & 0x0100) )
{
- if (data & 0x1000) logerror(" NO WAIT\n");
- if (data & 0x0400) logerror(" FULL STEP\n");
- if (data & 0x0200) logerror(" SET SQLCH\n");
- if (data & 0x0100) logerror(" AUI SELECT\n");
- if (data & 0x0080) logerror(" 16 BIT\n");
- if (data & 0x0040) logerror(" DIS LINK\n");
- if (data & 0x0004) logerror(" INT SEL1\n");
- if (data & 0x0002) logerror(" INT SEL0\n");
+ clear_rx_fifo();
}
+
+ if (data & 0x8000) reset();
+ if (data & 0x8000) LOG(" SOFT RST\n");
+ if (data & 0x4000) LOG(" FILT_CAR\n");
+ if (data & 0x0200) LOG(" STRIP CRC\n");
+ if (data & 0x0100) LOG(" RXEN\n");
+ if (data & 0x0004) LOG(" ALMUL\n");
+ if (data & 0x0002) LOG(" PRMS\n");
+ if (data & 0x0001) LOG(" RX_ABORT\n");
+ break;
+
+ case EREG_CONFIG: /* configuration register */
+ if (data & 0x1000) LOG(" NO WAIT\n");
+ if (data & 0x0400) LOG(" FULL STEP\n");
+ if (data & 0x0200) LOG(" SET SQLCH\n");
+ if (data & 0x0100) LOG(" AUI SELECT\n");
+ if (data & 0x0080) LOG(" 16 BIT\n");
+ if (data & 0x0040) LOG(" DIS LINK\n");
+ if (data & 0x0004) LOG(" INT SEL1\n");
+ if (data & 0x0002) LOG(" INT SEL0\n");
break;
case EREG_BASE: /* base address register */
- if (LOG_ETHERNET)
- {
- logerror(" base = $%04X\n", (data & 0xe000) | ((data & 0x1f00) >> 3));
- logerror(" romsize = %d\n", ((data & 0xc0) >> 6));
- logerror(" romaddr = $%05X\n", ((data & 0x3e) << 13));
- }
+ LOG(" base = $%04X\n", (data & 0xe000) | ((data & 0x1f00) >> 3));
+ LOG(" romsize = %d\n", ((data & 0xc0) >> 6));
+ LOG(" romaddr = $%05X\n", ((data & 0x3e) << 13));
break;
case EREG_CONTROL: /* control register */
- if (LOG_ETHERNET)
- {
- if (data & 0x4000) logerror(" RCV_BAD\n");
- if (data & 0x2000) logerror(" PWRDN\n");
- if (data & 0x0800) logerror(" AUTO RELEASE\n");
- if (data & 0x0080) logerror(" LE ENABLE\n");
- if (data & 0x0040) logerror(" CR ENABLE\n");
- if (data & 0x0020) logerror(" TE ENABLE\n");
- if (data & 0x0004) logerror(" EEPROM SELECT\n");
- if (data & 0x0002) logerror(" RELOAD\n");
- if (data & 0x0001) logerror(" STORE\n");
- }
+ if (data & 0x4000) LOG(" RCV_BAD\n");
+ if (data & 0x2000) LOG(" PWRDN\n");
+ if (data & 0x0800) LOG(" AUTO RELEASE\n");
+ if (data & 0x0080) LOG(" LE ENABLE\n");
+ if (data & 0x0040) LOG(" CR ENABLE\n");
+ if (data & 0x0020) LOG(" TE ENABLE\n");
+ if (data & 0x0004) LOG(" EEPROM SELECT\n");
+ if (data & 0x0002) LOG(" RELOAD\n");
+ if (data & 0x0001) LOG(" STORE\n");
break;
case EREG_MMU_COMMAND: /* command register */
@@ -550,12 +796,22 @@ WRITE16_MEMBER( smc91c9x_device::write )
case EREG_DATA_0: /* data register */
case EREG_DATA_1: /* data register */
{
- uint8_t *buffer = (m_reg[EREG_POINTER] & 0x8000) ? m_rx : m_tx;
+ uint8_t *buffer;
int addr = m_reg[EREG_POINTER] & 0x7ff;
+
+ if ( m_reg[EREG_POINTER] & 0x8000 )
+ {
+ buffer = &m_rx[(rx_fifo_out & ( ETHER_RX_BUFFERS - 1 )) * ETHER_BUFFER_SIZE];
+ }
+ else
+ {
+ buffer = (uint8_t *)&m_tx[(tx_fifo_in & (ETHER_TX_BUFFERS-1))* ETHER_BUFFER_SIZE];;
+ }
+
buffer[addr++] = data;
- if (ACCESSING_BITS_8_15)
+ if ( ACCESSING_BITS_8_15 )
buffer[addr++] = data >> 8;
- if (m_reg[EREG_POINTER] & 0x4000)
+ if ( m_reg[EREG_POINTER] & 0x4000 )
m_reg[EREG_POINTER] = (m_reg[EREG_POINTER] & ~0x7ff) | (addr & 0x7ff);
break;
}
@@ -563,7 +819,7 @@ WRITE16_MEMBER( smc91c9x_device::write )
case EREG_INTERRUPT:
m_reg[EREG_INTERRUPT] &= ~(data & 0x56);
// Need to clear tx int here for vegas cartfury
- if (m_reg[EREG_FIFO_PORTS] & 0x0080)
+ if ( m_reg[EREG_FIFO_PORTS] & 0x0080 )
m_reg[EREG_INTERRUPT] &= ~EINT_TX;
update_ethernet_irq();
break;
diff --git a/src/devices/machine/smc91c9x.h b/src/devices/machine/smc91c9x.h
index 78627fb58d3..9ff6229d466 100644
--- a/src/devices/machine/smc91c9x.h
+++ b/src/devices/machine/smc91c9x.h
@@ -4,7 +4,7 @@
SMC91C9X ethernet controller implementation
- by Aaron Giles
+ by Aaron Giles, Jean-François DEL NERO
**************************************************************************/
@@ -15,7 +15,7 @@
TYPE DEFINITIONS
***************************************************************************/
-class smc91c9x_device : public device_t
+class smc91c9x_device : public device_t,public device_network_interface
{
public:
template <class Object> static devcb_base &set_irq_callback(device_t &device, Object &&cb) { return downcast<smc91c9x_device &>(device).m_irq_handler.set_callback(std::forward<Object>(cb)); }
@@ -23,6 +23,8 @@ public:
DECLARE_READ16_MEMBER( read );
DECLARE_WRITE16_MEMBER( write );
+ virtual void recv_cb(uint8_t *data, int length) override;
+
protected:
smc91c9x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
@@ -32,7 +34,9 @@ protected:
private:
static constexpr unsigned ETHER_BUFFER_SIZE = 2048;
- static constexpr unsigned ETHER_RX_BUFFERS = 4;
+ static constexpr unsigned ETHER_RX_BUFFERS = 16;
+ static constexpr unsigned ETHER_TX_BUFFERS = 16;
+ static constexpr unsigned ETHERNET_ADDR_SIZE = 6;
// internal state
devcb_write_line m_irq_handler;
@@ -51,19 +55,29 @@ private:
uint8_t m_alloc_count;
/* transmit/receive FIFOs */
- uint8_t m_fifo_count;
+ uint32_t rx_fifo_out;
+ uint32_t rx_fifo_in;
uint8_t m_rx[ETHER_BUFFER_SIZE * ETHER_RX_BUFFERS];
- uint8_t m_tx[ETHER_BUFFER_SIZE];
+
+ uint32_t tx_fifo_out;
+ uint32_t tx_fifo_in;
+ uint8_t m_tx[ETHER_BUFFER_SIZE * ETHER_TX_BUFFERS];
/* counters */
uint32_t m_sent;
uint32_t m_recd;
+ int ethernet_packet_is_for_me(const uint8_t mac_address[]);
+ int is_broadcast(uint8_t mac_address[]);
+
void update_ethernet_irq();
void update_stats();
- TIMER_CALLBACK_MEMBER(finish_enqueue);
+
void process_command(uint16_t data);
- emu_timer* m_tx_timer;
+ void clear_tx_fifo();
+ void clear_rx_fifo();
+
+ int send_frame();
};
diff --git a/src/devices/machine/smpc.cpp b/src/devices/machine/smpc.cpp
index 85a49c5313b..ae017133bcc 100644
--- a/src/devices/machine/smpc.cpp
+++ b/src/devices/machine/smpc.cpp
@@ -179,7 +179,7 @@ DEFINE_DEVICE_TYPE(SMPC_HLE, smpc_hle_device, "smpc_hle", "Sega Saturn SMPC HLE
// TODO: use DEVICE_ADDRESS_MAP once this fatalerror is fixed:
// "uplift_submaps unhandled case: range straddling slots."
static ADDRESS_MAP_START( smpc_regs, 0, 8, smpc_hle_device )
-// ADDRESS_MAP_UNMAP_HIGH
+// ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(0x00, 0x0d) AM_WRITE(ireg_w)
AM_RANGE(0x1f, 0x1f) AM_WRITE(command_register_w)
AM_RANGE(0x20, 0x5f) AM_READ(oreg_r)
@@ -205,7 +205,7 @@ smpc_hle_device::smpc_hle_device(const machine_config &mconfig, const char *tag,
: device_t(mconfig, SMPC_HLE, tag, owner, clock),
device_memory_interface(mconfig, *this),
m_space_config("regs", ENDIANNESS_LITTLE, 8, 7, 0, nullptr, *ADDRESS_MAP_NAME(smpc_regs)),
- m_mini_nvram(*this, "smem"),
+ m_mini_nvram(*this, "smem"),
m_mshres(*this),
m_mshnmi(*this),
m_sshres(*this),
@@ -231,7 +231,7 @@ void smpc_hle_device::static_set_region_code(device_t &device, uint8_t rgn)
{
smpc_hle_device &dev = downcast<smpc_hle_device &>(device);
dev.m_region_code = rgn;
-}
+}
void smpc_hle_device::static_set_control_port_tags(device_t &device, const char *tag1, const char *tag2)
{
@@ -249,7 +249,7 @@ void smpc_hle_device::static_set_control_port_tags(device_t &device, const char
MACHINE_CONFIG_MEMBER(smpc_hle_device::device_add_mconfig)
MCFG_NVRAM_ADD_0FILL("smem")
-
+
// TODO: custom RTC subdevice
MACHINE_CONFIG_END
@@ -261,11 +261,11 @@ void smpc_hle_device::device_start()
{
system_time systime;
machine().base_datetime(systime);
-
-// check if SMEM has valid data via byte 4 in the array, if not then simulate a battery backup fail
+
+// check if SMEM has valid data via byte 4 in the array, if not then simulate a battery backup fail
// (-> call the RTC / Language select menu for Saturn)
m_mini_nvram->set_base(&m_smem, 5);
-
+
m_mshres.resolve_safe();
m_mshnmi.resolve_safe();
m_sshres.resolve_safe();
@@ -274,12 +274,12 @@ void smpc_hle_device::device_start()
m_syshalt.resolve_safe();
m_dotsel.resolve_safe();
m_irq_line.resolve_safe();
-
+
m_pdr1_read.resolve_safe(0xff);
m_pdr2_read.resolve_safe(0xff);
m_pdr1_write.resolve_safe();
m_pdr2_write.resolve_safe();
-
+
save_item(NAME(m_sf));
save_item(NAME(m_sr));
save_item(NAME(m_ddr1));
@@ -299,15 +299,15 @@ void smpc_hle_device::device_start()
save_item(NAME(m_pmode));
save_item(NAME(m_rtc_data));
save_item(NAME(m_smem));
-
+
m_cmd_timer = timer_alloc(COMMAND_ID);
m_rtc_timer = timer_alloc(RTC_ID);
m_intback_timer = timer_alloc(INTBACK_ID);
m_sndres_timer = timer_alloc(SNDRES_ID);
-
-// TODO: tag-ify, needed when SCU will be a device
+
+// TODO: tag-ify, needed when SCU will be a device
m_screen = machine().first_screen();
-
+
m_rtc_data[0] = DectoBCD(systime.local_time.year / 100);
m_rtc_data[1] = DectoBCD(systime.local_time.year % 100);
m_rtc_data[2] = (systime.local_time.weekday << 4) | (systime.local_time.month+1);
@@ -315,10 +315,13 @@ void smpc_hle_device::device_start()
m_rtc_data[4] = DectoBCD(systime.local_time.hour);
m_rtc_data[5] = DectoBCD(systime.local_time.minute);
m_rtc_data[6] = DectoBCD(systime.local_time.second);
-
- m_ctrl1 = downcast<saturn_control_port_device *>(machine().device(m_ctrl1_tag));
- m_ctrl2 = downcast<saturn_control_port_device *>(machine().device(m_ctrl2_tag));
-// m_has_ctrl_ports = (m_ctrl1 != nullptr && m_ctrl2 != nullptr);
+
+ if (m_has_ctrl_ports)
+ {
+ m_ctrl1 = downcast<saturn_control_port_device *>(machine().device(m_ctrl1_tag));
+ m_ctrl2 = downcast<saturn_control_port_device *>(machine().device(m_ctrl2_tag));
+ }
+// m_has_ctrl_ports = (m_ctrl1 != nullptr && m_ctrl2 != nullptr);
}
@@ -335,10 +338,10 @@ void smpc_hle_device::device_reset()
m_ddr2 = 0;
m_pdr1_readback = 0;
m_pdr2_readback = 0;
-
+
memset(m_ireg,0,7);
memset(m_oreg,0,32);
-
+
m_cmd_timer->reset();
m_intback_timer->reset();
m_sndres_timer->reset();
@@ -346,7 +349,7 @@ void smpc_hle_device::device_reset()
m_command_in_progress = false;
m_NMI_reset = false;
m_cur_dotsel = false;
-
+
m_rtc_timer->adjust(attotime::zero, 0, attotime::from_seconds(1));
}
@@ -366,10 +369,10 @@ WRITE8_MEMBER( smpc_hle_device::ireg_w )
{
if (!(offset & 1)) // avoid writing to even bytes
return;
-
+
m_ireg[offset >> 1] = data;
-
- if(offset == 1) // check if we are under intback
+
+ if(offset == 1) // check if we are under intback
{
if(m_intback_stage)
{
@@ -382,8 +385,8 @@ WRITE8_MEMBER( smpc_hle_device::ireg_w )
else if(data & 0x80)
{
if(LOG_PAD_CMD) printf("SMPC: CONTINUE request\n");
-
- m_intback_timer->adjust(attotime::from_usec(700)); // TODO: is timing correct?
+
+ m_intback_timer->adjust(attotime::from_usec(700)); // TODO: is timing correct?
// TODO: following looks wrong here
m_oreg[31] = 0x10;
@@ -394,7 +397,7 @@ WRITE8_MEMBER( smpc_hle_device::ireg_w )
}
READ8_MEMBER( smpc_hle_device::oreg_r )
-{
+{
if (!(offset & 1)) // avoid reading to even bytes (TODO: is it 0s or 1s?)
return 0x00;
@@ -421,14 +424,14 @@ WRITE8_MEMBER( smpc_hle_device::status_flag_w )
READ8_MEMBER( smpc_hle_device::pdr1_r )
{
uint8_t res = (m_pdr1_read() & ~m_ddr1) | m_pdr1_readback;
-
+
return res;
}
READ8_MEMBER( smpc_hle_device::pdr2_r )
{
uint8_t res = (m_pdr2_read() & ~m_ddr2) | m_pdr2_readback;
-
+
return res;
}
@@ -537,26 +540,26 @@ WRITE8_MEMBER( smpc_hle_device::write )
WRITE8_MEMBER( smpc_hle_device::command_register_w )
{
-// don't send a command if previous one is still in progress
+// don't send a command if previous one is still in progress
// ST-V tries to send a sysres command if OREG31 doesn't return the ack command
if(m_command_in_progress == true)
return;
-
+
m_comreg = data & 0x1f;
-
+
if(data & 0xe0)
logerror("%s COMREG = %02x!?\n",this->tag(),data);
-
+
m_command_in_progress = true;
if(m_comreg == 0x0e || m_comreg == 0x0f)
{
/* on ST-V timing of this is pretty fussy, you get 2 credits at start-up otherwise
- * My current theory is that the PLL device can halt the whole system until the frequency change occurs.
- * (cfr. diagram on page 3 of SMPC manual)
+ * My current theory is that the PLL device can halt the whole system until the frequency change occurs.
+ * (cfr. diagram on page 3 of SMPC manual)
* I really don't think that the system can do an usable mid-frame clock switching anyway.
*/
m_syshalt(1);
-
+
m_cmd_timer->adjust(m_screen->time_until_pos(m_screen->visible_area().max_y,0));
}
else if(m_comreg == 0x10)
@@ -564,7 +567,7 @@ WRITE8_MEMBER( smpc_hle_device::command_register_w )
// copy ireg to our intback buffer
for(int i=0;i<3;i++)
m_intback_buf[i] = m_ireg[i];
-
+
// calculate the timing for intback command
int timing;
@@ -573,12 +576,12 @@ WRITE8_MEMBER( smpc_hle_device::command_register_w )
if( m_ireg[0] != 0) // non-peripheral data
timing += 8;
- // TODO: At vblank-out actually ...
+ // TODO: At vblank-out actually ...
if( m_ireg[1] & 8) // peripheral data
timing += 700;
-
+
// TODO: check against ireg2, must be 0xf0
-
+
m_cmd_timer->adjust(attotime::from_usec(timing));
}
else
@@ -604,7 +607,7 @@ void smpc_hle_device::device_timer(emu_timer &timer, device_timer_id id, int par
// enable or disable Slave SH2
m_sshres(m_comreg & 1);
break;
-
+
case 0x06: // SNDON
case 0x07: // SNDOFF
// enable or disable 68k
@@ -618,15 +621,15 @@ void smpc_hle_device::device_timer(emu_timer &timer, device_timer_id id, int par
m_oreg[31] = m_comreg;
sf_ack(true); //clear hand-shake flag (TODO: diagnostic wants this to have bit 3 high)
return;
-
-// case 0x0a: // NETLINKON
-// case 0x0b: // NETLINKOFF
-
+
+// case 0x0a: // NETLINKON
+// case 0x0b: // NETLINKOFF
+
case 0x0d: // SYSRES
// send a 1 -> 0 to device reset lines
m_sysres(1);
m_sysres(0);
-
+
// send a 1 -> 0 transition to reset line (was PULSE_LINE)
m_mshres(1);
m_mshres(0);
@@ -635,7 +638,7 @@ void smpc_hle_device::device_timer(emu_timer &timer, device_timer_id id, int par
case 0x0e: // CKCHG352
case 0x0f: // CKCHG320
m_dotsel(m_comreg & 1);
-
+
// send a NMI to Master SH2 if enabled
if(m_NMI_reset == false)
master_sh2_nmi();
@@ -644,22 +647,22 @@ void smpc_hle_device::device_timer(emu_timer &timer, device_timer_id id, int par
m_sshres(1);
// clear PLL system halt
m_syshalt(0);
-
+
// setup the new dot select
m_cur_dotsel = (m_comreg & 1) ^ 1;
break;
case 0x10: // INTBACK
resolve_intback();
- return;
-
+ return;
+
case 0x16: // SETTIME
{
for(int i=0;i<7;i++)
m_rtc_data[i] = m_ireg[i];
break;
}
-
+
case 0x17: // SETSMEM
{
for(int i=0;i<4;i++)
@@ -669,7 +672,7 @@ void smpc_hle_device::device_timer(emu_timer &timer, device_timer_id id, int par
m_smem[4] = 0xff;
break;
}
-
+
case 0x18: // NMIREQ
// NMI is unconditionally requested
master_sh2_nmi();
@@ -709,11 +712,11 @@ void smpc_hle_device::device_timer(emu_timer &timer, device_timer_id id, int par
void smpc_hle_device::resolve_intback()
{
int i;
-
+
m_command_in_progress = false;
if(m_intback_buf[0] != 0)
- {
+ {
m_oreg[0] = ((m_smem[4] & 0x80) | ((m_NMI_reset & 1) << 6));
for(i=0;i<7;i++)
@@ -722,11 +725,11 @@ void smpc_hle_device::resolve_intback()
m_oreg[8] = 0; // CTG0 / CTG1?
m_oreg[9] = m_region_code; // TODO: system region on Saturn
-
+
/*
0-11 -1-- unknown
-x-- ---- VDP2 dot select
- ---- x--- MSHNMI
+ ---- x--- MSHNMI
---- --x- SYSRES
---- ---x SOUNDRES
*/
@@ -738,7 +741,7 @@ void smpc_hle_device::resolve_intback()
1 << 2 |
0 << 1 |
0 << 0;
-
+
m_oreg[11] = 0 << 6; // CDRES
for(i=0;i<4;i++)
@@ -752,7 +755,7 @@ void smpc_hle_device::resolve_intback()
m_pmode = m_intback_buf[0]>>4;
irq_request();
-
+
// put issued command in OREG31
m_oreg[31] = 0x10; // TODO: doc says 0?
/* clear hand-shake flag */
@@ -777,7 +780,7 @@ void smpc_hle_device::intback_continue_request()
{
if( m_has_ctrl_ports == true )
read_saturn_ports();
-
+
if (m_intback_stage == 2)
{
sr_set(0x80 | m_pmode); // pad 2, no more data, echo back pad mode set by intback
@@ -790,8 +793,8 @@ void smpc_hle_device::intback_continue_request()
}
irq_request();
- m_oreg[31] = 0x10; // callback for last command issued
- sf_ack(false);
+ m_oreg[31] = 0x10; // callback for last command issued
+ sf_ack(false);
}
int smpc_hle_device::DectoBCD(int num)
@@ -937,21 +940,21 @@ void smpc_hle_device::read_saturn_ports()
for (int i = 0; i < (status1 & 0xf); i++)
{
uint8_t id = m_ctrl1->read_id(i);
-
+
m_oreg[reg_offset++] = id;
for (int j = 0; j < (id & 0xf); j++)
m_oreg[reg_offset++] = m_ctrl1->read_ctrl(j + ctrl1_offset);
ctrl1_offset += (id & 0xf);
}
-
+
m_oreg[reg_offset++] = status2;
// read ctrl2
for (int i = 0; i < (status2 & 0xf); i++)
{
uint8_t id = m_ctrl2->read_id(i);
-
+
m_oreg[reg_offset++] = id;
for (int j = 0; j < (id & 0xf); j++)
diff --git a/src/devices/machine/smpc.h b/src/devices/machine/smpc.h
index 99d857acff5..c3977db9015 100644
--- a/src/devices/machine/smpc.h
+++ b/src/devices/machine/smpc.h
@@ -28,7 +28,7 @@
#define MCFG_SMPC_HLE_PDR2_IN_CB(_devcb) \
devcb = &smpc_hle_device::set_pdr2_in_handler(*device, DEVCB_##_devcb);
-
+
#define MCFG_SMPC_HLE_PDR1_OUT_CB(_devcb) \
devcb = &smpc_hle_device::set_pdr1_out_handler(*device, DEVCB_##_devcb);
@@ -75,7 +75,7 @@ public:
smpc_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// I/O operations
-// DECLARE_ADDRESS_MAP( io_map, 8);
+// DECLARE_ADDRESS_MAP( io_map, 8);
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
@@ -95,11 +95,11 @@ public:
DECLARE_WRITE8_MEMBER( exle_w );
DECLARE_INPUT_CHANGED_MEMBER( trigger_nmi_r );
- void m68k_reset_trigger();
+ void m68k_reset_trigger();
bool get_iosel(bool which);
uint8_t get_ddr(bool which);
-
-// system delegation
+
+// system delegation
template <class Object> static devcb_base &set_master_reset_handler(device_t &device, Object &&cb) { return downcast<smpc_hle_device &>(device).m_mshres.set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &set_master_nmi_handler(device_t &device, Object &&cb) { return downcast<smpc_hle_device &>(device).m_mshnmi.set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &set_slave_reset_handler(device_t &device, Object &&cb) { return downcast<smpc_hle_device &>(device).m_sshres.set_callback(std::forward<Object>(cb)); }
@@ -109,7 +109,7 @@ public:
template <class Object> static devcb_base &set_dot_select_handler(device_t &device, Object &&cb) { return downcast<smpc_hle_device &>(device).m_dotsel.set_callback(std::forward<Object>(cb)); }
-// PDR delegation
+// PDR delegation
template <class Object> static devcb_base &set_pdr1_in_handler(device_t &device, Object &&cb) { return downcast<smpc_hle_device &>(device).m_pdr1_read.set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &set_pdr2_in_handler(device_t &device, Object &&cb) { return downcast<smpc_hle_device &>(device).m_pdr2_read.set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &set_pdr1_out_handler(device_t &device, Object &&cb) { return downcast<smpc_hle_device &>(device).m_pdr1_write.set_callback(std::forward<Object>(cb)); }
@@ -123,7 +123,7 @@ public:
protected:
// device-level overrides
-// virtual void device_validity_check(validity_checker &valid) const override;
+// virtual void device_validity_check(validity_checker &valid) const override;
virtual void device_add_mconfig(machine_config &config) override;
virtual void device_start() override;
virtual void device_reset() override;
@@ -147,7 +147,7 @@ private:
const char *m_ctrl1_tag;
const char *m_ctrl2_tag;
bool m_has_ctrl_ports;
-
+
bool m_sf;
bool m_cd_sf;
uint8_t m_sr;
@@ -163,8 +163,8 @@ private:
uint8_t m_comreg;
// in usec
// timing table, from manual in usec
- const uint32_t m_cmd_table_timing[0x20] =
- {
+ const uint32_t m_cmd_table_timing[0x20] =
+ {
30, 30, // MASTER ON / OFF
30, 30, // SLAVE ON / OFF
10, 10, // <unknown>
@@ -188,7 +188,7 @@ private:
void intback_continue_request();
void handle_rtc_increment();
void read_saturn_ports();
-
+
void sr_set(uint8_t data);
void sr_ack();
void sf_ack(bool cd_enable);
@@ -202,10 +202,10 @@ private:
devcb_write_line m_mshres;
devcb_write_line m_mshnmi;
devcb_write_line m_sshres;
-// devcb_write_line m_sshnmi;
+// devcb_write_line m_sshnmi;
devcb_write_line m_sndres;
devcb_write_line m_sysres;
-// devcb_write_line m_cdres;
+// devcb_write_line m_cdres;
devcb_write_line m_syshalt;
devcb_write_line m_dotsel;
devcb_read8 m_pdr1_read;
diff --git a/src/devices/machine/stvcd.cpp b/src/devices/machine/stvcd.cpp
index 21b2128a68d..87b3b9e344c 100644
--- a/src/devices/machine/stvcd.cpp
+++ b/src/devices/machine/stvcd.cpp
@@ -1383,7 +1383,7 @@ void saturn_state::cd_exec_command( void )
if(cr2 == 0x0001) // MPEG card
cr2 = 0x2;
else
- cr2 = 0x4; // 0 = No CD, 1 = Audio CD, 2 Regular Data disk (not Saturn), 3 pirate disc, 4 Saturn disc
+ cr2 = 0x4; // 0 = No CD, 1 = Audio CD, 2 Regular Data disk (not Saturn), 3 pirate disc, 4 Saturn disc
cr3 = 0;
cr4 = 0;
hirqreg |= (CMOK);
@@ -1410,19 +1410,19 @@ void saturn_state::cd_exec_command( void )
cr4 = 0;
hirqreg |= (CMOK);
break;
-
+
// MPEG init
case 0x93:
hirqreg |= (CMOK|MPED);
if(cr2 == 0x0001)
hirqreg |= (MPCM);
-
+
cr1 = cd_stat;
cr2 = 0;
cr3 = 0;
cr4 = 0;
break;
-
+
default:
CDROM_LOG(("CD: Unknown command %04x\n", cr1>>8))
popmessage("CD Block unknown command %02x, contact MAMEdev",cr1>>8);
@@ -1520,9 +1520,9 @@ void saturn_state::stvcd_reset( void )
}
// open device
- if (cdrom)
+ //if (cdrom)
{
- cdrom_close(cdrom);
+ //cdrom_close(cdrom);
cdrom = (cdrom_file *)nullptr;
}
@@ -2720,6 +2720,7 @@ void saturn_state::stvcd_set_tray_close( void )
return;
hirqreg |= DCHG;
+ cdrom = (cdrom_file *)nullptr;
cdrom_image_device *cddevice = machine().device<cdrom_image_device>("cdrom");
if (cddevice!=nullptr)
diff --git a/src/devices/machine/terminal.cpp b/src/devices/machine/terminal.cpp
index 2eca017ec6c..4661e085716 100644
--- a/src/devices/machine/terminal.cpp
+++ b/src/devices/machine/terminal.cpp
@@ -116,7 +116,7 @@ static const uint8_t terminal_font[256*16] =
0x00, 0x02, 0x02, 0x7a, 0x86, 0x82, 0x86, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7c, 0x82, 0xfe, 0x80, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x22, 0x20, 0xf8, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x7c, 0x84, 0x84, 0x7c, 0x04, 0x84, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,//g
+ 0x00, 0x00, 0x00, 0x7c, 0x84, 0x84, 0x7c, 0x04, 0x84, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x80, 0xbc, 0xc2, 0x82, 0x82, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x84, 0x84, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -339,7 +339,7 @@ MACHINE_CONFIG_MEMBER( generic_terminal_device::device_add_mconfig )
MCFG_SPEAKER_STANDARD_MONO("bell")
MCFG_SOUND_ADD("beeper", BEEP, 2'000)
- MCFG_SOUND_ROUTE(ALL_OUTPUTS, "bell", 0.50)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "bell", 0.25)
MACHINE_CONFIG_END
void generic_terminal_device::device_start()
diff --git a/src/devices/machine/upd4701.cpp b/src/devices/machine/upd4701.cpp
index 0875ce31861..7a02e8bf1e4 100644
--- a/src/devices/machine/upd4701.cpp
+++ b/src/devices/machine/upd4701.cpp
@@ -354,11 +354,12 @@ READ8_MEMBER(upd4701_device::read_y)
READ8_MEMBER(upd4701_device::read_xy)
{
+ bool old_cs = m_cs;
cs_w(0);
xy_w(BIT(offset, 1));
ul_w(BIT(offset, 0));
u8 result = d_r(space, 0);
- cs_w(1);
+ cs_w(old_cs);
return result;
}
diff --git a/src/devices/machine/vrc5074.cpp b/src/devices/machine/vrc5074.cpp
index ca4b1540df7..8471d631c47 100644
--- a/src/devices/machine/vrc5074.cpp
+++ b/src/devices/machine/vrc5074.cpp
@@ -877,6 +877,8 @@ WRITE32_MEMBER(vrc5074_device::cpu_reg_w)
map_cpu_space();
break;
case NREG_CPUSTAT + 0: /* CPU status */
+ if (data & 0x1) logerror("cpu_reg_w: System Cold Reset\n");
+ if (data & 0x2) logerror("cpu_reg_w: CPU Warm Reset\n");
case NREG_CPUSTAT + 1: /* CPU status */
if (LOG_NILE) logerror("%08X:NILE WRITE: CPU status(%03X) = %08X & %08X\n", m_cpu_space->device().safe_pc(), offset * 4, data, mem_mask);
logit = 0;
@@ -1003,7 +1005,7 @@ READ32_MEMBER(vrc5074_device::serial_r)
{
uint32_t result = m_uart->ins8250_r(space, offset>>1);
- if (LOG_NILE)
+ if (0 && LOG_NILE)
logerror("%06X:serial_r offset %03X = %08X (%08x)\n", m_cpu_space->device().safe_pc(), offset>>1, result, offset*4);
return result;
}
@@ -1011,8 +1013,17 @@ READ32_MEMBER(vrc5074_device::serial_r)
WRITE32_MEMBER(vrc5074_device::serial_w)
{
m_uart->ins8250_w(space, offset>>1, data);
- if (PRINTF_SERIAL && offset == NREG_UARTTHR)
+ if (PRINTF_SERIAL && offset == NREG_UARTTHR) {
+ static std::string debugStr;
printf("%c", data);
- if (LOG_NILE)
+ if (data == 0xd || debugStr.length()>=80) {
+ logerror("%s", debugStr.c_str());
+ debugStr.clear();
+ }
+ else {
+ debugStr += char(data);
+ }
+ }
+ if (0 && LOG_NILE)
logerror("%06X:serial_w offset %03X = %08X & %08X (%08x)\n", m_cpu_space->device().safe_pc(), offset>>1, data, mem_mask, offset*4);
}
diff --git a/src/devices/machine/wd_fdc.cpp b/src/devices/machine/wd_fdc.cpp
index f629fdc08bd..8baeff7edf0 100644
--- a/src/devices/machine/wd_fdc.cpp
+++ b/src/devices/machine/wd_fdc.cpp
@@ -200,15 +200,15 @@ void wd_fdc_device_base::set_floppy(floppy_image_device *_floppy)
ready_callback(floppy, next_ready);
}
-void wd_fdc_device_base::dden_w(bool _dden)
+WRITE_LINE_MEMBER(wd_fdc_device_base::dden_w)
{
if(disable_mfm) {
logerror("Error, this chip does not have a dden line\n");
return;
}
- if(dden != _dden) {
- dden = _dden;
+ if(dden != bool(state)) {
+ dden = bool(state);
if (TRACE_LINES) logerror("select %s\n", dden ? "fm" : "mfm");
}
}
@@ -1368,27 +1368,27 @@ void wd_fdc_device_base::index_callback(floppy_image_device *floppy, int state)
general_continue();
}
-bool wd_fdc_device_base::intrq_r()
+READ_LINE_MEMBER(wd_fdc_device_base::intrq_r)
{
return intrq;
}
-bool wd_fdc_device_base::drq_r()
+READ_LINE_MEMBER(wd_fdc_device_base::drq_r)
{
return drq;
}
-bool wd_fdc_device_base::hld_r()
+READ_LINE_MEMBER(wd_fdc_device_base::hld_r)
{
return hld;
}
-void wd_fdc_device_base::hlt_w(bool state)
+WRITE_LINE_MEMBER(wd_fdc_device_base::hlt_w)
{
- hlt = state;
+ hlt = bool(state);
}
-bool wd_fdc_device_base::enp_r()
+READ_LINE_MEMBER(wd_fdc_device_base::enp_r)
{
return enp;
}
diff --git a/src/devices/machine/wd_fdc.h b/src/devices/machine/wd_fdc.h
index e3811b003f1..a5d3006e3b1 100644
--- a/src/devices/machine/wd_fdc.h
+++ b/src/devices/machine/wd_fdc.h
@@ -145,7 +145,7 @@ public:
void soft_reset();
- void dden_w(bool dden);
+ DECLARE_WRITE_LINE_MEMBER(dden_w);
void set_floppy(floppy_image_device *floppy);
void set_force_ready(bool force_ready);
void set_disable_motor_control(bool _disable_motor_control);
@@ -175,13 +175,13 @@ public:
DECLARE_READ8_MEMBER( read ) { return gen_r(offset); }
DECLARE_WRITE8_MEMBER( write ) { gen_w(offset,data); }
- bool intrq_r();
- bool drq_r();
+ DECLARE_READ_LINE_MEMBER(intrq_r);
+ DECLARE_READ_LINE_MEMBER(drq_r);
- bool hld_r();
- void hlt_w(bool state);
+ DECLARE_READ_LINE_MEMBER(hld_r);
+ DECLARE_WRITE_LINE_MEMBER(hlt_w);
- bool enp_r();
+ DECLARE_READ_LINE_MEMBER(enp_r);
void index_callback(floppy_image_device *floppy, int state);
diff --git a/src/devices/machine/x2212.cpp b/src/devices/machine/x2212.cpp
index 04a9ee5a1c9..cae221b6f94 100644
--- a/src/devices/machine/x2212.cpp
+++ b/src/devices/machine/x2212.cpp
@@ -31,8 +31,8 @@ ADDRESS_MAP_END
//**************************************************************************
// device type definition
-DEFINE_DEVICE_TYPE(X2212, x2212_device, "x2212", "Xicor X2212 256x4 NVRAM")
-DEFINE_DEVICE_TYPE(X2210, x2210_device, "x2210", "Xicor X2210 64x4 NVRAM")
+DEFINE_DEVICE_TYPE(X2212, x2212_device, "x2212", "Xicor X2212 256x4 NOVRAM")
+DEFINE_DEVICE_TYPE(X2210, x2210_device, "x2210", "Xicor X2210 64x4 NOVRAM")
//-------------------------------------------------
// x2212_device - constructor
@@ -209,7 +209,7 @@ READ8_MEMBER( x2212_device::read )
//-------------------------------------------------
// store - set the state of the store line
-// (active high)
+// (FIXME: actually active low, not active high)
//-------------------------------------------------
WRITE_LINE_MEMBER( x2212_device::store )
@@ -222,7 +222,7 @@ WRITE_LINE_MEMBER( x2212_device::store )
//-------------------------------------------------
// recall - set the state of the recall line
-// (active high)
+// (FIXME: actually active low, not active high)
//-------------------------------------------------
WRITE_LINE_MEMBER( x2212_device::recall )
diff --git a/src/devices/machine/z80scc.cpp b/src/devices/machine/z80scc.cpp
index 805018d00f2..af412fec149 100644
--- a/src/devices/machine/z80scc.cpp
+++ b/src/devices/machine/z80scc.cpp
@@ -81,18 +81,18 @@ DONE (x) (p=partly) NMOS CMOS ESCC EMSCC
#define LOG_GENERAL (1U << 0)
#define LOG_SETUP (1U << 1)
-#define LOG_PRINTF (1U << 2)
-#define LOG_READ (1U << 3)
-#define LOG_INT (1U << 4)
-#define LOG_CMD (1U << 5)
-#define LOG_TX (1U << 6)
-#define LOG_RCV (1U << 7)
-#define LOG_CTS (1U << 8)
-#define LOG_DCD (1U << 9)
-#define LOG_SYNC (1U << 10)
-
-//#define VERBOSE (LOG_CMD|LOG_INT|LOG_SETUP|LOG_TX|LOG_RCV|LOG_READ|LOG_CTS|LOG_DCD)
-//#define LOG_OUTPUT_FUNC printf
+#define LOG_READ (1U << 2)
+#define LOG_INT (1U << 3)
+#define LOG_CMD (1U << 4)
+#define LOG_TX (1U << 5)
+#define LOG_RCV (1U << 6)
+#define LOG_CTS (1U << 7)
+#define LOG_DCD (1U << 8)
+#define LOG_SYNC (1U << 9)
+
+//#define VERBOSE (LOG_TX)
+//#define LOG_OUTPUT_STREAM std::cout
+
#include "logmacro.h"
#define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__)
@@ -112,10 +112,8 @@ DONE (x) (p=partly) NMOS CMOS ESCC EMSCC
#ifdef _MSC_VER
#define FUNCNAME __func__
-#define LLFORMAT "%I64d"
#else
#define FUNCNAME __PRETTY_FUNCTION__
-#define LLFORMAT "%lld"
#endif
/* LOCAL _BRG is set in z80scc.h, local timer based BRG is not complete and will be removed if not needed for synchrounous mode */
@@ -130,6 +128,231 @@ DONE (x) (p=partly) NMOS CMOS ESCC EMSCC
#define CHANA_TAG "cha"
#define CHANB_TAG "chb"
+enum : uint8_t
+{
+ RR0_RX_CHAR_AVAILABLE = 0x01,
+ RR0_ZC = 0x02,
+ RR0_TX_BUFFER_EMPTY = 0x04,
+ RR0_DCD = 0x08,
+ RR0_SYNC_HUNT = 0x10,
+ RR0_CTS = 0x20,
+ RR0_TX_UNDERRUN = 0x40,
+ RR0_BREAK_ABORT = 0x80
+};
+
+enum : uint8_t
+{
+ RR1_ALL_SENT = 0x01,
+ RR1_RESIDUE_CODE_MASK = 0x0e,
+ RR1_PARITY_ERROR = 0x10,
+ RR1_RX_OVERRUN_ERROR = 0x20,
+ RR1_CRC_FRAMING_ERROR = 0x40,
+ RR1_END_OF_FRAME = 0x80
+};
+
+enum : uint8_t
+{
+ RR3_CHANB_EXT_IP = 0x01, // SCC IP pending registers
+ RR3_CHANB_TX_IP = 0x02, // only read in Channel A (for both channels)
+ RR3_CHANB_RX_IP = 0x04, // channel B return all zero
+ RR3_CHANA_EXT_IP = 0x08,
+ RR3_CHANA_TX_IP = 0x10,
+ RR3_CHANA_RX_IP = 0x20
+};
+
+// Universal Bus WR0 commands for 85X30
+enum : uint8_t
+{
+ WR0_REGISTER_MASK = 0x07,
+ WR0_COMMAND_MASK = 0x38, // COMMANDS
+ WR0_NULL = 0x00, // 0 0 0
+ WR0_POINT_HIGH = 0x08, // 0 0 1
+ WR0_RESET_EXT_STATUS = 0x10, // 0 1 0
+ WR0_SEND_ABORT = 0x18, // 0 1 1
+ WR0_ENABLE_INT_NEXT_RX = 0x20, // 1 0 0
+ WR0_RESET_TX_INT = 0x28, // 1 0 1
+ WR0_ERROR_RESET = 0x30, // 1 1 0
+ WR0_RESET_HIGHEST_IUS = 0x38, // 1 1 1
+ WR0_CRC_RESET_CODE_MASK = 0xc0, // RESET
+ WR0_CRC_RESET_NULL = 0x00, // 0 0
+ WR0_CRC_RESET_RX = 0x40, // 0 1
+ WR0_CRC_RESET_TX = 0x80, // 1 0
+ WR0_CRC_RESET_TX_UNDERRUN = 0xc0 // 1 1
+};
+
+enum : uint8_t // ZBUS WR0 commands or 80X30
+{
+ WR0_Z_COMMAND_MASK = 0x38, // COMMANDS
+ WR0_Z_NULL_1 = 0x00, // 0 0 0
+ WR0_Z_NULL_2 = 0x08, // 0 0 1
+ WR0_Z_RESET_EXT_STATUS = 0x10, // 0 1 0
+ WR0_Z_SEND_ABORT = 0x18, // 0 1 1
+ WR0_Z_ENABLE_INT_NEXT_RX = 0x20, // 1 0 0
+ WR0_Z_RESET_TX_INT = 0x28, // 1 0 1
+ WR0_Z_ERROR_RESET = 0x30, // 1 1 0
+ WR0_Z_RESET_HIGHEST_IUS = 0x38, // 1 1 1
+ WR0_Z_SHIFT_MASK = 0x03, // SHIFT mode SDLC chan B
+ WR0_Z_SEL_SHFT_LEFT = 0x02, // 1 0
+ WR0_Z_SEL_SHFT_RIGHT = 0x03 // 1 1
+};
+
+enum : uint8_t
+{
+ WR1_EXT_INT_ENABLE = 0x01,
+ WR1_TX_INT_ENABLE = 0x02,
+ WR1_PARITY_IS_SPEC_COND = 0x04,
+ WR1_RX_INT_MODE_MASK = 0x18,
+ WR1_RX_INT_DISABLE = 0x00,
+ WR1_RX_INT_FIRST = 0x08,
+ WR1_RX_INT_ALL = 0x10,
+ WR1_RX_INT_PARITY = 0x18,
+ WR1_WREQ_ON_RX_TX = 0x20,
+ WR1_WREQ_FUNCTION = 0x40,
+ WR1_WREQ_ENABLE = 0x80
+};
+
+enum : uint8_t
+{
+ WR3_RX_ENABLE = 0x01,
+ WR3_SYNC_CHAR_LOAD_INHIBIT = 0x02,
+ WR3_ADDRESS_SEARCH_MODE = 0x04,
+ WR3_RX_CRC_ENABLE = 0x08,
+ WR3_ENTER_HUNT_MODE = 0x10,
+ WR3_AUTO_ENABLES = 0x20,
+ WR3_RX_WORD_LENGTH_MASK = 0xc0,
+ WR3_RX_WORD_LENGTH_5 = 0x00,
+ WR3_RX_WORD_LENGTH_7 = 0x40,
+ WR3_RX_WORD_LENGTH_6 = 0x80,
+ WR3_RX_WORD_LENGTH_8 = 0xc0
+};
+
+enum : uint8_t
+{
+ WR4_PARITY_ENABLE = 0x01,
+ WR4_PARITY_EVEN = 0x02,
+ WR4_STOP_BITS_MASK = 0x0c,
+ WR4_STOP_BITS_1 = 0x04,
+ WR4_STOP_BITS_1_5 = 0x08,
+ WR4_STOP_BITS_2 = 0x0c,
+ WR4_SYNC_MODE_MASK = 0x30,
+ WR4_SYNC_MODE_8_BIT = 0x00,
+ WR4_SYNC_MODE_16_BIT = 0x10,
+ WR4_BIT4 = 0x10,
+ WR4_SYNC_MODE_SDLC = 0x20,
+ WR4_BIT5 = 0x20,
+ WR4_SYNC_MODE_EXT = 0x30,
+ WR4_CLOCK_RATE_MASK = 0xc0,
+ WR4_CLOCK_RATE_X1 = 0x00,
+ WR4_CLOCK_RATE_X16 = 0x40,
+ WR4_CLOCK_RATE_X32 = 0x80,
+ WR4_CLOCK_RATE_X64 = 0xc0
+};
+
+enum : uint8_t
+{
+ WR5_TX_CRC_ENABLE = 0x01,
+ WR5_RTS = 0x02,
+ WR5_CRC16 = 0x04,
+ WR5_TX_ENABLE = 0x08,
+ WR5_SEND_BREAK = 0x10,
+ WR5_TX_WORD_LENGTH_MASK = 0x60,
+ WR5_TX_WORD_LENGTH_5 = 0x00,
+ WR5_TX_WORD_LENGTH_6 = 0x40,
+ WR5_TX_WORD_LENGTH_7 = 0x20,
+ WR5_TX_WORD_LENGTH_8 = 0x60,
+ WR5_DTR = 0x80
+};
+
+enum : uint8_t
+{
+ WR7P_TX_FIFO_EMPTY = 0x04
+};
+
+enum : uint8_t
+{
+ WR9_CMD_MASK = 0xC0,
+ WR9_CMD_NORESET = 0x00,
+ WR9_CMD_CHNB_RESET = 0x40,
+ WR9_CMD_CHNA_RESET = 0x80,
+ WR9_CMD_HW_RESET = 0xC0,
+ WR9_BIT_VIS = 0x01,
+ WR9_BIT_NV = 0x02,
+ WR9_BIT_DLC = 0x04,
+ WR9_BIT_MIE = 0x08,
+ WR9_BIT_SHSL = 0x10,
+ WR9_BIT_IACK = 0x20
+};
+
+enum : uint8_t
+{
+ WR10_8_6_BIT_SYNC = 0x01,
+ WR10_LOOP_MODE = 0x02,
+ WR10_ABORT_FLAG_UNDERRUN = 0x04,
+ WR10_MARK_FLAG_IDLE = 0x08,
+ WR10_GO_ACTIVE_ON_POLL = 0x10,
+ WR10_ENCODING_MASK = 0x60,
+ WR10_NRZ_ENCODING = 0x00,
+ WR10_NRZI_ENCODING = 0x20,
+ WR10_BIT5 = 0x20,
+ WR10_FM1_ENCODING = 0x40,
+ WR10_BIT6 = 0x40,
+ WR10_FM0_ENCODING = 0x60,
+ WR10_CRC_PRESET = 0x80
+};
+
+enum : uint8_t
+{
+ WR11_RCVCLK_TYPE = 0x80,
+ WR11_RCVCLK_SRC_MASK = 0x60, // RCV CLOCK
+ WR11_RCVCLK_SRC_RTXC = 0x00, // 0 0
+ WR11_RCVCLK_SRC_TRXC = 0x20, // 0 1
+ WR11_RCVCLK_SRC_BR = 0x40, // 1 0
+ WR11_RCVCLK_SRC_DPLL = 0x60, // 1 1
+ WR11_TRACLK_SRC_MASK = 0x18, // TRA CLOCK
+ WR11_TRACLK_SRC_RTXC = 0x00, // 0 0
+ WR11_TRACLK_SRC_TRXC = 0x08, // 0 1
+ WR11_TRACLK_SRC_BR = 0x10, // 1 0
+ WR11_TRACLK_SRC_DPLL = 0x18, // 1 1
+ WR11_TRXC_DIRECTION = 0x04,
+ WR11_TRXSRC_SRC_MASK = 0x03, // TRXX CLOCK
+ WR11_TRXSRC_SRC_XTAL = 0x00, // 0 0
+ WR11_TRXSRC_SRC_TRA = 0x01, // 0 1
+ WR11_TRXSRC_SRC_BR = 0x02, // 1 0
+ WR11_TRXSRC_SRC_DPLL = 0x03 // 1 1
+};
+
+enum : uint8_t
+{
+ WR14_DPLL_CMD_MASK = 0xe0, // Command
+ WR14_CMD_NULL = 0x00, // 0 0 0
+ WR14_CMD_ESM = 0x20, // 0 0 1
+ WR14_CMD_RMC = 0x40, // 0 1 0
+ WR14_CMD_DISABLE_DPLL = 0x60, // 0 1 1
+ WR14_CMD_SS_BRG = 0x80, // 1 0 0
+ WR14_CMD_SS_RTXC = 0xa0, // 1 0 1
+ WR14_CMD_SET_FM = 0xc0, // 1 1 0
+ WR14_CMD_SET_NRZI = 0xe0, // 1 1 1
+ WR14_BRG_ENABLE = 0x01,
+ WR14_BRG_SOURCE = 0x02,
+ WR14_DTR_REQ_FUNC = 0x04,
+ WR14_AUTO_ECHO = 0x08,
+ WR14_LOCAL_LOOPBACK = 0x10
+};
+
+enum : uint8_t
+{
+ WR15_WR7PRIME = 0x01,
+ WR15_ZEROCOUNT = 0x02,
+ WR15_STATUS_FIFO = 0x04,
+ WR15_DCD = 0x08,
+ WR15_SYNC = 0x10,
+ WR15_CTS = 0x20,
+ WR15_TX_EOM = 0x40,
+ WR15_BREAK_ABORT = 0x80
+};
+
+
+
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
@@ -158,10 +381,24 @@ MACHINE_CONFIG_END
// LIVE DEVICE
//**************************************************************************
+inline void z80scc_channel::out_txd_cb(int state)
+{
+ m_uart->m_out_txd_cb[m_index](state);
+}
+
+inline void z80scc_channel::out_rts_cb(int state)
+{
+ m_uart->m_out_rts_cb[m_index](state);
+}
+
+inline void z80scc_channel::out_dtr_cb(int state)
+{
+ m_uart->m_out_dtr_cb[m_index](state);
+}
+
//-------------------------------------------------
// z80scc_device - constructor
//-------------------------------------------------
-
z80scc_device::z80scc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t variant)
: device_t(mconfig, type, tag, owner, clock),
device_z80daisy_interface(mconfig, *this),
@@ -171,23 +408,17 @@ z80scc_device::z80scc_device(const machine_config &mconfig, device_type type, co
m_txca(0),
m_rxcb(0),
m_txcb(0),
- m_out_txda_cb(*this),
- m_out_dtra_cb(*this),
- m_out_rtsa_cb(*this),
- m_out_wreqa_cb(*this),
- m_out_synca_cb(*this),
- m_out_txdb_cb(*this),
- m_out_dtrb_cb(*this),
- m_out_rtsb_cb(*this),
- m_out_wreqb_cb(*this),
- m_out_syncb_cb(*this),
+ m_out_txd_cb{ { *this }, { *this } },
+ m_out_dtr_cb{ { *this }, { *this } },
+ m_out_rts_cb{ { *this }, { *this } },
+ m_out_wreq_cb{ { *this }, { *this } },
+ m_out_sync_cb{ { *this }, { *this } },
+ m_out_rxdrq_cb{ { *this }, { *this } },
+ m_out_txdrq_cb{ { *this }, { *this } },
m_out_int_cb(*this),
- m_out_rxdrqa_cb(*this),
- m_out_txdrqa_cb(*this),
- m_out_rxdrqb_cb(*this),
- m_out_txdrqb_cb(*this),
m_variant(variant),
- m_wr0_ptrbits(0)
+ m_wr0_ptrbits(0),
+ m_cputag(nullptr)
{
for (auto & elem : m_int_state)
elem = 0;
@@ -239,28 +470,36 @@ scc8523l_device::scc8523l_device(const machine_config &mconfig, const char *tag,
}
//-------------------------------------------------
-// device_start - device-specific startup
+// device_resolve_objects - device-specific setup
//-------------------------------------------------
-
-void z80scc_device::device_start()
+void z80scc_device::device_resolve_objects()
{
- LOGSETUP("%s\n", FUNCNAME);
+ LOG("%s\n", FUNCNAME);
+
// resolve callbacks
- m_out_txda_cb.resolve_safe();
- m_out_dtra_cb.resolve_safe();
- m_out_rtsa_cb.resolve_safe();
- m_out_wreqa_cb.resolve_safe();
- m_out_synca_cb.resolve_safe();
- m_out_txdb_cb.resolve_safe();
- m_out_dtrb_cb.resolve_safe();
- m_out_rtsb_cb.resolve_safe();
- m_out_wreqb_cb.resolve_safe();
- m_out_syncb_cb.resolve_safe();
+ m_out_txd_cb[CHANNEL_A].resolve_safe();
+ m_out_dtr_cb[CHANNEL_A].resolve_safe();
+ m_out_rts_cb[CHANNEL_A].resolve_safe();
+ m_out_wreq_cb[CHANNEL_A].resolve_safe();
+ m_out_sync_cb[CHANNEL_A].resolve_safe();
+ m_out_txd_cb[CHANNEL_B].resolve_safe();
+ m_out_dtr_cb[CHANNEL_B].resolve_safe();
+ m_out_rts_cb[CHANNEL_B].resolve_safe();
+ m_out_wreq_cb[CHANNEL_B].resolve_safe();
+ m_out_sync_cb[CHANNEL_B].resolve_safe();
+ m_out_rxdrq_cb[CHANNEL_A].resolve_safe();
+ m_out_txdrq_cb[CHANNEL_A].resolve_safe();
+ m_out_rxdrq_cb[CHANNEL_B].resolve_safe();
+ m_out_txdrq_cb[CHANNEL_B].resolve_safe();
m_out_int_cb.resolve_safe();
- m_out_rxdrqa_cb.resolve_safe();
- m_out_txdrqa_cb.resolve_safe();
- m_out_rxdrqb_cb.resolve_safe();
- m_out_txdrqb_cb.resolve_safe();
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void z80scc_device::device_start()
+{
+ LOG("%s", FUNCNAME);
// state saving
save_item(NAME(m_int_state));
@@ -274,16 +513,15 @@ void z80scc_device::device_start()
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
-
void z80scc_device::device_reset()
{
- LOGSETUP("%s %s \n",tag(), FUNCNAME);
+ LOG("%s %s \n",tag(), FUNCNAME);
// Do channel reset on both channels
m_chanA->reset();
m_chanB->reset();
- // Fix hardware reset values for registers where it differs from channel reset values
+ // Hardware reset values for registers where it differs from channel reset values
m_wr9 &= 0x3c;
m_wr9 |= 0xc0;
m_chanA->m_wr10 = 0x00;
@@ -358,7 +596,7 @@ int z80scc_device::z80daisy_irq_state()
}
// Last chance to keep the control of the interrupt line
- state |= (m_wr9 & z80scc_channel::WR9_BIT_DLC) ? Z80_DAISY_IEO : 0;
+ state |= (m_wr9 & WR9_BIT_DLC) ? Z80_DAISY_IEO : 0;
LOGINT("- Interrupt State %u\n", state);
@@ -371,6 +609,8 @@ int z80scc_device::z80daisy_irq_state()
//-------------------------------------------------
int z80scc_device::z80daisy_irq_ack()
{
+ int ret = -1; // Indicate default vector
+
LOGINT("%s %s \n",tag(), FUNCNAME);
// loop over all interrupt sources
for (auto & elem : m_int_state)
@@ -381,20 +621,30 @@ int z80scc_device::z80daisy_irq_ack()
elem = Z80_DAISY_IEO; // Set IUS bit (called IEO in z80 daisy lingo)
check_interrupts();
LOGINT(" - Found an INT request, ");
- if (m_wr9 & z80scc_channel::WR9_BIT_VIS)
+ if (m_wr9 & WR9_BIT_VIS)
{
- LOGINT("but WR9 D1 set to use autovector, returning -1\n");
- return -1;
+ LOGINT("but WR9 D1 set to use autovector, returning the default vector\n");
+ break;
}
else
{
LOGINT("returning RR2: %02x\n", m_chanB->m_rr2 );
- return m_chanB->m_rr2;
+ ret = m_chanB->m_rr2;
+ break;
}
}
}
- return -1;
+ // Did we not find a vector? Get the notion of a default vector from the CPU implementation
+ if (ret == -1 && m_cputag != nullptr)
+ {
+ // default irq vector is -1 for 68000 but 0 for z80 for example...
+ ret = owner()->subdevice<cpu_device>(m_cputag)->default_irq_vector();
+ LOGINT(" - failed to find an interrupt to ack, returning default IRQ vector: %02x\n", ret );
+ logerror("z80sio_irq_ack: failed to find an interrupt to ack!\n");
+ }
+
+ return ret;
}
@@ -417,7 +667,6 @@ void z80scc_device::z80daisy_irq_reti()
//-------------------------------------------------
// check_interrupts -
//-------------------------------------------------
-
void z80scc_device::check_interrupts()
{
int state = (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE;
@@ -429,7 +678,6 @@ void z80scc_device::check_interrupts()
//-------------------------------------------------
// reset_interrupts -
//-------------------------------------------------
-
void z80scc_device::reset_interrupts()
{
LOGINT("%s %s \n",tag(), FUNCNAME);
@@ -464,7 +712,7 @@ uint8_t z80scc_device::modify_vector(uint8_t vec, int i, uint8_t src)
src |= (i == CHANNEL_A ? 0x04 : 0x00 );
// Modify vector according to Hi/lo bit of WR9
- if (m_wr9 & z80scc_channel::WR9_BIT_SHSL) // Affect V4-V6
+ if (m_wr9 & WR9_BIT_SHSL) // Affect V4-V6
{
vec &= 0x8f;
vec |= src << 4;
@@ -506,7 +754,7 @@ void z80scc_device::trigger_interrupt(int index, int type)
LOGINT("%s %s:%c %02x \n",FUNCNAME, tag(), 'A' + index, type);
/* The Master Interrupt Enable (MIE) bit, WR9 D3, must be set to enable the SCC to generate interrupts.*/
- if (!(m_wr9 & z80scc_channel::WR9_BIT_MIE))
+ if (!(m_wr9 & WR9_BIT_MIE))
{
LOGINT("Master Interrupt Enable is not set, blocking attempt to interrupt\n");
return;
@@ -520,7 +768,7 @@ void z80scc_device::trigger_interrupt(int index, int type)
return;
}
// Vector modification requested?
- if (m_wr9 & z80scc_channel::WR9_BIT_VIS)
+ if (m_wr9 & WR9_BIT_VIS)
{
vector = modify_vector(vector, index, source);
}
@@ -549,7 +797,7 @@ void z80scc_device::trigger_interrupt(int index, int type)
m_int_source[priority] = source;
// Based on the fact that prio levels are aligned with the bitorder of rr3 we can do this...
- m_chanA->m_rr3 |= ((1 << prio_level) + (index == CHANNEL_A ? 3 : 0 ));
+ m_chanA->m_rr3 |= 1 << (prio_level + ((index == CHANNEL_A) ? 3 : 0));
// check for interrupt
check_interrupts();
@@ -572,7 +820,7 @@ int z80scc_device::update_extint(int index)
// - External and Special interripts has the same prio, just add channel offset
m_int_state[z80scc_channel::INT_EXTERNAL_PRIO + (index == CHANNEL_A ? 0 : 3 )] = 0;
// Based on the fact that prio levels are aligned with the bitorder of rr3 we can do this...
- m_chanA->m_rr3 &= ~((1 << z80scc_channel::INT_EXTERNAL_PRIO) + (index == CHANNEL_A ? 3 : 0 ));
+ m_chanA->m_rr3 &= ~(1 << (z80scc_channel::INT_EXTERNAL_PRIO + ((index == CHANNEL_A) ? 3 : 0)));
ret = 0; // indicate that we are done
}
else
@@ -585,7 +833,6 @@ int z80scc_device::update_extint(int index)
//-------------------------------------------------
// m1_r - interrupt acknowledge
//-------------------------------------------------
-
int z80scc_device::m1_r()
{
return z80daisy_irq_ack();
@@ -610,8 +857,8 @@ READ8_MEMBER( z80scc_device::zbus_r )
switch ((m_chanB->m_wr0) & 7)
{
- case z80scc_channel::WR0_Z_SEL_SHFT_LEFT: ba = offset & 0x01; reg = (offset >> 1) & 0x0f; break; /* Shift Left mode */
- case z80scc_channel::WR0_Z_SEL_SHFT_RIGHT: ba = offset & 0x10; reg = (offset >> 1) & 0x0f; break; /* Shift Right mode */
+ case WR0_Z_SEL_SHFT_LEFT: ba = offset & 0x01; reg = (offset >> 1) & 0x0f; break; /* Shift Left mode */
+ case WR0_Z_SEL_SHFT_RIGHT: ba = offset & 0x10; reg = (offset >> 1) & 0x0f; break; /* Shift Right mode */
default:
logerror("Malformed Z-bus SCC read: offset %02x WR0 bits %02x\n", offset, m_chanB->m_wr0);
LOG("Malformed Z-bus SCC read: offset %02x WR0 bits %02x\n", offset, m_chanB->m_wr0);
@@ -643,8 +890,8 @@ WRITE8_MEMBER( z80scc_device::zbus_w )
switch ((m_chanB->m_wr0) & 7)
{
- case z80scc_channel::WR0_Z_SEL_SHFT_LEFT: ba = offset & 0x01; reg = (offset >> 1) & 0x0f; break; /* Shift Left mode */
- case z80scc_channel::WR0_Z_SEL_SHFT_RIGHT: ba = offset & 0x10; reg = (offset >> 1) & 0x0f; break; /* Shift Right mode */
+ case WR0_Z_SEL_SHFT_LEFT: ba = offset & 0x01; reg = (offset >> 1) & 0x0f; break; /* Shift Left mode */
+ case WR0_Z_SEL_SHFT_RIGHT: ba = offset & 0x10; reg = (offset >> 1) & 0x0f; break; /* Shift Right mode */
default:
logerror("Malformed Z-bus SCC write: offset %02x WR0 bits %02x\n", offset, m_chanB->m_wr0);
LOG("Malformed Z-bus SCC write: offset %02x WR0 bits %02x\n", offset, m_chanB->m_wr0);
@@ -748,7 +995,6 @@ WRITE8_MEMBER( z80scc_device::cd_ba_w )
//-------------------------------------------------
// ba_cd_r - Universal Bus read
//-------------------------------------------------
-
READ8_MEMBER( z80scc_device::ba_cd_r )
{
int ba = BIT(offset, 1);
@@ -770,7 +1016,6 @@ READ8_MEMBER( z80scc_device::ba_cd_r )
//-------------------------------------------------
// ba_cd_w - Universal Bus write
//-------------------------------------------------
-
WRITE8_MEMBER( z80scc_device::ba_cd_w )
{
int ba = BIT(offset, 1);
@@ -795,7 +1040,6 @@ WRITE8_MEMBER( z80scc_device::ba_cd_w )
//-------------------------------------------------
// ba_cd_inv_r - Universal Bus read
//-------------------------------------------------
-
READ8_MEMBER( z80scc_device::ba_cd_inv_r )
{
int ba = BIT(offset, 1);
@@ -817,7 +1061,6 @@ READ8_MEMBER( z80scc_device::ba_cd_inv_r )
//-------------------------------------------------
// ba_cd_inv_w - Universal Bus read
//-------------------------------------------------
-
WRITE8_MEMBER( z80scc_device::ba_cd_inv_w )
{
int ba = BIT(offset, 1);
@@ -846,7 +1089,6 @@ WRITE8_MEMBER( z80scc_device::ba_cd_inv_w )
//-------------------------------------------------
// SCC_channel - constructor
//-------------------------------------------------
-
z80scc_channel::z80scc_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, Z80SCC_CHANNEL, tag, owner, clock),
device_serial_interface(mconfig, *this),
@@ -894,7 +1136,6 @@ z80scc_channel::z80scc_channel(const machine_config &mconfig, const char *tag, d
//-------------------------------------------------
// start - channel startup
//-------------------------------------------------
-
void z80scc_channel::device_start()
{
LOGSETUP("%s\n", FUNCNAME);
@@ -975,7 +1216,6 @@ void z80scc_channel::device_start()
//-------------------------------------------------
// reset - reset channel status
//-------------------------------------------------
-
void z80scc_channel::device_reset()
{
LOGSETUP("%s\n", FUNCNAME);
@@ -991,7 +1231,7 @@ void z80scc_channel::device_reset()
m_wr4 = 0x04;
m_wr5 = 0x00;
if (m_uart->m_variant & (z80scc_device::TYPE_SCC85C30 | z80scc_device::SET_ESCC))
- m_wr7 = 0x20;
+ m_wr7 = 0x20;
// WR9,WR10,WR11 and WR14 has a different hard reset (see z80scc_device::device_reset()) values
m_uart->m_wr9 &= 0xdf;
m_wr10 &= 0x60;
@@ -1008,9 +1248,8 @@ void z80scc_channel::device_reset()
m_rr10 &= 0x40;
// reset external lines
- set_rts(m_wr5 & WR5_RTS ? 0 : 1);
- set_dtr(m_wr14 & WR14_DTR_REQ_FUNC ? 0 : (m_wr5 & WR5_DTR ? 0 : 1));
-
+ out_rts_cb(m_rts = m_wr5 & WR5_RTS ? 0 : 1);
+ out_dtr_cb(m_dtr = m_wr14 & WR14_DTR_REQ_FUNC ? 0 : (m_wr5 & WR5_DTR ? 0 : 1));
// reset interrupts
if (m_index == z80scc_device::CHANNEL_A)
{
@@ -1055,50 +1294,38 @@ void z80scc_channel::device_timer(emu_timer &timer, device_timer_id id, int para
//-------------------------------------------------
// tra_callback -
//-------------------------------------------------
-
void z80scc_channel::tra_callback()
{
if (!(m_wr5 & WR5_TX_ENABLE))
{
- LOG(LLFORMAT " %s() \"%s \"Channel %c transmit mark 1 m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
+ LOGTX("%s \"%s \"Channel %c transmit mark 1 m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
// transmit mark
- if (m_index == z80scc_device::CHANNEL_A)
- m_uart->m_out_txda_cb(1);
- else
- m_uart->m_out_txdb_cb(1);
+ out_txd_cb(1);
}
else if (m_wr5 & WR5_SEND_BREAK)
{
- LOG(LLFORMAT " %s() \"%s \"Channel %c send break 1 m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
+ LOGTX("%s \"%s \"Channel %c send break 1 m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
// transmit break
- if (m_index == z80scc_device::CHANNEL_A)
- m_uart->m_out_txda_cb(0);
- else
- m_uart->m_out_txdb_cb(0);
+ out_txd_cb(0);
}
else if (!is_transmit_register_empty())
{
int db = transmit_register_get_data_bit();
- LOG(LLFORMAT " %s() \"%s \"Channel %c transmit data bit %d m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, owner()->tag(), 'A' + m_index, db, m_wr5);
+ LOGTX("%s \"%s \"Channel %c transmit data bit %d m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, db, m_wr5);
// transmit data
- if (m_index == z80scc_device::CHANNEL_A)
- m_uart->m_out_txda_cb(db);
- else
- m_uart->m_out_txdb_cb(db);
+ out_txd_cb(db);
}
else
{
- LOG(LLFORMAT " %s() \"%s \"Channel %c Failed to transmit m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
+ LOGTX("%s \"%s \"Channel %c Failed to transmit m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
logerror("%s \"%s \"Channel %c Failed to transmit\n", FUNCNAME, owner()->tag(), 'A' + m_index);
}
}
-
//-------------------------------------------------
// tra_complete -
//-------------------------------------------------
-
void z80scc_channel::tra_complete()
{
// Delayed baudrate change according to SCC specs
@@ -1106,7 +1333,7 @@ void z80scc_channel::tra_complete()
{
m_delayed_tx_brg_change = 0;
set_tra_rate(m_brg_rate);
- LOG("Delayed Init - Baud Rate Generator: %d mode: %dx\n", m_brg_rate, get_clock_mode() );
+ LOGTX("Delayed Init - Baud Rate Generator: %d mode: %dx\n", m_brg_rate, get_clock_mode() );
}
if ((m_wr5 & WR5_TX_ENABLE) && !(m_wr5 & WR5_SEND_BREAK))
@@ -1114,7 +1341,7 @@ void z80scc_channel::tra_complete()
if ( (m_rr0 & RR0_TX_BUFFER_EMPTY) == 0 || // Takes care of the NMOS/CMOS 1 slot TX FIFO
m_tx_fifo_rp != m_tx_fifo_wp) // or there are more characters to send in a longer FIFO.
{
- LOGTX(" %s() %s %c done sending, loading data from fifo:%02x '%c'\n", FUNCNAME, owner()->tag(), 'A' + m_index,
+ LOGTX("%s %s %c done sending, loading data from fifo:%02x '%c'\n", FUNCNAME, owner()->tag(), 'A' + m_index,
m_tx_data_fifo[m_tx_fifo_rp], isascii(m_tx_data_fifo[m_tx_fifo_rp]) ? m_tx_data_fifo[m_tx_fifo_rp] : ' ');
transmit_register_setup(m_tx_data_fifo[m_tx_fifo_rp]); // Reload the shift register
m_tx_fifo_rp_step();
@@ -1122,7 +1349,7 @@ void z80scc_channel::tra_complete()
}
else
{
- LOGTX(" %s() %s %c done sending, setting all sent bit\n", FUNCNAME, owner()->tag(), 'A' + m_index);
+ LOGTX("%s %s %c done sending, setting all sent bit\n", FUNCNAME, owner()->tag(), 'A' + m_index);
m_rr1 |= RR1_ALL_SENT;
// when the RTS bit is reset, the _RTS output goes high after the transmitter empties
@@ -1151,21 +1378,15 @@ void z80scc_channel::tra_complete()
}
else if (m_wr5 & WR5_SEND_BREAK)
{
- LOG(LLFORMAT " %s() \"%s \"Channel %c Transmit Break 0 m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
+ LOG("%s \"%s \"Channel %c Transmit Break 0 m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
// transmit break
- if (m_index == z80scc_device::CHANNEL_A)
- m_uart->m_out_txda_cb(0);
- else
- m_uart->m_out_txdb_cb(0);
+ out_txd_cb(0);
}
else
{
- LOG(LLFORMAT " %s() \"%s \"Channel %c Transmit Mark 1 m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
+ LOG("%s \"%s \"Channel %c Transmit Mark 1 m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
// transmit mark
- if (m_index == z80scc_device::CHANNEL_A)
- m_uart->m_out_txda_cb(1);
- else
- m_uart->m_out_txdb_cb(1);
+ out_txd_cb(1);
}
}
@@ -1173,18 +1394,17 @@ void z80scc_channel::tra_complete()
//-------------------------------------------------
// rcv_callback -
//-------------------------------------------------
-
void z80scc_channel::rcv_callback()
{
if (m_wr3 & WR3_RX_ENABLE)
{
- LOG(LLFORMAT " %s() \"%s \"Channel %c receive data bit %d m_wr3:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, owner()->tag(), 'A' + m_index, m_rxd, m_wr3);
+ LOG("%s \"%s \"Channel %c receive data bit %d m_wr3:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_rxd, m_wr3);
receive_register_update_bit(m_rxd);
}
#if 1
else
{
- LOG(LLFORMAT " %s() \"%s \"Channel %c Received Data Bit but receiver is disabled\n", machine().firstcpu->total_cycles(), FUNCNAME, owner()->tag(), 'A' + m_index);
+ LOG("%s \"%s \"Channel %c Received Data Bit but receiver is disabled\n", FUNCNAME, owner()->tag(), 'A' + m_index);
logerror("%s \"%s \"Channel %c Received data dit but receiver is disabled\n", FUNCNAME, owner()->tag(), 'A' + m_index);
}
#endif
@@ -1194,14 +1414,13 @@ void z80scc_channel::rcv_callback()
//-------------------------------------------------
// rcv_complete -
//-------------------------------------------------
-
void z80scc_channel::rcv_complete()
{
uint8_t data;
receive_register_extract();
data = get_received_char();
- LOG(LLFORMAT " %s() \"%s \"Channel %c Received Data %c\n", machine().firstcpu->total_cycles(), FUNCNAME, owner()->tag(), 'A' + m_index, data);
+ LOG("%s \"%s \"Channel %c Received Data %c\n", FUNCNAME, owner()->tag(), 'A' + m_index, data);
receive_data(data);
#if START_BIT_HUNT
m_rcv_mode = RCV_SEEKING;
@@ -1212,7 +1431,6 @@ void z80scc_channel::rcv_complete()
//-------------------------------------------------
// get_clock_mode - get clock divisor
//-------------------------------------------------
-
int z80scc_channel::get_clock_mode()
{
int clocks = 1;
@@ -1242,10 +1460,7 @@ TODO:
void z80scc_channel::set_rts(int state)
{
LOG("%s(%d) \"%s\": %c \n", FUNCNAME, state, owner()->tag(), 'A' + m_index);
- if (m_index == z80scc_device::CHANNEL_A)
- m_uart->m_out_rtsa_cb(state);
- else
- m_uart->m_out_rtsb_cb(state);
+ out_rts_cb(state);
}
void z80scc_channel::update_rts()
@@ -1270,7 +1485,6 @@ void z80scc_channel::update_rts()
//-------------------------------------------------
// get_stop_bits - get number of stop bits
//-------------------------------------------------
-
device_serial_interface::stop_bits_t z80scc_channel::get_stop_bits()
{
switch (m_wr4 & WR4_STOP_BITS_MASK)
@@ -1287,7 +1501,6 @@ device_serial_interface::stop_bits_t z80scc_channel::get_stop_bits()
//-------------------------------------------------
// get_rx_word_length - get receive word length
//-------------------------------------------------
-
int z80scc_channel::get_rx_word_length()
{
int bits = 5;
@@ -1307,7 +1520,6 @@ int z80scc_channel::get_rx_word_length()
//-------------------------------------------------
// get_tx_word_length - get transmit word length
//-------------------------------------------------
-
int z80scc_channel::get_tx_word_length()
{
int bits = 5;
@@ -1409,7 +1621,7 @@ Bit: D0 D1 D2 |D3 D4 D5 |D6 D7
uint8_t z80scc_channel::do_sccreg_rr3()
{
LOGR("%s(%02x)\n", FUNCNAME, m_rr3);
- return m_index == z80scc_device::CHANNEL_A ? m_rr3 & 0x3f : 0; // TODO Update all bits of this status register
+ return (m_index == z80scc_device::CHANNEL_A) ? (m_rr3 & 0x3f) : 0; // TODO Update all bits of this status register
}
@@ -1470,10 +1682,10 @@ uint8_t z80scc_channel::do_sccreg_rr7()
LOGR("%s\n", FUNCNAME);
if (!(m_uart->m_variant & (z80scc_device::SET_NMOS)))
{
- logerror(" %s() not implemented feature\n", FUNCNAME);
+ logerror("%s not implemented feature\n", FUNCNAME);
return 0;
}
- return m_rr3;
+ return m_rr3;
}
#if 0 // Short cutted in control_read()
@@ -1501,7 +1713,7 @@ uint8_t z80scc_channel::do_sccreg_rr9()
uint8_t z80scc_channel::do_sccreg_rr10()
{
LOGR("%s\n", FUNCNAME);
- logerror("%s() not implemented feature\n", FUNCNAME);
+ logerror("%s not implemented feature\n", FUNCNAME);
return m_rr10;
}
@@ -1594,7 +1806,7 @@ uint8_t z80scc_channel::scc_register_read( uint8_t reg)
{
case REG_RR0_STATUS: data = do_sccreg_rr0(); break; // TODO: verify handling of SCC specific bits: D6 and D1
case REG_RR1_SPEC_RCV_COND: data = do_sccreg_rr1(); break;
- case REG_RR2_INTERRUPT_VECT: data = do_sccreg_rr2(); break; // Channel dependent and SCC specific handling compared to SIO
+ case REG_RR2_INTERRUPT_VECT: data = do_sccreg_rr2(); break;
/* registers 3-7 are specific to SCC. TODO: Check variant and log/stop misuse */
case REG_RR3_INTERUPPT_PEND: data = wreg ? m_wr3 : do_sccreg_rr3(); break;
case REG_RR4_WR4_OR_RR0: data = wreg ? m_wr4 : do_sccreg_rr4(); break;
@@ -1611,7 +1823,7 @@ uint8_t z80scc_channel::scc_register_read( uint8_t reg)
case REG_RR14_WR7_OR_R10: data = do_sccreg_rr14(); break;
case REG_RR15_WR15_EXT_STAT: data = do_sccreg_rr15(); break;
default:
- logerror(" \"%s\" %s: %c : Unsupported RRx register:%02x\n", owner()->tag(), FUNCNAME, 'A' + m_index, reg);
+ logerror(" \"%s\"%s: %c : Unsupported RRx register:%02x\n", owner()->tag(), FUNCNAME, 'A' + m_index, reg);
}
return data;
}
@@ -1741,7 +1953,7 @@ void z80scc_channel::do_sccreg_wr0(uint8_t data)
LOGCMD("%s: %c : WR0_RESET_TX_INT\n", owner()->tag(), 'A' + m_index);
m_uart->m_int_state[INT_TRANSMIT_PRIO + (m_index == z80scc_device::CHANNEL_A ? 0 : 3 )] = 0;
// Based on the fact that prio levels are aligned with the bitorder of rr3 we can do this...
- m_uart->m_chanA->m_rr3 &= ~((1 << INT_TRANSMIT_PRIO) + (m_index == z80scc_device::CHANNEL_A ? 3 : 0 ));
+ m_uart->m_chanA->m_rr3 &= ~(1 << (INT_TRANSMIT_PRIO + ((m_index == z80scc_device::CHANNEL_A) ? 3 : 0)));
// Update interrupt line
m_uart->check_interrupts();
break;
@@ -1779,7 +1991,7 @@ void z80scc_channel::do_sccreg_wr0(uint8_t data)
void z80scc_channel::do_sccreg_wr1(uint8_t data)
{
LOG("%s(%02x) \"%s\": %c : %s - %02x\n", FUNCNAME, data, owner()->tag(), 'A' + m_index, FUNCNAME, data);
- /* TODO: Sort out SCC specific behaviours from legacy SIO behaviours:
+ /* TODO: Sort out SCC specific behaviours from legacy SIO behaviours inherited from z80dart.cpp:
- Channel B only bits vs
- Parity Is Special Condition, bit2 */
m_wr1 = data;
@@ -1915,7 +2127,7 @@ void z80scc_channel::do_sccreg_wr7(uint8_t data)
/* WR8 is the transmit buffer register */
void z80scc_channel::do_sccreg_wr8(uint8_t data)
{
- LOG("%s(%02x) \"%s\": %c : Transmit Buffer write %02x\n", FUNCNAME, data, owner()->tag(), 'A' + m_index, data);
+ LOGTX("%s(%02x) \"%s\": %c : Transmit Buffer write %02x\n", FUNCNAME, data, owner()->tag(), 'A' + m_index, data);
data_write(data);
}
@@ -2245,7 +2457,6 @@ with 0 before accessing WR0 or RR0.*/
//-------------------------------------------------
// control_write - write control register
//-------------------------------------------------
-
void z80scc_channel::control_write(uint8_t data)
{
uint8_t reg = m_uart->m_wr0_ptrbits; //m_wr0;
@@ -2274,7 +2485,6 @@ void z80scc_channel::control_write(uint8_t data)
//-------------------------------------------------
// data_read - read data register from fifo
//-------------------------------------------------
-
uint8_t z80scc_channel::data_read()
{
uint8_t data = 0;
@@ -2319,7 +2529,7 @@ uint8_t z80scc_channel::data_read()
{
LOGRCV("Rx FIFO empty, resetting status and interrupt state");
m_uart->m_int_state[INT_RECEIVE_PRIO + (m_index == z80scc_device::CHANNEL_A ? 0 : 3 )] = 0;
- m_uart->m_chanA->m_rr3 &= ~((1 << INT_RECEIVE_PRIO) + (m_index == z80scc_device::CHANNEL_A ? 3 : 0 ));
+ m_uart->m_chanA->m_rr3 &= ~(1 << (INT_RECEIVE_PRIO + ((m_index == z80scc_device::CHANNEL_A) ? 3 : 0)));
}
}
}
@@ -2371,7 +2581,7 @@ WRITE8_MEMBER (z80scc_device::db_w) { m_chanB->data_write(data); }
void z80scc_channel::data_write(uint8_t data)
{
/* Tx FIFO is full or...? */
- LOGTX("%s \"%s\": %c : Data Register Write: %02d '%c'\n", FUNCNAME, owner()->tag(), 'A' + m_index, data, isprint(data) ? data : ' ');
+ LOG("%s \"%s\": %c : Data Register Write: %02d '%c'\n", FUNCNAME, owner()->tag(), 'A' + m_index, data, isprint(data) ? data : ' ');
if ( !(m_rr0 & RR0_TX_BUFFER_EMPTY) && // NMOS/CMOS 1 slot "FIFO" is controlled by the TBE bit instead of fifo logic
( (m_tx_fifo_wp + 1 == m_tx_fifo_rp) || ( (m_tx_fifo_wp + 1 == m_tx_fifo_sz) && (m_tx_fifo_rp == 0) )))
@@ -2457,7 +2667,6 @@ void z80scc_channel::data_write(uint8_t data)
//-------------------------------------------------
// receive_data - put received data word into fifo
//-------------------------------------------------
-
void z80scc_channel::receive_data(uint8_t data)
{
LOGRCV("\"%s\": %c : Received Data Byte '%c'/%02x put into FIFO\n", owner()->tag(), 'A' + m_index, isprint(data) ? data : ' ', data);
@@ -2510,10 +2719,9 @@ void z80scc_channel::receive_data(uint8_t data)
//-------------------------------------------------
// cts_w - clear to send handler
//-------------------------------------------------
-
WRITE_LINE_MEMBER( z80scc_channel::cts_w )
{
- LOG("\"%s\" %s: %c : CTS %u\n", owner()->tag(), FUNCNAME, 'A' + m_index, state);
+ LOG("\"%s\"%s: %c : CTS %u\n", owner()->tag(), FUNCNAME, 'A' + m_index, state);
if ((m_rr0 & RR0_CTS) != (state ? RR0_CTS : 0)) // SCC change detection logic
{
@@ -2591,31 +2799,6 @@ WRITE_LINE_MEMBER( z80scc_channel::dcd_w )
WRITE_LINE_MEMBER( z80scc_channel::ri_w )
{
LOGINT("\"%s\": %c : RI %u - not implemented\n", owner()->tag(), 'A' + m_index, state);
-
-#if 0 // TODO: This code is inherited from another device driver and not correct for SCC
- if (m_ri != state)
- {
- // set ring indicator state
- m_ri = state;
-
- if (!m_rx_rr0_latch)
- {
- if (m_ri)
- m_rr0 |= RR0_RI;
- else
- m_rr0 &= ~RR0_RI;
-
- if (m_wr1 & WR1_EXT_INT_ENABLE)
- {
- // trigger interrupt
- m_uart->trigger_interrupt(m_index, INT_EXTERNAL);
-
- // latch read register 0
- m_rx_rr0_latch = 1;
- }
- }
- }
-#endif
}
//-------------------------------------------------
@@ -2770,7 +2953,7 @@ void z80scc_channel::update_serial()
parity = PARITY_NONE;
}
- LOG(" %s() \"%s \"Channel %c setting data frame %d+%d%c%d\n", FUNCNAME, owner()->tag(), 'A' + m_index, 1,
+ LOG("%s \"%s \"Channel %c setting data frame %d+%d%c%d\n", FUNCNAME, owner()->tag(), 'A' + m_index, 1,
data_bit_count, parity == PARITY_NONE ? 'N' : parity == PARITY_EVEN ? 'E' : 'O', (stop_bits + 1) / 2);
set_data_frame(1, data_bit_count, parity, stop_bits);
@@ -2829,10 +3012,7 @@ void z80scc_channel::set_dtr(int state)
LOG("%s(%d)\n", FUNCNAME, state);
m_dtr = state;
- if (m_index == z80scc_device::CHANNEL_A)
- m_uart->m_out_dtra_cb(m_dtr);
- else
- m_uart->m_out_dtrb_cb(m_dtr);
+ out_dtr_cb(m_dtr);
}
//-------------------------------------------------
@@ -2881,8 +3061,6 @@ void z80scc_channel::check_waitrequest()
if (m_wr1 & WR1_WREQ_FUNCTION)
{
// assert /W//REQ if transmit buffer is empty, clear if it's not
- int state = (m_rr0 & RR0_TX_BUFFER_EMPTY) ? ASSERT_LINE : CLEAR_LINE;
-
- (m_index ? m_uart->m_out_wreqb_cb : m_uart->m_out_wreqa_cb)(state);
+ m_uart->m_out_wreq_cb[m_index]((m_rr0 & RR0_TX_BUFFER_EMPTY) ? ASSERT_LINE : CLEAR_LINE);
}
}
diff --git a/src/devices/machine/z80scc.h b/src/devices/machine/z80scc.h
index 2b0cae0ea76..49ec17aab43 100644
--- a/src/devices/machine/z80scc.h
+++ b/src/devices/machine/z80scc.h
@@ -90,47 +90,47 @@
// Port A callbacks
#define MCFG_Z80SCC_OUT_TXDA_CB(_devcb) \
- devcb = &z80scc_device::set_out_txda_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_txd_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_DTRA_CB(_devcb) \
- devcb = &z80scc_device::set_out_dtra_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_dtr_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_RTSA_CB(_devcb) \
- devcb = &z80scc_device::set_out_rtsa_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_rts_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_WREQA_CB(_devcb) \
- devcb = &z80scc_device::set_out_wreqa_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_wreq_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_SYNCA_CB(_devcb) \
- devcb = &z80scc_device::set_out_synca_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_sync_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_RXDRQA_CB(_devcb) \
- devcb = &z80scc_device::set_out_rxdrqa_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_rxdrq_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_TXDRQA_CB(_devcb) \
- devcb = &z80scc_device::set_out_txdrqa_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_txdrq_callback<0>(*device, DEVCB_##_devcb);
// Port B callbacks
#define MCFG_Z80SCC_OUT_TXDB_CB(_devcb) \
- devcb = &z80scc_device::set_out_txdb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_txd_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_DTRB_CB(_devcb) \
- devcb = &z80scc_device::set_out_dtrb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_dtr_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_RTSB_CB(_devcb) \
- devcb = &z80scc_device::set_out_rtsb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_rts_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_WREQB_CB(_devcb) \
- devcb = &z80scc_device::set_out_wreqb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_wreq_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_SYNCB_CB(_devcb) \
- devcb = &z80scc_device::set_out_syncb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_sync_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_RXDRQB_CB(_devcb) \
- devcb = &z80scc_device::set_out_rxdrqb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_rxdrq_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SCC_OUT_TXDRQB_CB(_devcb) \
- devcb = &z80scc_device::set_out_txdrqb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80scc_device::set_out_txdrq_callback<1>(*device, DEVCB_##_devcb);
//**************************************************************************
@@ -242,7 +242,7 @@ public:
uint8_t m_rr14; // REG_RR14_WR7_OR_R10
uint8_t m_rr15; // REG_RR15_WR15_EXT_STAT
- // write registers enum
+ // write registers enum
uint8_t m_wr0; // REG_WR0_COMMAND_REGPT
uint8_t m_wr1; // REG_WR1_INT_DMA_ENABLE
uint8_t m_wr2; // REG_WR2_INT_VECTOR
@@ -264,45 +264,45 @@ public:
protected:
enum
{
- RCV_IDLE = 0,
- RCV_SEEKING = 1,
- RCV_SAMPLING = 2
+ RCV_IDLE = 0,
+ RCV_SEEKING = 1,
+ RCV_SAMPLING = 2
};
enum
{
- INT_TRANSMIT = 0,
- INT_EXTERNAL = 1,
- INT_RECEIVE = 2,
- INT_SPECIAL = 3,
+ INT_TRANSMIT = 0,
+ INT_EXTERNAL = 1,
+ INT_RECEIVE = 2,
+ INT_SPECIAL = 3,
};
enum
{
- INT_TRANSMIT_PRIO = 1,
- INT_EXTERNAL_PRIO = 0,
- INT_RECEIVE_PRIO = 2,
- INT_SPECIAL_PRIO = 0,
+ INT_TRANSMIT_PRIO = 1,
+ INT_EXTERNAL_PRIO = 0,
+ INT_RECEIVE_PRIO = 2,
+ INT_SPECIAL_PRIO = 0,
};
// Read registers
enum
{
- REG_RR0_STATUS = 0,
+ REG_RR0_STATUS = 0,
REG_RR1_SPEC_RCV_COND = 1,
REG_RR2_INTERRUPT_VECT = 2,
REG_RR3_INTERUPPT_PEND = 3,
- REG_RR4_WR4_OR_RR0 = 4,
- REG_RR5_WR5_OR_RR0 = 5,
- REG_RR6_LSB_OR_RR2 = 6,
- REG_RR7_MSB_OR_RR3 = 7,
+ REG_RR4_WR4_OR_RR0 = 4,
+ REG_RR5_WR5_OR_RR0 = 5,
+ REG_RR6_LSB_OR_RR2 = 6,
+ REG_RR7_MSB_OR_RR3 = 7,
REG_RR8_RECEIVE_DATA = 8,
- REG_RR9_WR3_OR_RR13 = 9,
+ REG_RR9_WR3_OR_RR13 = 9,
REG_RR10_MISC_STATUS = 10,
REG_RR11_WR10_OR_RR15 = 11,
REG_RR12_LO_TIME_CONST = 12,
REG_RR13_HI_TIME_CONST = 13,
- REG_RR14_WR7_OR_R10 = 14,
+ REG_RR14_WR7_OR_R10 = 14,
REG_RR15_WR15_EXT_STAT = 15
};
@@ -329,237 +329,6 @@ protected:
enum
{
- RR0_RX_CHAR_AVAILABLE = 0x01,
- RR0_ZC = 0x02,
- RR0_TX_BUFFER_EMPTY = 0x04,
- RR0_DCD = 0x08,
- RR0_SYNC_HUNT = 0x10,
- RR0_CTS = 0x20,
- RR0_TX_UNDERRUN = 0x40,
- RR0_BREAK_ABORT = 0x80
- };
-
- enum
- {
- RR1_ALL_SENT = 0x01,
- RR1_RESIDUE_CODE_MASK = 0x0e,
- RR1_PARITY_ERROR = 0x10,
- RR1_RX_OVERRUN_ERROR = 0x20,
- RR1_CRC_FRAMING_ERROR = 0x40,
- RR1_END_OF_FRAME = 0x80
- };
-
- enum
- {
- RR2_INT_VECTOR_MASK = 0xff, // SCC channel A, SIO channel B (special case)
- RR2_INT_VECTOR_V1 = 0x02, // SIO (special case) /SCC Channel B
- RR2_INT_VECTOR_V2 = 0x04, // SIO (special case) /SCC Channel B
- RR2_INT_VECTOR_V3 = 0x08 // SIO (special case) /SCC Channel B
- };
-
- enum
- {
- RR3_CHANB_EXT_IP = 0x01, // SCC IP pending registers
- RR3_CHANB_TX_IP = 0x02, // only read in Channel A (for both channels)
- RR3_CHANB_RX_IP = 0x04, // channel B return all zero
- RR3_CHANA_EXT_IP = 0x08,
- RR3_CHANA_TX_IP = 0x10,
- RR3_CHANA_RX_IP = 0x20
- };
-
- enum // Universal Bus WR0 commands for 85X30
- {
- WR0_REGISTER_MASK = 0x07,
- WR0_COMMAND_MASK = 0x38, // COMMANDS
- WR0_NULL = 0x00, // 0 0 0
- WR0_POINT_HIGH = 0x08, // 0 0 1
- WR0_RESET_EXT_STATUS = 0x10, // 0 1 0
- WR0_SEND_ABORT = 0x18, // 0 1 1
- WR0_ENABLE_INT_NEXT_RX = 0x20, // 1 0 0
- WR0_RESET_TX_INT = 0x28, // 1 0 1
- WR0_ERROR_RESET = 0x30, // 1 1 0
- WR0_RESET_HIGHEST_IUS = 0x38, // 1 1 1
- WR0_CRC_RESET_CODE_MASK = 0xc0, // RESET
- WR0_CRC_RESET_NULL = 0x00, // 0 0
- WR0_CRC_RESET_RX = 0x40, // 0 1
- WR0_CRC_RESET_TX = 0x80, // 1 0
- WR0_CRC_RESET_TX_UNDERRUN = 0xc0 // 1 1
- };
-
- enum // ZBUS WR0 commands or 80X30
- {
- WR0_Z_COMMAND_MASK = 0x38, // COMMANDS
- WR0_Z_NULL_1 = 0x00, // 0 0 0
- WR0_Z_NULL_2 = 0x08, // 0 0 1
- WR0_Z_RESET_EXT_STATUS = 0x10, // 0 1 0
- WR0_Z_SEND_ABORT = 0x18, // 0 1 1
- WR0_Z_ENABLE_INT_NEXT_RX= 0x20, // 1 0 0
- WR0_Z_RESET_TX_INT = 0x28, // 1 0 1
- WR0_Z_ERROR_RESET = 0x30, // 1 1 0
- WR0_Z_RESET_HIGHEST_IUS = 0x38, // 1 1 1
- WR0_Z_SHIFT_MASK = 0x03, // SHIFT mode SDLC chan B
- WR0_Z_SEL_SHFT_LEFT = 0x02, // 1 0
- WR0_Z_SEL_SHFT_RIGHT = 0x03 // 1 1
- };
-
- enum
- {
- WR1_EXT_INT_ENABLE = 0x01,
- WR1_TX_INT_ENABLE = 0x02,
- WR1_PARITY_IS_SPEC_COND = 0x04,
- WR1_RX_INT_MODE_MASK = 0x18,
- WR1_RX_INT_DISABLE = 0x00,
- WR1_RX_INT_FIRST = 0x08,
- WR1_RX_INT_ALL = 0x10,
- WR1_RX_INT_PARITY = 0x18,
- WR1_WREQ_ON_RX_TX = 0x20,
- WR1_WREQ_FUNCTION = 0x40,
- WR1_WREQ_ENABLE = 0x80
- };
-
- enum
- {
- WR3_RX_ENABLE = 0x01,
- WR3_SYNC_CHAR_LOAD_INHIBIT = 0x02,
- WR3_ADDRESS_SEARCH_MODE = 0x04,
- WR3_RX_CRC_ENABLE = 0x08,
- WR3_ENTER_HUNT_MODE = 0x10,
- WR3_AUTO_ENABLES = 0x20,
- WR3_RX_WORD_LENGTH_MASK = 0xc0,
- WR3_RX_WORD_LENGTH_5 = 0x00,
- WR3_RX_WORD_LENGTH_7 = 0x40,
- WR3_RX_WORD_LENGTH_6 = 0x80,
- WR3_RX_WORD_LENGTH_8 = 0xc0
- };
-
- enum
- {
- WR4_PARITY_ENABLE = 0x01,
- WR4_PARITY_EVEN = 0x02,
- WR4_STOP_BITS_MASK = 0x0c,
- WR4_STOP_BITS_1 = 0x04,
- WR4_STOP_BITS_1_5 = 0x08,
- WR4_STOP_BITS_2 = 0x0c,
- WR4_SYNC_MODE_MASK = 0x30,
- WR4_SYNC_MODE_8_BIT = 0x00,
- WR4_SYNC_MODE_16_BIT = 0x10,
- WR4_BIT4 = 0x10,
- WR4_SYNC_MODE_SDLC = 0x20,
- WR4_BIT5 = 0x20,
- WR4_SYNC_MODE_EXT = 0x30,
- WR4_CLOCK_RATE_MASK = 0xc0,
- WR4_CLOCK_RATE_X1 = 0x00,
- WR4_CLOCK_RATE_X16 = 0x40,
- WR4_CLOCK_RATE_X32 = 0x80,
- WR4_CLOCK_RATE_X64 = 0xc0
- };
-
- enum
- {
- WR5_TX_CRC_ENABLE = 0x01,
- WR5_RTS = 0x02,
- WR5_CRC16 = 0x04,
- WR5_TX_ENABLE = 0x08,
- WR5_SEND_BREAK = 0x10,
- WR5_TX_WORD_LENGTH_MASK = 0x60,
- WR5_TX_WORD_LENGTH_5 = 0x00,
- WR5_TX_WORD_LENGTH_6 = 0x40,
- WR5_TX_WORD_LENGTH_7 = 0x20,
- WR5_TX_WORD_LENGTH_8 = 0x60,
- WR5_DTR = 0x80
- };
-
-
- enum
- {
- WR7P_TX_FIFO_EMPTY = 0x04
- };
-
- enum
- {
- WR9_CMD_MASK = 0xC0,
- WR9_CMD_NORESET = 0x00,
- WR9_CMD_CHNB_RESET = 0x40,
- WR9_CMD_CHNA_RESET = 0x80,
- WR9_CMD_HW_RESET = 0xC0,
- WR9_BIT_VIS = 0x01,
- WR9_BIT_NV = 0x02,
- WR9_BIT_DLC = 0x04,
- WR9_BIT_MIE = 0x08,
- WR9_BIT_SHSL = 0x10,
- WR9_BIT_IACK = 0x20
- };
-
- enum
- {
- WR10_8_6_BIT_SYNC = 0x01,
- WR10_LOOP_MODE = 0x02,
- WR10_ABORT_FLAG_UNDERRUN = 0x04,
- WR10_MARK_FLAG_IDLE = 0x08,
- WR10_GO_ACTIVE_ON_POLL = 0x10,
- WR10_ENCODING_MASK = 0x60,
- WR10_NRZ_ENCODING = 0x00,
- WR10_NRZI_ENCODING = 0x20,
- WR10_BIT5 = 0x20,
- WR10_FM1_ENCODING = 0x40,
- WR10_BIT6 = 0x40,
- WR10_FM0_ENCODING = 0x60,
- WR10_CRC_PRESET = 0x80
- };
-
- enum
- {
- WR11_RCVCLK_TYPE = 0x80,
- WR11_RCVCLK_SRC_MASK = 0x60, // RCV CLOCK
- WR11_RCVCLK_SRC_RTXC = 0x00, // 0 0
- WR11_RCVCLK_SRC_TRXC = 0x20, // 0 1
- WR11_RCVCLK_SRC_BR = 0x40, // 1 0
- WR11_RCVCLK_SRC_DPLL = 0x60, // 1 1
- WR11_TRACLK_SRC_MASK = 0x18, // TRA CLOCK
- WR11_TRACLK_SRC_RTXC = 0x00, // 0 0
- WR11_TRACLK_SRC_TRXC = 0x08, // 0 1
- WR11_TRACLK_SRC_BR = 0x10, // 1 0
- WR11_TRACLK_SRC_DPLL = 0x18, // 1 1
- WR11_TRXC_DIRECTION = 0x04,
- WR11_TRXSRC_SRC_MASK = 0x03, // TRXX CLOCK
- WR11_TRXSRC_SRC_XTAL = 0x00, // 0 0
- WR11_TRXSRC_SRC_TRA = 0x01, // 0 1
- WR11_TRXSRC_SRC_BR = 0x02, // 1 0
- WR11_TRXSRC_SRC_DPLL = 0x03 // 1 1
- };
-
- enum
- {
- WR14_DPLL_CMD_MASK = 0xe0, // Command
- WR14_CMD_NULL = 0x00, // 0 0 0
- WR14_CMD_ESM = 0x20, // 0 0 1
- WR14_CMD_RMC = 0x40, // 0 1 0
- WR14_CMD_DISABLE_DPLL = 0x60, // 0 1 1
- WR14_CMD_SS_BRG = 0x80, // 1 0 0
- WR14_CMD_SS_RTXC = 0xa0, // 1 0 1
- WR14_CMD_SET_FM = 0xc0, // 1 1 0
- WR14_CMD_SET_NRZI = 0xe0, // 1 1 1
- WR14_BRG_ENABLE = 0x01,
- WR14_BRG_SOURCE = 0x02,
- WR14_DTR_REQ_FUNC = 0x04,
- WR14_AUTO_ECHO = 0x08,
- WR14_LOCAL_LOOPBACK = 0x10
- };
-
- enum
- {
- WR15_WR7PRIME = 0x01,
- WR15_ZEROCOUNT = 0x02,
- WR15_STATUS_FIFO = 0x04,
- WR15_DCD = 0x08,
- WR15_SYNC = 0x10,
- WR15_CTS = 0x20,
- WR15_TX_EOM = 0x40,
- WR15_BREAK_ABORT = 0x80
- };
-
- enum
- {
TIMER_ID_BAUD,
TIMER_ID_XTAL,
TIMER_ID_RTXC,
@@ -632,6 +401,12 @@ protected:
// SCC specifics
int m_ph; // Point high command to access regs 08-0f
uint8_t m_zc;
+private:
+ // helpers
+ void out_txd_cb(int state);
+ void out_rts_cb(int state);
+ void out_dtr_cb(int state);
+
};
@@ -645,21 +420,20 @@ public:
// construction/destruction
z80scc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
- template <class Object> static devcb_base &set_out_txda_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_txda_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_dtra_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_dtra_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_rtsa_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_rtsa_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_wreqa_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_wreqa_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_synca_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_synca_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_txdb_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_txdb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_dtrb_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_dtrb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_rtsb_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_rtsb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_wreqb_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_wreqb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_syncb_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_syncb_cb.set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_txd_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_txd_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_dtr_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_dtr_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_rts_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_rts_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_wreq_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_wreq_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_sync_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_sync_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_rxdrq_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_rxdrq_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_txdrq_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_txdrq_cb[N].set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &set_out_int_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_int_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_rxdrqa_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_rxdrqa_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_txdrqa_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_txdrqa_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_rxdrqb_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_rxdrqb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_txdrqb_callback(device_t &device, Object &&cb) { return downcast<z80scc_device &>(device).m_out_txdrqb_cb.set_callback(std::forward<Object>(cb)); }
+
+ static void static_set_cputag(device_t &device, const char *tag)
+ {
+ z80scc_device &dev = downcast<z80scc_device &>(device);
+ dev.m_cputag = tag;
+ }
static void configure_channels(device_t &device, int rxa, int txa, int rxb, int txb)
{
@@ -721,6 +495,7 @@ protected:
z80scc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t variant);
// device-level overrides
+ virtual void device_resolve_objects() override;
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
@@ -775,29 +550,23 @@ protected:
int m_rxcb;
int m_txcb;
- devcb_write_line m_out_txda_cb;
- devcb_write_line m_out_dtra_cb;
- devcb_write_line m_out_rtsa_cb;
- devcb_write_line m_out_wreqa_cb;
- devcb_write_line m_out_synca_cb;
-
- devcb_write_line m_out_txdb_cb;
- devcb_write_line m_out_dtrb_cb;
- devcb_write_line m_out_rtsb_cb;
- devcb_write_line m_out_wreqb_cb;
- devcb_write_line m_out_syncb_cb;
+ // internal state
+ devcb_write_line m_out_txd_cb[2];
+ devcb_write_line m_out_dtr_cb[2];
+ devcb_write_line m_out_rts_cb[2];
+ devcb_write_line m_out_wreq_cb[2];
+ devcb_write_line m_out_sync_cb[2];
+ devcb_write_line m_out_rxdrq_cb[2];
+ devcb_write_line m_out_txdrq_cb[2];
devcb_write_line m_out_int_cb;
- devcb_write_line m_out_rxdrqa_cb;
- devcb_write_line m_out_txdrqa_cb;
- devcb_write_line m_out_rxdrqb_cb;
- devcb_write_line m_out_txdrqb_cb;
int m_int_state[6]; // interrupt state
int m_int_source[6]; // interrupt source
int const m_variant;
uint8_t m_wr0_ptrbits;
+ const char *m_cputag;
};
class scc8030_device : public z80scc_device
diff --git a/src/devices/machine/z80sio.cpp b/src/devices/machine/z80sio.cpp
index 4b9ee65b981..ddf951161b4 100644
--- a/src/devices/machine/z80sio.cpp
+++ b/src/devices/machine/z80sio.cpp
@@ -50,6 +50,9 @@
#include "emu.h"
#include "z80sio.h"
+#include "machine/sdlc.h"
+
+
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
@@ -66,8 +69,8 @@
#define LOG_SYNC (1U << 9)
#define LOG_BIT (1U << 10)
-//#define VERBOSE (LOG_INT|LOG_READ|LOG_SETUP|LOG_TX|LOG_CMD) //(LOG_SETUP|LOG_INT|LOG_CMD|LOG_DCD|LOG_CTS|LOG_TX)
-//#define LOG_OUTPUT_FUNC printf
+//#define VERBOSE (LOG_INT | LOG_READ | LOG_SETUP | LOG_TX | LOG_CMD | LOG_CTS)
+//#define LOG_OUTPUT_STREAM std::cout
#include "logmacro.h"
@@ -91,15 +94,150 @@
#define CHANA_TAG "cha"
#define CHANB_TAG "chb"
+
+enum : uint8_t
+{
+ RR0_RX_CHAR_AVAILABLE = 0x01,
+ RR0_INTERRUPT_PENDING = 0x02,
+ RR0_TX_BUFFER_EMPTY = 0x04,
+ RR0_DCD = 0x08,
+ RR0_SYNC_HUNT = 0x10,
+ RR0_CTS = 0x20,
+ RR0_TX_UNDERRUN = 0x40,
+ RR0_BREAK_ABORT = 0x80
+};
+
+enum : uint8_t
+{
+ RR1_ALL_SENT = 0x01,
+ RR1_RESIDUE_CODE_MASK = 0x0e,
+ RR1_PARITY_ERROR = 0x10,
+ RR1_RX_OVERRUN_ERROR = 0x20,
+ RR1_CRC_FRAMING_ERROR = 0x40,
+ RR1_END_OF_FRAME = 0x80
+};
+
+enum : uint8_t
+{
+ RR2_INT_VECTOR_MASK = 0xff,
+ RR2_INT_VECTOR_V1 = 0x02,
+ RR2_INT_VECTOR_V2 = 0x04,
+ RR2_INT_VECTOR_V3 = 0x08
+};
+
+enum : uint8_t
+{
+ WR0_REGISTER_MASK = 0x07,
+ WR0_COMMAND_MASK = 0x38,
+ WR0_NULL = 0x00,
+ WR0_SEND_ABORT = 0x08,
+ WR0_RESET_EXT_STATUS = 0x10,
+ WR0_CHANNEL_RESET = 0x18,
+ WR0_ENABLE_INT_NEXT_RX = 0x20,
+ WR0_RESET_TX_INT = 0x28,
+ WR0_ERROR_RESET = 0x30,
+ WR0_RETURN_FROM_INT = 0x38,
+ WR0_CRC_RESET_CODE_MASK = 0xc0,
+ WR0_CRC_RESET_NULL = 0x00,
+ WR0_CRC_RESET_RX = 0x40,
+ WR0_CRC_RESET_TX = 0x80,
+ WR0_CRC_RESET_TX_UNDERRUN = 0xc0
+};
+
+enum : uint8_t
+{
+ WR1_EXT_INT_ENABLE = 0x01,
+ WR1_TX_INT_ENABLE = 0x02,
+ WR1_STATUS_VECTOR = 0x04,
+ WR1_RX_INT_MODE_MASK = 0x18,
+ WR1_RX_INT_DISABLE = 0x00,
+ WR1_RX_INT_FIRST = 0x08,
+ WR1_RX_INT_ALL_PARITY = 0x10,
+ WR1_RX_INT_ALL = 0x18,
+ WR1_WRDY_ON_RX_TX = 0x20,
+ WR1_WRDY_FUNCTION = 0x40, // WAIT not supported
+ WR1_WRDY_ENABLE = 0x80
+};
+
+enum : uint8_t
+{
+ WR2_DATA_XFER_INT = 0x00, // not supported
+ WR2_DATA_XFER_DMA_INT = 0x01, // not supported
+ WR2_DATA_XFER_DMA = 0x02, // not supported
+ WR2_DATA_XFER_ILLEGAL = 0x03, // not supported
+ WR2_DATA_XFER_MASK = 0x03, // not supported
+ WR2_PRIORITY = 0x04,
+ WR2_MODE_8085_1 = 0x00, // not supported
+ WR2_MODE_8085_2 = 0x08, // not supported
+ WR2_MODE_8086_8088 = 0x10, // not supported
+ WR2_MODE_ILLEGAL = 0x18, // not supported
+ WR2_MODE_MASK = 0x18, // not supported
+ WR2_VECTORED_INT = 0x20, // partially supported
+ WR2_PIN10_SYNDETB_RTSB = 0x80 // not supported
+};
+
+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_ENTER_HUNT_PHASE = 0x10,
+ WR3_AUTO_ENABLES = 0x20,
+ WR3_RX_WORD_LENGTH_MASK = 0xc0,
+ WR3_RX_WORD_LENGTH_5 = 0x00,
+ WR3_RX_WORD_LENGTH_7 = 0x40,
+ WR3_RX_WORD_LENGTH_6 = 0x80,
+ WR3_RX_WORD_LENGTH_8 = 0xc0
+};
+
+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_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_EXT = 0x30, // partially supported
+ WR4_CLOCK_RATE_MASK = 0xc0,
+ WR4_CLOCK_RATE_X1 = 0x00,
+ WR4_CLOCK_RATE_X16 = 0x40,
+ WR4_CLOCK_RATE_X32 = 0x80,
+ WR4_CLOCK_RATE_X64 = 0xc0
+};
+
+enum : uint8_t
+{
+ WR5_TX_CRC_ENABLE = 0x01,
+ WR5_RTS = 0x02,
+ WR5_CRC16 = 0x04,
+ WR5_TX_ENABLE = 0x08,
+ WR5_SEND_BREAK = 0x10,
+ WR5_TX_WORD_LENGTH_MASK = 0x60,
+ WR5_TX_WORD_LENGTH_5 = 0x00,
+ WR5_TX_WORD_LENGTH_6 = 0x40,
+ WR5_TX_WORD_LENGTH_7 = 0x20,
+ WR5_TX_WORD_LENGTH_8 = 0x60,
+ WR5_DTR = 0x80
+};
+
+
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
// device type definition
-DEFINE_DEVICE_TYPE(Z80SIO, z80sio_device, "z80sio", "Z80 SIO")
DEFINE_DEVICE_TYPE(Z80SIO_CHANNEL, z80sio_channel, "z80sio_channel", "Z80 SIO channel")
-DEFINE_DEVICE_TYPE(UPD7201_NEW, upd7201_new_device, "upd7201_new", "NEC uPD7201 MPSC (new)") // Remove trailing N when z80dart.cpp's 7201 implementation is fully replaced
+DEFINE_DEVICE_TYPE(I8274_CHANNEL, i8274_channel, "i8274_channel", "Intel 8274 MPSC channel")
+DEFINE_DEVICE_TYPE(Z80SIO, z80sio_device, "z80sio", "Z80 SIO")
DEFINE_DEVICE_TYPE(I8274_NEW, i8274_new_device, "i8274_new", "Intel 8274 MPSC (new)") // Remove trailing N when z80dart.cpp's 8274 implementation is fully replaced
+DEFINE_DEVICE_TYPE(UPD7201_NEW, upd7201_new_device, "upd7201_new", "NEC uPD7201 MPSC (new)") // Remove trailing N when z80dart.cpp's 7201 implementation is fully replaced
//-------------------------------------------------
// device_add_mconfig - add device configuration
@@ -109,94 +247,182 @@ MACHINE_CONFIG_MEMBER( z80sio_device::device_add_mconfig )
MCFG_DEVICE_ADD(CHANB_TAG, Z80SIO_CHANNEL, 0)
MACHINE_CONFIG_END
+MACHINE_CONFIG_MEMBER( i8274_new_device::device_add_mconfig )
+ MCFG_DEVICE_ADD(CHANA_TAG, I8274_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHANB_TAG, I8274_CHANNEL, 0)
+MACHINE_CONFIG_END
+
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
+inline void z80sio_channel::out_txd_cb(int state)
+{
+ m_uart->m_out_txd_cb[m_index](state);
+}
+
+inline void z80sio_channel::out_rts_cb(int state)
+{
+ m_uart->m_out_rts_cb[m_index](state);
+}
+
+inline void z80sio_channel::out_dtr_cb(int state)
+{
+ m_uart->m_out_dtr_cb[m_index](state);
+}
+
+inline void z80sio_channel::set_ready(bool ready)
+{
+ // WAIT mode not supported yet
+ if (m_wr1 & WR1_WRDY_FUNCTION)
+ m_uart->m_out_wrdy_cb[m_index](ready ? 0 : 1);
+}
+
+inline bool z80sio_channel::receive_allowed() const
+{
+ return (m_wr3 & WR3_RX_ENABLE) && (!(m_wr3 & WR3_AUTO_ENABLES) || !m_dcd);
+}
+
+inline bool z80sio_channel::transmit_allowed() const
+{
+ return (m_wr5 & WR5_TX_ENABLE) && (!(m_wr3 & WR3_AUTO_ENABLES) || !m_cts);
+}
+
+inline void z80sio_channel::set_rts(int state)
+{
+ if (bool(m_rts) != bool(state))
+ {
+ LOG("%s(%d) \"%s\" Channel %c \n", FUNCNAME, state, owner()->tag(), 'A' + m_index);
+ out_rts_cb(m_rts = state);
+ }
+}
+
+inline void z80sio_channel::set_dtr(int state)
+{
+ if (bool(m_dtr) != bool(state))
+ {
+ LOG("%s(%d) \"%s\" Channel %c \n", FUNCNAME, state, owner()->tag(), 'A' + m_index);
+ out_dtr_cb(m_dtr = state);
+ }
+}
+
+inline void z80sio_channel::tx_setup(uint16_t data, int bits, int parity, bool framing, bool special)
+{
+ 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_tx_flags =
+ ((!framing && (m_wr5 & WR5_TX_CRC_ENABLE)) ? TX_FLAG_CRC : 0U) |
+ (framing ? TX_FLAG_FRAMING : 0U) |
+ (special ? TX_FLAG_SPECIAL : 0U);
+}
+
+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);
+ break;
+ case WR4_SYNC_MODE_16_BIT:
+ tx_setup(uint16_t(m_wr6) | (uint16_t(m_wr7) << 8), 16, 0, true, 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);
+ break;
+ }
+}
+
+
//-------------------------------------------------
// z80sio_device - constructor
//-------------------------------------------------
-z80sio_device::z80sio_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t variant)
- : device_t(mconfig, type, tag, owner, clock),
+z80sio_device::z80sio_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_z80daisy_interface(mconfig, *this),
m_chanA(*this, CHANA_TAG),
m_chanB(*this, CHANB_TAG),
- m_rxca(0),
- m_txca(0),
- m_rxcb(0),
- m_txcb(0),
- m_out_txda_cb(*this),
- m_out_dtra_cb(*this),
- m_out_rtsa_cb(*this),
- m_out_wrdya_cb(*this),
- m_out_synca_cb(*this),
- m_out_txdb_cb(*this),
- m_out_dtrb_cb(*this),
- m_out_rtsb_cb(*this),
- m_out_wrdyb_cb(*this),
- m_out_syncb_cb(*this),
+ m_out_txd_cb{ { *this }, { *this } },
+ m_out_dtr_cb{ { *this }, { *this } },
+ m_out_rts_cb{ { *this }, { *this } },
+ m_out_wrdy_cb{ { *this }, { *this } },
+ m_out_sync_cb{ { *this }, { *this } },
m_out_int_cb(*this),
- m_out_rxdrqa_cb(*this),
- m_out_txdrqa_cb(*this),
- m_out_rxdrqb_cb(*this),
- m_out_txdrqb_cb(*this),
- m_variant(variant),
- m_cputag("maincpu")
+ m_out_rxdrq_cb{ { *this }, { *this } },
+ m_out_txdrq_cb{ { *this }, { *this } },
+ m_cputag(nullptr)
{
for (auto & elem : m_int_state)
elem = 0;
}
-z80sio_device::z80sio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : z80sio_device(mconfig, Z80SIO, tag, owner, clock, TYPE_Z80SIO)
+z80sio_device::z80sio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ z80sio_device(mconfig, Z80SIO, tag, owner, clock)
+{
+}
+
+i8274_new_device::i8274_new_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+ z80sio_device(mconfig, type, tag, owner, clock)
{
}
-upd7201_new_device::upd7201_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : z80sio_device(mconfig, UPD7201_NEW, tag, owner, clock, TYPE_UPD7201)
+i8274_new_device::i8274_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ i8274_new_device(mconfig, I8274_NEW, tag, owner, clock)
{
}
-i8274_new_device::i8274_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : z80sio_device(mconfig, I8274_NEW, tag, owner, clock, TYPE_I8274)
+upd7201_new_device::upd7201_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ i8274_new_device(mconfig, UPD7201_NEW, tag, owner, clock)
{
}
//-------------------------------------------------
-// device_start - device-specific startup
+// device_resolve_objects - device-specific setup
//-------------------------------------------------
-void z80sio_device::device_start()
+void z80sio_device::device_resolve_objects()
{
LOG("%s\n", FUNCNAME);
+
// resolve callbacks
- m_out_txda_cb.resolve_safe();
- m_out_dtra_cb.resolve_safe();
- m_out_rtsa_cb.resolve_safe();
- m_out_wrdya_cb.resolve_safe();
- m_out_synca_cb.resolve_safe();
- m_out_txdb_cb.resolve_safe();
- m_out_dtrb_cb.resolve_safe();
- m_out_rtsb_cb.resolve_safe();
- m_out_wrdyb_cb.resolve_safe();
- m_out_syncb_cb.resolve_safe();
+ m_out_txd_cb[CHANNEL_A].resolve_safe();
+ m_out_dtr_cb[CHANNEL_A].resolve_safe();
+ m_out_rts_cb[CHANNEL_A].resolve_safe();
+ m_out_wrdy_cb[CHANNEL_A].resolve_safe();
+ m_out_sync_cb[CHANNEL_A].resolve_safe();
+ m_out_txd_cb[CHANNEL_B].resolve_safe();
+ m_out_dtr_cb[CHANNEL_B].resolve_safe();
+ m_out_rts_cb[CHANNEL_B].resolve_safe();
+ m_out_wrdy_cb[CHANNEL_B].resolve_safe();
+ m_out_sync_cb[CHANNEL_B].resolve_safe();
m_out_int_cb.resolve_safe();
- m_out_rxdrqa_cb.resolve_safe();
- m_out_txdrqa_cb.resolve_safe();
- m_out_rxdrqb_cb.resolve_safe();
- m_out_txdrqb_cb.resolve_safe();
-
- // configure channel A
- m_chanA->m_rxc = m_rxca;
- m_chanA->m_txc = m_txca;
+ m_out_rxdrq_cb[CHANNEL_A].resolve_safe();
+ m_out_txdrq_cb[CHANNEL_A].resolve_safe();
+ m_out_rxdrq_cb[CHANNEL_B].resolve_safe();
+ m_out_txdrq_cb[CHANNEL_B].resolve_safe();
+}
- // configure channel B
- m_chanB->m_rxc = m_rxcb;
- m_chanB->m_txc = m_txcb;
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void z80sio_device::device_start()
+{
+ LOG("%s\n", FUNCNAME);
// state saving
save_item(NAME(m_int_state));
- save_item(NAME(m_int_source));
}
@@ -206,9 +432,6 @@ void z80sio_device::device_start()
void z80sio_device::device_reset()
{
LOG("%s \"%s\" \n", FUNCNAME, tag());
-
- m_chanA->reset();
- m_chanB->reset();
}
//-------------------------------------------------
@@ -216,26 +439,26 @@ void z80sio_device::device_reset()
//-------------------------------------------------
int z80sio_device::z80daisy_irq_state()
{
- int state = 0;
+ int const *const prio = interrupt_priorities();
LOGINT("%s %s Hi->Lo:%d%d%d%d%d%d ", tag(), FUNCNAME,
- m_int_state[0], m_int_state[1], m_int_state[2],
- m_int_state[3], m_int_state[4], m_int_state[5]);
+ m_int_state[prio[0]], m_int_state[prio[1]], m_int_state[prio[2]],
+ m_int_state[prio[3]], m_int_state[prio[4]], m_int_state[prio[5]]);
// loop over all interrupt sources
- for (auto & elem : m_int_state)
+ int state = 0;
+ for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i)
{
// if we're servicing a request, don't indicate more interrupts
- if (elem & Z80_DAISY_IEO)
+ if (m_int_state[prio[i]] & Z80_DAISY_IEO)
{
state |= Z80_DAISY_IEO;
break;
}
- state |= elem;
+ state |= m_int_state[prio[i]];
}
LOGINT("Interrupt State %u\n", state);
-
return state;
}
@@ -245,29 +468,105 @@ int z80sio_device::z80daisy_irq_state()
//-------------------------------------------------
int z80sio_device::z80daisy_irq_ack()
{
- // default irq vector is -1 for 68000 but 0 for z80 for example...
- int ret = owner()->subdevice<cpu_device>(m_cputag)->default_irq_vector();
+ LOGINT("%s \n", FUNCNAME);
- LOGINT("%s %s \n",tag(), FUNCNAME);
// loop over all interrupt sources
- for (auto & elem : m_int_state)
+ int const *const prio = interrupt_priorities();
+ for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i)
{
// find the first channel with an interrupt requested
- if (elem & Z80_DAISY_INT)
+ if (m_int_state[prio[i]] & Z80_DAISY_INT)
{
- elem = Z80_DAISY_IEO; // Set IUS bit (called IEO in z80 daisy lingo)
- m_chanA->m_rr0 &= ~z80sio_channel::RR0_INTERRUPT_PENDING;
- LOGINT(" - Found an INT request, ");
- LOGINT("returning RR2: %02x\n", m_chanB->m_rr2 );
+ m_int_state[prio[i]] |= Z80_DAISY_IEO; // Set IUS bit (called IEO in z80 daisy lingo)
+ unsigned const vector = read_vector();
+ LOGINT(" - Found an INT request, returning RR2: %02x\n", vector);
check_interrupts();
- return m_chanB->m_rr2;
+ return vector;
+ }
+ }
+
+ // Did we not find a vector? Get the notion of a default vector from the CPU implementation
+ logerror(" - failed to find an interrupt to ack!\n");
+ if (m_cputag)
+ {
+ // default irq vector is -1 for 68000 but 0 for z80 for example...
+ // FIXME: use an optional_device or something
+ int const ret = owner()->subdevice<cpu_device>(m_cputag)->default_irq_vector();
+ LOGINT(" - failed to find an interrupt to ack [%s], returning default IRQ vector: %02x\n", m_cputag, ret);
+ return ret;
+ }
+
+ // indicate default vector
+ return -1;
+}
+
+int i8274_new_device::z80daisy_irq_ack()
+{
+ // FIXME: we're not modelling the full behaviour of this chip
+ // The 8274 is designed to work with Intel processors with multiple interrupt acknowledge cycles
+ // Values placed on the bus depend on WR2 A mode bits and /IPI input
+ // +----+----+----+------+--------------+-------+--------+
+ // | D5 | D4 | D3 | /IPI | Mode | Cycle | Data |
+ // +----+----+----+------+--------------+-------+--------+
+ // | 0 | - | - | - | non-vectored | - | hi-Z |
+ // +----+----+----+------+--------------+-------+--------+
+ // | 1 | 0 | 0 | 0 | 8085 (1) | 1 | 0xcd |
+ // | | | | | | 2 | vector |
+ // | | | | | | 3 | 0x00 |
+ // +----+----+----+------+--------------+-------+--------+
+ // | 1 | 0 | 0 | 1 | 8085 (1) | 1 | 0xcd |
+ // | | | | | | 2 | hi-Z |
+ // | | | | | | 3 | hi-Z |
+ // +----+----+----+------+--------------+-------+--------+
+ // | 1 | 0 | 1 | 0 | 8085 (2) | 1 | hi-Z |
+ // | | | | | | 2 | vector |
+ // | | | | | | 3 | 0x00 |
+ // +----+----+----+------+--------------+-------+--------+
+ // | 1 | 0 | 1 | 1 | 8085 (2) | 1 | hi-Z |
+ // | | | | | | 2 | hi-Z |
+ // | | | | | | 3 | hi-Z |
+ // +----+----+----+------+--------------+-------+--------+
+ // | 1 | 1 | 0 | 0 | 8086 | 1 | hi-Z |
+ // | | | | | | 2 | vector |
+ // +----+----+----+------+--------------+-------+--------+
+ // | 1 | 1 | 0 | 1 | 8086 | 1 | hi-Z |
+ // | | | | | | 2 | hi-Z |
+ // +----+----+----+------+--------------+-------+--------+
+ LOGINT("%s \n", FUNCNAME);
+
+ // don't do this in non-vectored mode
+ if (m_chanB->m_wr2 & WR2_VECTORED_INT)
+ {
+ // loop over all interrupt sources
+ int const *const prio = interrupt_priorities();
+ for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i)
+ {
+ // find the first channel with an interrupt requested
+ if (m_int_state[prio[i]] & Z80_DAISY_INT)
+ {
+ m_int_state[prio[i]] |= Z80_DAISY_IEO; // Set IUS bit (called IEO in z80 daisy lingo)
+ unsigned const vector = read_vector();
+ LOGINT(" - Found an INT request, returning RR2: %02x\n", vector);
+ check_interrupts();
+ return vector;
+ }
}
+
+ // Did we not find a vector? Get the notion of a default vector from the CPU implementation
+ logerror(" - failed to find an interrupt to ack!\n");
}
- ret = m_chanB->m_rr2;
- LOGINT(" - failed to find an interrupt to ack, returning default IRQ vector: %02x\n", ret );
- logerror("z80sio_irq_ack: failed to find an interrupt to ack!\n");
- return ret;
+ if (m_cputag)
+ {
+ // default irq vector is -1 for 68000 but 0 for z80 for example...
+ // FIXME: use an optional_device or something
+ int const ret = owner()->subdevice<cpu_device>(m_cputag)->default_irq_vector();
+ LOGINT(" - failed to find an interrupt to ack [%s], returning default IRQ vector: %02x\n", m_cputag, ret);
+ return ret;
+ }
+
+ // indicate default vector
+ return -1;
}
@@ -276,27 +575,13 @@ int z80sio_device::z80daisy_irq_ack()
//-------------------------------------------------
void z80sio_device::z80daisy_irq_reti()
{
- LOGINT("%s %s \n",tag(), FUNCNAME);
-
- if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
- {
- LOGINT(" - I8274 and UPD7201 lacks RETI detection, no action taken\n");
- return;
- }
+ LOGINT("%s\n", FUNCNAME);
+ return_from_interrupt();
+}
- // loop over all interrupt sources
- for (auto & elem : m_int_state)
- {
- // find the first channel with an interrupt requested
- if (elem & Z80_DAISY_IEO)
- {
- // clear the IEO state and update the IRQs
- elem &= ~Z80_DAISY_IEO;
- check_interrupts();
- return;
- }
- }
- LOGINT("z80sio_irq_reti: failed to find an interrupt to clear IEO on!\n");
+void i8274_new_device::z80daisy_irq_reti()
+{
+ LOGINT("%s - i8274/uPD7201 lacks RETI detection, no action taken\n", FUNCNAME);
}
@@ -305,7 +590,7 @@ void z80sio_device::z80daisy_irq_reti()
//-------------------------------------------------
void z80sio_device::check_interrupts()
{
- LOGINT("%s %s \n",FUNCNAME, tag());
+ LOGINT("%s %s \n", FUNCNAME, tag());
int state = (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE;
m_out_int_cb(state);
}
@@ -326,44 +611,114 @@ void z80sio_device::reset_interrupts()
check_interrupts();
}
-int z80sio_device::get_interrupt_prio(int index, int type)
+//-------------------------------------------------
+// trigger_interrupt - interrupt has fired
+//-------------------------------------------------
+void z80sio_device::trigger_interrupt(int index, int type)
{
- int prio_level = -1;
- int priority = -1;
+ LOGINT("%s Chan:%c Type:%s\n", FUNCNAME, 'A' + index, std::array<char const *, 3>
+ {{"INT_TRANSMIT", "INT_EXTERNAL", "INT_RECEIVE"}}[type]);
+
+ // trigger interrupt
+ m_int_state[(index * 3) + type] |= Z80_DAISY_INT;
+ m_chanA->m_rr0 |= RR0_INTERRUPT_PENDING;
- if ((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
+ // check for interrupt
+ check_interrupts();
+}
+
+
+//-------------------------------------------------
+// clear_interrupt - interrupt has been cleared
+//-------------------------------------------------
+void z80sio_device::clear_interrupt(int index, int type)
+{
+ LOGINT("%s Chan:%c Type:%s\n", FUNCNAME, 'A' + index, std::array<char const *, 3>
+ {{"INT_TRANSMIT", "INT_EXTERNAL", "INT_RECEIVE"}}[type]);
+
+ // clear interrupt
+ m_int_state[(index * 3) + type] &= ~Z80_DAISY_INT;
+ if (std::find_if(std::begin(m_int_state), std::end(m_int_state), [] (int state) { return bool(state & Z80_DAISY_INT); }) == std::end(m_int_state))
+ m_chanA->m_rr0 &= ~RR0_INTERRUPT_PENDING;
+
+ // update interrupt output
+ check_interrupts();
+}
+
+
+//-------------------------------------------------
+// return_from_interrupt - reset interrupt under
+// service latch
+//-------------------------------------------------
+void z80sio_device::return_from_interrupt()
+{
+ // loop over all interrupt sources
+ int const *const prio = interrupt_priorities();
+ for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i)
{
- /* These CPU variants use Bit 2 of WR2 of Channnel A to determine the priority Hi to Lo:
- 0: RxA TxA RxB TxB ExtA ExtB
- 1: RxA RxB TxA TxB ExtA ExtB */
- switch(type)
+ // find the first channel with an interrupt requested
+ if (m_int_state[prio[i]] & (Z80_DAISY_IEO))
{
- case z80sio_channel::INT_RECEIVE:
- case z80sio_channel::INT_SPECIAL: prio_level = z80sio_channel::INT_RCV_SPC_PRI_LVL; break; // 0
- case z80sio_channel::INT_TRANSMIT: prio_level = z80sio_channel::INT_TRANSMIT_PRI_LVL; break; // 1
- case z80sio_channel::INT_EXTERNAL: prio_level = z80sio_channel::INT_EXTERNAL_PRI_LVL; break; // 2
- default:
- logerror("Bad interrupt source being prioritized!");
- return -1;
+ // clear the IEO state and update the IRQs
+ m_int_state[prio[i]] &= ~Z80_DAISY_IEO;
+ check_interrupts();
+ LOGINT("%s - cleared IEO\n", FUNCNAME);
+ return;
}
- // Assume that the PRIORITY bit is set
- priority = (prio_level * 2) + index;
+ }
+ LOGINT("%s - failed to find an interrupt to clear IEO on!", FUNCNAME);
+}
+
+
+//-------------------------------------------------
+// read_vector - read modified interrupt vector
+//-------------------------------------------------
+uint8_t z80sio_device::read_vector()
+{
+ uint8_t vec = m_chanB->m_wr2;
- // Check if it actually was cleared
- if ( (m_chanA->m_wr2 & z80sio_channel::WR2_PRIORITY) == 0)
+ // if status doesn't affect vector, return unmodified value
+ if (!(m_chanB->m_wr1 & WR1_STATUS_VECTOR))
+ return vec;
+
+ // modify vector for highest-priority pending interrupt
+ int const *const prio = interrupt_priorities();
+ vec &= 0xf1U;
+ for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i)
+ {
+ if (m_int_state[prio[i]] & Z80_DAISY_INT)
{
- // Adjust priority if needed, only affects TxA and RxB
- if (index == CHANNEL_A && type == z80sio_channel::INT_TRANSMIT )
- priority--;
- else if (index == CHANNEL_B && type == z80sio_channel::INT_RECEIVE )
- priority++;
+ 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:
+ return vec | 0x08U;
+ 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)))
+ return vec | 0x0eU;
+ else if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanA->m_rr1 & RR1_SPECIAL))
+ return vec | 0x0eU;
+ else
+ return vec | 0x0cU;
+ case 3 + z80sio_channel::INT_TRANSMIT:
+ return vec | 0x00U;
+ 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)))
+ return vec | 0x06U;
+ else if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanB->m_rr1 & RR1_SPECIAL))
+ return vec | 0x06U;
+ else
+ return vec | 0x04U;
+ }
}
}
- else // Plain old z80sio
- {
- priority = (index << 2) | type;
- }
- return priority;
+
+ // no interrupt pending - stuff 011 in the variable bits
+ return vec | 0x06U;
}
/*
@@ -373,59 +728,92 @@ int z80sio_device::get_interrupt_prio(int index, int type)
interrrupt is generated, otherwise it will indicate the previous state."
8274: "If RR2 is specified but not read, no internal interrupts, regardless of priority, are accepted."
*/
-uint8_t z80sio_device::modify_vector(int index, int type)
+uint8_t i8274_new_device::read_vector()
{
- uint8_t vector = m_chanB->m_wr2;
- if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
+ // 8086 and 8085 modes have different variable bits
+ bool const aff(m_chanB->m_wr1 & WR1_STATUS_VECTOR);
+ int const shift(((m_chanA->m_wr2 & WR2_MODE_MASK) == WR2_MODE_8086_8088) ? 0 : 2);
+ uint8_t vec(m_chanB->m_wr2);
+
+ // if status doesn't affect vector, return unmodified value
+ if (aff)
+ vec &= ~(0x07U << shift);
+
+ // modify vector for highest-priority pending interrupt
+ int const *const prio = interrupt_priorities();
+ for (int i = 0; ARRAY_LENGTH(m_int_state) > i; ++i)
{
- if (m_chanB->m_wr1 & z80sio_channel::WR1_STATUS_VECTOR)
+ if (m_int_state[prio[i]] & Z80_DAISY_INT)
{
- vector = (!index << 2) | type;
- if((m_chanA->m_wr1 & 0x18) == z80sio_channel::WR2_MODE_8086_8088)
+ constexpr uint8_t RR1_SPECIAL(RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR | RR1_END_OF_FRAME);
+
+ // in non-vectored mode this serves the same function as the end of the second acknowldege cycle
+ if (!(m_chanB->m_wr2 & WR2_VECTORED_INT) && !machine().side_effect_disabled())
{
- vector = (m_chanB->m_wr2 & 0xf8) | vector; // m_chanB->m_wr2;
+ m_int_state[prio[i]] |= Z80_DAISY_IEO;
+ check_interrupts();
}
- else
+
+ // if status doesn't affect vector return unmodified value
+ if (!aff)
+ return vec;
+
+ switch (prio[i])
{
- vector = (m_chanB->m_wr2 & 0xe3) | (vector << 2); //(m_chanB->m_wr2 << 2);
+ case 0 + z80sio_channel::INT_TRANSMIT:
+ return vec | (0x04U << shift);
+ case 0 + z80sio_channel::INT_EXTERNAL:
+ return vec | (0x05U << shift);
+ 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)))
+ return vec | (0x07U << shift);
+ else if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanA->m_rr1 & RR1_SPECIAL))
+ return vec | (0x07U << shift);
+ else
+ return vec | (0x06U << shift);
+ case 3 + z80sio_channel::INT_TRANSMIT:
+ return vec | (0x00U << shift);
+ case 3 + z80sio_channel::INT_EXTERNAL:
+ return vec | (0x01U << shift);
+ 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)))
+ return vec | (0x03U << shift);
+ else if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanB->m_rr1 & RR1_SPECIAL))
+ return vec | (0x03U << shift);
+ else
+ return vec | (0x02U << shift);
}
}
}
- else
- {
- if (m_chanB->m_wr1 & z80sio_channel::WR1_STATUS_VECTOR)
- {
- // status affects vector
- vector = (m_chanB->m_wr2 & 0xf1) | (!index << 3) | (type << 1);
- }
- }
- return vector;
+
+ // no interrupt pending - stuff 111 in the variable bits
+ return aff ? (vec | (0x07 << shift)) : vec;
}
+
//-------------------------------------------------
-// trigger_interrupt - TODO: needs attention for SIO
+// interrupt_priorities - get interrupt indexes
+// in priority order
//-------------------------------------------------
-void z80sio_device::trigger_interrupt(int index, int type)
+int const *z80sio_device::interrupt_priorities() const
{
- uint8_t priority = get_interrupt_prio(index, type);
- uint8_t vector = modify_vector(index, type);
-
- LOGINT("%s %s Chan:%c Type:%s\n", tag(), FUNCNAME, 'A' + index, std::array<char const *, 4>
- {{"INT_TRANSMIT", "INT_EXTERNAL", "INT_RECEIVE", "INT_SPECIAL"}}[type]);
- LOGINT(" - Priority:%02x Vector:%02x\n", priority, vector);
-
- // update vector register
- m_chanB->m_rr2 = vector;
-
- // trigger interrupt
- m_int_state[priority] |= Z80_DAISY_INT;
- m_chanA->m_rr0 |= z80sio_channel::RR0_INTERRUPT_PENDING;
-
- // remember the source and channel
- m_int_source[priority] = (type & 0xff) | (index << 8);
+ static constexpr EQUIVALENT_ARRAY(m_int_state, int) prio{
+ 0 + z80sio_channel::INT_RECEIVE, 0 + z80sio_channel::INT_TRANSMIT, 0 + z80sio_channel::INT_EXTERNAL,
+ 3 + z80sio_channel::INT_RECEIVE, 3 + z80sio_channel::INT_TRANSMIT, 3 + z80sio_channel::INT_EXTERNAL };
+ return prio;
+}
- // check for interrupt
- check_interrupts();
+int const *i8274_new_device::interrupt_priorities() const
+{
+ static constexpr EQUIVALENT_ARRAY(m_int_state, int) prio_a{
+ 0 + z80sio_channel::INT_RECEIVE, 3 + z80sio_channel::INT_RECEIVE,
+ 0 + z80sio_channel::INT_TRANSMIT, 3 + z80sio_channel::INT_TRANSMIT,
+ 0 + z80sio_channel::INT_EXTERNAL, 3 + z80sio_channel::INT_EXTERNAL };
+ static constexpr EQUIVALENT_ARRAY(m_int_state, int) prio_b{
+ 0 + z80sio_channel::INT_RECEIVE, 0 + z80sio_channel::INT_TRANSMIT,
+ 3 + z80sio_channel::INT_RECEIVE, 3 + z80sio_channel::INT_TRANSMIT,
+ 0 + z80sio_channel::INT_EXTERNAL, 3 + z80sio_channel::INT_EXTERNAL };
+ return (m_chanA->m_wr2 & WR2_PRIORITY) ? prio_a : prio_b;
}
@@ -435,10 +823,13 @@ void z80sio_device::trigger_interrupt(int index, int type)
int z80sio_device::m1_r()
{
LOGINT("%s %s \n",FUNCNAME, tag());
- if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
- return 0;
- else
- return z80daisy_irq_ack();
+ return z80daisy_irq_ack();
+}
+
+int i8274_new_device::m1_r()
+{
+ LOGINT("%s %s \n",FUNCNAME, tag());
+ return 0;
}
@@ -506,45 +897,68 @@ WRITE8_MEMBER( z80sio_device::ba_cd_w )
//-------------------------------------------------
// z80sio_channel - constructor
//-------------------------------------------------
-z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, Z80SIO_CHANNEL, tag, owner, clock)
- , device_serial_interface(mconfig, *this)
- , m_rx_error(0)
+z80sio_channel::z80sio_channel(
+ const machine_config &mconfig,
+ device_type type,
+ const char *tag,
+ device_t *owner,
+ uint32_t clock,
+ uint8_t rr1_auto_reset)
+ : device_t(mconfig, type, tag, owner, clock)
+ , m_rx_fifo_depth(0)
+ , m_rx_data_fifo(0)
+ , m_rx_error_fifo(0)
, m_rx_clock(0)
+ , m_rx_count(0)
+ , m_rx_bit(0)
+ , m_rx_sr(0)
, m_rx_first(0)
, m_rx_break(0)
- , m_rx_rr0_latch(0)
- , m_rxd(0)
- , m_sh(0)
- , m_cts(0)
- , m_dcd(0)
+ , m_rxd(1)
, m_tx_data(0)
- , m_tx_clock(0)
- , m_dtr(0)
- , m_rts(0)
- , m_sync(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_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)
{
LOG("%s\n",FUNCNAME);
- // Reset all registers
- m_rr0 = m_rr1 = m_rr2 = 0;
- m_wr0 = m_wr1 = m_wr2 = m_wr3 = m_wr4 = m_wr5 = m_wr6 = m_wr7 = 0;
+
+ // Reset all registers
+ m_rr0 = m_rr1 = 0;
+ m_wr0 = m_wr1 = m_wr2 = m_wr3 = m_wr4 = m_wr5 = m_wr6 = m_wr7 = 0;
+}
+
+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)
+{
+}
+
+i8274_channel::i8274_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : z80sio_channel(mconfig, I8274_CHANNEL, tag, owner, clock, RR1_RX_OVERRUN_ERROR)
+{
}
//-------------------------------------------------
-// start - channel startup
+// resove_objects - channel setup
//-------------------------------------------------
-void z80sio_channel::device_start()
+void z80sio_channel::device_resolve_objects()
{
LOG("%s\n",FUNCNAME);
m_uart = downcast<z80sio_device *>(owner());
m_index = m_uart->get_channel_index(this);
- m_variant = ((z80sio_device *)owner())->m_variant;
+}
+
+//-------------------------------------------------
+// start - channel startup
+//-------------------------------------------------
+void z80sio_channel::device_start()
+{
+ LOG("%s\n",FUNCNAME);
// state saving
save_item(NAME(m_rr0));
save_item(NAME(m_rr1));
- save_item(NAME(m_rr2));
save_item(NAME(m_wr0));
save_item(NAME(m_wr1));
save_item(NAME(m_wr2));
@@ -553,20 +967,32 @@ void z80sio_channel::device_start()
save_item(NAME(m_wr5));
save_item(NAME(m_wr6));
save_item(NAME(m_wr7));
- save_item(NAME(m_rx_error));
+ save_item(NAME(m_rx_fifo_depth));
+ save_item(NAME(m_rx_data_fifo));
+ save_item(NAME(m_rx_error_fifo));
save_item(NAME(m_rx_clock));
+ save_item(NAME(m_rx_count));
+ save_item(NAME(m_rx_bit));
+ save_item(NAME(m_rx_sr));
save_item(NAME(m_rx_first));
save_item(NAME(m_rx_break));
- save_item(NAME(m_rx_rr0_latch));
- save_item(NAME(m_sh));
- save_item(NAME(m_cts));
- save_item(NAME(m_dcd));
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_parity));
+ 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_txd));
save_item(NAME(m_dtr));
save_item(NAME(m_rts));
+ save_item(NAME(m_ext_latched));
+ save_item(NAME(m_brk_latched));
+ save_item(NAME(m_dcd));
save_item(NAME(m_sync));
- save_item(NAME(m_variant));
+ save_item(NAME(m_cts));
}
@@ -578,145 +1004,217 @@ void z80sio_channel::device_reset()
LOG("%s\n", FUNCNAME);
// Reset RS232 emulation
- receive_register_reset();
- transmit_register_reset();
+ m_rx_fifo_depth = 0;
+ m_rx_data_fifo = m_rx_error_fifo = 0U;
+ m_rx_bit = 0;
+ m_tx_count = 0;
+ m_tx_bits = 0;
+ m_rr0 &= ~RR0_RX_CHAR_AVAILABLE;
+ m_rr1 &= ~(RR1_PARITY_ERROR | RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR);
// disable receiver
m_wr3 &= ~WR3_RX_ENABLE;
// disable transmitter
m_wr5 &= ~WR5_TX_ENABLE;
- m_rr0 |= RR0_TX_BUFFER_EMPTY;
+ m_rr0 |= RR0_TX_BUFFER_EMPTY | RR0_TX_UNDERRUN;
m_rr1 |= RR1_ALL_SENT;
+ m_tx_flags = 0U;
+
+ // TODO: what happens to WAIT/READY?
// reset external lines
- set_rts(1);
- set_dtr(1);
+ out_rts_cb(m_rts = 1);
+ out_dtr_cb(m_dtr = 1);
// reset interrupts
+ m_uart->clear_interrupt(m_index, INT_TRANSMIT);
+ m_uart->clear_interrupt(m_index, INT_RECEIVE);
+ reset_ext_status();
+ // FIXME: should this actually reset all the interrtupts, or just the prioritisation (daisy chain) logic?
if (m_index == z80sio_device::CHANNEL_A)
- {
m_uart->reset_interrupts();
- }
}
+
//-------------------------------------------------
-// tra_callback -
+// transmit_enable - start transmission if
+// conditions met
//-------------------------------------------------
-void z80sio_channel::tra_callback()
+void z80sio_channel::transmit_enable()
{
- if (!(m_wr5 & WR5_TX_ENABLE))
+ if (!m_tx_bits && transmit_allowed())
{
- LOGBIT("%s() \"%s \"Channel %c transmit mark 1 m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
- // transmit mark
- if (m_index == z80sio_device::CHANNEL_A)
- m_uart->m_out_txda_cb(1);
- else
- m_uart->m_out_txdb_cb(1);
- }
- else if (m_wr5 & WR5_SEND_BREAK)
- {
- LOGBIT("%s() \"%s \"Channel %c send break 1 m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
- // transmit break
- if (m_index == z80sio_device::CHANNEL_A)
- m_uart->m_out_txda_cb(0);
- else
- m_uart->m_out_txdb_cb(0);
+ 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();
+ 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 (!is_transmit_register_empty())
- {
- int db = transmit_register_get_data_bit();
+}
- LOGBIT("%s() \"%s \"Channel %c transmit data bit %d m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, db, m_wr5);
- // transmit data
- if (m_index == z80sio_device::CHANNEL_A)
- m_uart->m_out_txda_cb(db);
- else
- m_uart->m_out_txdb_cb(db);
- }
+//-------------------------------------------------
+// transmit_complete - transmit shift register
+// empty
+//-------------------------------------------------
+void z80sio_channel::transmit_complete()
+{
+ LOG("%s %s\n",FUNCNAME, tag());
+
+ if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC)
+ sync_tx_sr_empty();
+ else if (transmit_allowed() && !(m_rr0 & RR0_TX_BUFFER_EMPTY))
+ async_tx_setup(); // async mode, with data available
else
- {
- LOGBIT("%s() \"%s \"Channel %c Failed to transmit m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
- logerror("%s \"%s \"Channel %c Failed to transmit\n", FUNCNAME, owner()->tag(), 'A' + m_index);
- }
+ LOGTX("%s() \"%s \"Channel %c Transmit buffer empty m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
}
-
//-------------------------------------------------
-// tra_complete -
+// sync_tx_sr_empty - set up next chunk of bits
+// to send
//-------------------------------------------------
-void z80sio_channel::tra_complete()
+void z80sio_channel::sync_tx_sr_empty()
{
- LOG("%s %s\n",FUNCNAME, tag());
- if ((m_wr5 & WR5_TX_ENABLE) && !(m_wr5 & WR5_SEND_BREAK) && !(m_rr0 & RR0_TX_BUFFER_EMPTY))
+ if (!transmit_allowed())
{
- LOGTX("%s() \"%s \"Channel %c Transmit Data Byte '%02x' m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_tx_data, m_wr5);
+ LOGTX("%s() Channel %c Transmitter Disabled m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_wr5);
- transmit_register_setup(m_tx_data);
+ // 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;
+ }
+ 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);
// 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_wr5 & WR5_SEND_BREAK)
+ else if ((m_rr0 & RR0_TX_UNDERRUN) || ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_8_BIT))
{
- LOGTX("%s() \"%s \"Channel %c Transmit Break 0 m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
- // transmit break
- if (m_index == z80sio_device::CHANNEL_A)
- m_uart->m_out_txda_cb(0);
- else
- m_uart->m_out_txdb_cb(0);
+ // 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);
}
else
{
- LOGTX("%s() \"%s \"Channel %c Transmit Mark 1 m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
- // transmit mark
- if (m_index == z80sio_device::CHANNEL_A)
- m_uart->m_out_txda_cb(1);
- else
- m_uart->m_out_txdb_cb(1);
- }
+ LOGTX("%s() Channel %c Transmit FCS '%04x' m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_tx_crc, m_wr5);
- // if transmit buffer is empty
- if (m_rr0 & RR0_TX_BUFFER_EMPTY)
- {
- LOGTX("%s() \"%s \"Channel %c Transmit buffer empty m_wr5:%02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_wr5);
- // then all characters have been sent
- m_rr1 |= RR1_ALL_SENT;
+ // just for fun, SDLC sends the FCS inverted in reverse bit order
+ uint16_t const fcs(BITSWAP16(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);
- // when the RTS bit is reset, the _RTS output goes high after the transmitter empties
- if (!m_rts)
- set_rts(1);
+ // set the underrun flag so it will send sync next time
+ m_rr0 |= RR0_TX_UNDERRUN;
+ trigger_ext_int();
}
}
//-------------------------------------------------
-// rcv_callback -
+// async_tx_setup - set up for asynchronous
+// transmission
//-------------------------------------------------
-void z80sio_channel::rcv_callback()
+void z80sio_channel::async_tx_setup()
{
- if (m_wr3 & WR3_RX_ENABLE)
- {
- LOGBIT("%s() \"%s \"Channel %c Received Data Bit %d\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_rxd);
- receive_register_update_bit(m_rxd);
- }
+ 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
+
+ // 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);
}
//-------------------------------------------------
-// rcv_complete -
+// reset_ext_status - reset external/status
+// condiotions
//-------------------------------------------------
-void z80sio_channel::rcv_complete()
+void z80sio_channel::reset_ext_status()
{
- uint8_t data;
+ // this will clear latched external pin state
+ m_ext_latched = 0;
+ m_brk_latched = 0;
+ read_ext();
- receive_register_extract();
- data = get_received_char();
- LOGRCV("%s() \"%s \"Channel %c Received Data %02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, data);
- receive_data(data);
+ // Clear any pending External interrupt
+ m_uart->clear_interrupt(m_index, INT_EXTERNAL);
+}
+
+
+//-------------------------------------------------
+// read_ext - copy external status to register
+//-------------------------------------------------
+void z80sio_channel::read_ext()
+{
+ // clear to send
+ if (m_cts)
+ m_rr0 &= ~RR0_CTS;
+ else
+ m_rr0 |= RR0_CTS;
+
+ // data carrier detect
+ if (m_dcd)
+ m_rr0 &= ~RR0_DCD;
+ else
+ m_rr0 |= RR0_DCD;
+
+ // sync is a general-purpose input in asynchronous mode
+ if ((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC)
+ {
+ if (m_sync)
+ m_rr0 &= ~RR0_SYNC_HUNT;
+ else
+ m_rr0 |= RR0_SYNC_HUNT;
+ }
+}
+
+//-------------------------------------------------
+// trigger_ext_int - trigger external signal
+// interrupt
+//-------------------------------------------------
+void z80sio_channel::trigger_ext_int()
+{
+ // update line
+ if (!m_ext_latched)
+ read_ext();
+ m_ext_latched = 1;
+
+ // trigger interrupt if enabled
+ if (m_wr1 & WR1_EXT_INT_ENABLE)
+ m_uart->trigger_interrupt(m_index, INT_EXTERNAL);
}
@@ -749,49 +1247,24 @@ int z80sio_channel::get_clock_mode()
CR5 = m_wr5 and SR1 = m_rr1
*/
-void z80sio_channel::set_rts(int state)
-{
- LOG("%s(%d) \"%s\" Channel %c \n", FUNCNAME, state, owner()->tag(), 'A' + m_index);
- if (m_index == z80sio_device::CHANNEL_A)
- m_uart->m_out_rtsa_cb(state);
- else
- m_uart->m_out_rtsb_cb(state);
-}
-
-void z80sio_channel::update_rts()
+void z80sio_channel::update_dtr_rts_break()
{
// LOG("%s(%d) \"%s\" Channel %c \n", FUNCNAME, state, owner()->tag(), 'A' + m_index);
LOG("%s() \"%s\" Channel %c \n", FUNCNAME, owner()->tag(), 'A' + m_index);
+
+ // RTS is affected by transmit queue state in asynchronous mode
if (m_wr5 & WR5_RTS)
- {
- // when the RTS bit is set, the _RTS output goes low
- set_rts(0);
- m_rts = 1;
- }
+ 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)
+ set_rts(1); // in synchronous mode, there's no automatic RTS
else
- {
- // when the RTS bit is reset, the _RTS output goes high after the transmitter empties
- m_rts = 0;
- }
-
- // data terminal ready output follows the state programmed into the DTR bit*/
- set_dtr((m_wr5 & WR5_DTR) ? 0 : 1);
-}
+ set_rts(((m_rr0 & RR0_TX_BUFFER_EMPTY) && !m_tx_count) ? 1 : 0); // TODO: is this affected by transmit enable?
-//-------------------------------------------------
-// get_stop_bits - get number of stop bits
-//-------------------------------------------------
-device_serial_interface::stop_bits_t z80sio_channel::get_stop_bits()
-{
- LOG("%s %s\n",FUNCNAME, tag());
- switch (m_wr4 & WR4_STOP_BITS_MASK)
- {
- case WR4_STOP_BITS_1: return STOP_BITS_1;
- case WR4_STOP_BITS_1_5: return STOP_BITS_1_5;
- case WR4_STOP_BITS_2: return STOP_BITS_2;
- }
+ // break immediately forces spacing condition on TxD output
+ out_txd_cb((m_wr5 & WR5_SEND_BREAK) ? 0 : m_txd);
- return STOP_BITS_0;
+ // data terminal ready output follows the state programmed into the DTR bit
+ set_dtr((m_wr5 & WR5_DTR) ? 0 : 1);
}
@@ -818,19 +1291,32 @@ int z80sio_channel::get_rx_word_length()
//-------------------------------------------------
// get_tx_word_length - get transmit word length
//-------------------------------------------------
-int z80sio_channel::get_tx_word_length()
+int z80sio_channel::get_tx_word_length() const
{
- LOG("%s %s\n",FUNCNAME, tag());
- int bits = 5;
+ LOG("%s\n", FUNCNAME);
switch (m_wr5 & WR5_TX_WORD_LENGTH_MASK)
{
- case WR5_TX_WORD_LENGTH_5: bits = 5; break;
- case WR5_TX_WORD_LENGTH_6: bits = 6; break;
- case WR5_TX_WORD_LENGTH_7: bits = 7; break;
- case WR5_TX_WORD_LENGTH_8: bits = 8; break;
+ case WR5_TX_WORD_LENGTH_5: return 5;
+ case WR5_TX_WORD_LENGTH_6: return 6;
+ case WR5_TX_WORD_LENGTH_7: return 7;
+ case WR5_TX_WORD_LENGTH_8: return 8;
}
+ 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;
}
@@ -840,8 +1326,11 @@ int z80sio_channel::get_tx_word_length()
* Break/Abort latch. */
uint8_t z80sio_channel::do_sioreg_rr0()
{
- LOGR("%s %s\n",FUNCNAME, tag());
- return m_rr0;
+ LOGR("%s\n", FUNCNAME);
+ if (m_tx_flags & TX_FLAG_SPECIAL)
+ return m_rr0 & ~RR0_TX_BUFFER_EMPTY;
+ else
+ return m_rr0;
}
/*
@@ -849,10 +1338,7 @@ uint8_t z80sio_channel::do_sioreg_rr0()
* codes for the I-Field in the SDLC Receive Mode. */
uint8_t z80sio_channel::do_sioreg_rr1()
{
- LOGR("%s %s\n",FUNCNAME, tag());
- // channel B only, channel A returns 0
- if (m_index == z80sio_device::CHANNEL_A) return 0;
-
+ LOGR("%s\n", FUNCNAME);
return m_rr1;
}
@@ -879,41 +1365,12 @@ uint8_t z80sio_channel::do_sioreg_rr1()
uint8_t z80sio_channel::do_sioreg_rr2()
{
LOGINT("%s %s Chan:%c\n", tag(), FUNCNAME, 'A' + m_index);
- // channel B only, channel A returns 0
- if (m_index == z80sio_device::CHANNEL_A) return 0;
-
- LOGINT(" - Channel B so we might need to update the vector modification\n");
- // Assume the unmodified vector
- m_rr2 = m_uart->m_chanB->m_wr2;
-
- if((m_variant == z80sio_device::TYPE_I8274) || (m_variant == z80sio_device::TYPE_UPD7201))
- {
- int i = 0;
- LOGINT(" - 8274 or 7201 requires special care\n");
- // loop over all interrupt sources
- for (auto & elem : m_uart->m_int_state)
- {
- // find the first channel with an interrupt requested
- if (elem & Z80_DAISY_INT)
- {
- LOGINT(" - Checking an INT source %d\n", i);
- m_rr2 = m_uart->modify_vector((m_uart->m_int_source[i] >> 8) & 1, m_uart->m_int_source[i] & 3);
- LOGINT(" - Found an INT request to ack while reading RR2\n");
- elem = Z80_DAISY_IEO; // Set IUS bit (called IEO in z80 daisy lingo)
- m_uart->check_interrupts();
- break;
- }
- i++;
- }
- // If no pending interrupt were found set variable bits to ones.
- if (i >= 6)
- {
- m_rr2 |= 0x1F;
- m_uart->m_chanA->m_rr0 &= ~z80sio_channel::RR0_INTERRUPT_PENDING;
- }
- }
- return m_rr2;
+ // channel B only, channel A returns 0
+ if (m_index == z80sio_device::CHANNEL_A)
+ return 0U;
+ else
+ return m_uart->read_vector();
}
@@ -923,18 +1380,16 @@ uint8_t z80sio_channel::do_sioreg_rr2()
uint8_t z80sio_channel::control_read()
{
uint8_t data = 0;
- uint8_t reg = m_wr0 & WR0_REGISTER_MASK;
+ uint8_t const reg = m_wr0 & WR0_REGISTER_MASK;
//LOG("%s %s\n",FUNCNAME, tag());
- if (reg != 0)
- {
- // mask out register index
+ // mask out register index
+ if (!machine().side_effect_disabled())
m_wr0 &= ~WR0_REGISTER_MASK;
- }
switch (reg)
{
- case REG_RR0_STATUS: data = do_sioreg_rr0(); break;
+ case REG_RR0_STATUS: data = do_sioreg_rr0(); break;
case REG_RR1_SPEC_RCV_COND: data = do_sioreg_rr1(); break;
case REG_RR2_INTERRUPT_VECT: data = do_sioreg_rr2(); break;
default:
@@ -951,23 +1406,25 @@ uint8_t z80sio_channel::control_read()
Handle the WR0 CRC Reset/Init bits separatelly, needed by derived devices separatelly from the commands */
void z80sio_channel::do_sioreg_wr0_resets(uint8_t data)
{
- LOG("%s %s\n",FUNCNAME, tag());
+ LOG("%s\n", FUNCNAME);
switch (data & WR0_CRC_RESET_CODE_MASK)
{
case WR0_CRC_RESET_NULL:
- LOG("Z80SIO \"%s\" Channel %c : CRC_RESET_NULL\n", owner()->tag(), 'A' + m_index);
+ LOGCMD("Z80SIO Channel %c : CRC_RESET_NULL\n", 'A' + m_index);
break;
case WR0_CRC_RESET_RX: /* In Synchronous mode: all Os (zeros) (CCITT-O CRC-16) */
- LOG("Z80SIO \"%s\" Channel %c : CRC_RESET_RX - not implemented\n", owner()->tag(), 'A' + m_index);
+ LOGCMD("Z80SIO Channel %c : CRC_RESET_RX - not implemented\n", 'A' + m_index);
break;
case WR0_CRC_RESET_TX: /* In HDLC mode: all 1s (ones) (CCITT-1) */
- LOG("Z80SIO \"%s\" Channel %c : CRC_RESET_TX - not implemented\n", owner()->tag(), 'A' + m_index);
+ LOGCMD("Z80SIO Channel %c : CRC_RESET_TX\n", 'A' + m_index);
+ m_tx_crc = ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC) ? ~uint16_t(0U) : uint16_t(0U);
break;
case WR0_CRC_RESET_TX_UNDERRUN: /* Resets Tx underrun/EOM bit (D6 of the SRO register) */
- LOG("Z80SIO \"%s\" Channel %c : CRC_RESET_TX_UNDERRUN - not implemented\n", owner()->tag(), 'A' + m_index);
+ LOGCMD("Z80SIO Channel %c : CRC_RESET_TX_UNDERRUN\n", 'A' + m_index);
+ m_rr0 &= ~RR0_TX_UNDERRUN;
break;
default: /* Will not happen unless someone messes with the mask */
- logerror("Z80SIO \"%s\" Channel %c : %s Wrong CRC reset/init command:%02x\n", owner()->tag(), 'A' + m_index, FUNCNAME, data & WR0_CRC_RESET_CODE_MASK);
+ logerror("Z80SIO Channel %c : %s Wrong CRC reset/init command:%02x\n", 'A' + m_index, FUNCNAME, data & WR0_CRC_RESET_CODE_MASK);
}
}
@@ -980,81 +1437,60 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data)
switch (data & WR0_COMMAND_MASK)
{
case WR0_NULL:
- LOGCMD("%s %s Ch:%c : Null command\n", FUNCNAME, tag(), 'A' + m_index);
+ LOGCMD("%s Ch:%c : Null command\n", FUNCNAME, 'A' + m_index);
break;
case WR0_SEND_ABORT:
- LOGCMD("%s %s Ch:%c : Send abort command - not implemented\n", FUNCNAME, tag(), 'A' + m_index);
+ LOGCMD("%s Ch:%c : Send abort command\n", FUNCNAME, 'A' + m_index);
+ // TODO: what actually happens if you try this in a mode other than SDLC?
+ // 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);
break;
case WR0_RESET_EXT_STATUS:
- // reset external/status interrupt
-
- m_rr0 &= ~(RR0_DCD | RR0_SYNC_HUNT | RR0_CTS | RR0_BREAK_ABORT);
- // release the latch
-
- m_rx_rr0_latch = 0;
- // update register to reflect wire values TODO: Check if this will fire new interrupts
- if (!m_dcd) m_rr0 |= RR0_DCD;
- if (m_sync) m_rr0 |= RR0_SYNC_HUNT;
- if (m_cts) m_rr0 |= RR0_CTS;
-
- // Clear any pending External interrupt
- m_uart->m_int_state[m_index == z80sio_device::CHANNEL_A ? 4 : 5] = 0;
-
- LOGINT("%s %s Ch:%c : Reset External/Status Interrupt\n", FUNCNAME, tag(), 'A' + m_index);
+ reset_ext_status();
+ LOGINT("%s Ch:%c : Reset External/Status Interrupt\n", FUNCNAME, 'A' + m_index);
break;
case WR0_CHANNEL_RESET:
// channel reset
- LOGCMD("%s %s Ch:%c : Channel Reset\n", FUNCNAME, tag(), 'A' + m_index);
+ LOGCMD("%s Ch:%c : Channel Reset\n", FUNCNAME, 'A' + m_index);
device_reset();
break;
case WR0_ENABLE_INT_NEXT_RX:
// enable interrupt on next receive character
- LOGINT("%s %s Ch:%c : Enable Interrupt on Next Received Character\n", FUNCNAME, tag(), 'A' + m_index);
+ LOGINT("%s Ch:%c : Enable Interrupt on Next Received Character\n", FUNCNAME, 'A' + m_index);
m_rx_first = 1;
break;
case WR0_RESET_TX_INT:
+ LOGCMD("%s Ch:%c : Reset Transmitter Interrupt Pending\n", FUNCNAME, 'A' + m_index);
// reset transmitter interrupt pending
- {
- uint8_t priority = 3; // Assume TxB
- // Check if it is TxA
- if (m_index == z80sio_device::CHANNEL_A)
- {
- // Check if priority bit is cleared
- priority = (m_uart->m_chanA->m_wr2 & z80sio_channel::WR2_PRIORITY) == 0 ? 1 : 2;
- }
- m_uart->m_int_state[priority] = 0;
- LOGINT("%s %s Ch:%c : Reset TX Interrupt, priority:%d\n", FUNCNAME, tag(), 'A' + m_index, priority);
- }
- m_uart->check_interrupts();
- LOGCMD("%s %s Ch:%c : Reset Transmitter Interrupt Pending\n", FUNCNAME, tag(), 'A' + m_index);
+ m_uart->clear_interrupt(m_index, INT_TRANSMIT);
break;
case WR0_ERROR_RESET:
// error reset
- LOGCMD("%s %s Ch:%c : Error Reset\n", FUNCNAME, tag(), 'A' + m_index);
- m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
- break;
- case WR0_RETURN_FROM_INT:
- LOGINT("%s %s Ch:%c : Return from interrupt\n", FUNCNAME, tag(), 'A' + m_index);
+ LOGCMD("%s Ch:%c : Error Reset\n", FUNCNAME, 'A' + m_index);
+ if ((WR1_RX_INT_FIRST == (m_wr1 & WR1_RX_INT_MODE_MASK)) && (m_rr1 & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR)))
{
- int found = 0;
- // loop over all interrupt sources
- for (auto & elem : m_uart->m_int_state)
- {
- // find the first channel with an interrupt requested
- if (elem & (Z80_DAISY_IEO))
- {
- // clear the IEO state and update the IRQs
- elem &= ~(Z80_DAISY_IEO);
- m_uart->check_interrupts();
- found = 1;
- break;
- }
- }
- LOGINT(" - %s\n", found == 0 ? "failed to find an interrupt to clear IEO on!" : "cleared IEO");
+ // 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);
+ advance_rx_fifo();
+ }
+ else
+ {
+ m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
}
break;
+ case WR0_RETURN_FROM_INT:
+ LOGINT("%s Ch:%c : Return from interrupt\n", FUNCNAME, 'A' + m_index);
+ if (m_index == z80sio_device::CHANNEL_A)
+ m_uart->return_from_interrupt();
+ break;
default:
- LOG("Z80SIO \"%s\" Channel %c : Unsupported WR0 command %02x mask %02x\n", owner()->tag(), 'A' + m_index, data, WR0_REGISTER_MASK);
+ LOG("Z80SIO Channel %c : Unsupported WR0 command %02x mask %02x\n", 'A' + m_index, data, WR0_REGISTER_MASK);
}
do_sioreg_wr0_resets(data);
@@ -1089,6 +1525,13 @@ void z80sio_channel::do_sioreg_wr1(uint8_t data)
LOG("Z80SIO \"%s\" Channel %c : Receiver Interrupt on All Characters\n", owner()->tag(), 'A' + m_index);
break;
}
+
+ if (!(data & WR1_WRDY_ENABLE))
+ set_ready(false);
+ 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));
}
void z80sio_channel::do_sioreg_wr2(uint8_t data)
@@ -1099,10 +1542,35 @@ void z80sio_channel::do_sioreg_wr2(uint8_t data)
void z80sio_channel::do_sioreg_wr3(uint8_t data)
{
+ LOGSETUP("Z80SIO Channel %c : Receiver Enable %u\n", 'A' + m_index, (data & WR3_RX_ENABLE) ? 1 : 0);
+ LOGSETUP("Z80SIO Channel %c : Sync Character Load Inhibit %u\n", 'A' + m_index, (data & WR3_SYNC_CHAR_LOAD_INHIBIT) ? 1 : 0);
+ LOGSETUP("Z80SIO Channel %c : Receive CRC Enable %u\n", 'A' + m_index, (data & WR3_RX_CRC_ENABLE) ? 1 : 0);
+ LOGSETUP("Z80SIO Channel %c : Auto Enables %u\n", 'A' + m_index, (data & WR3_AUTO_ENABLES) ? 1 : 0);
+ LOGSETUP("Z80SIO Channel %c : Receiver Bits/Character %u\n", 'A' + m_index, get_rx_word_length());
+ if (data & WR3_ENTER_HUNT_PHASE)
+ LOGCMD("Z80SIO Channel %c : Enter Hunt Phase\n", 'A' + m_index);
+
+ bool const was_allowed(receive_allowed());
m_wr3 = data;
- LOG("Z80SIO \"%s\" Channel %c : Receiver Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR3_RX_ENABLE) ? 1 : 0);
- LOG("Z80SIO \"%s\" Channel %c : Auto Enables %u\n", owner()->tag(), 'A' + m_index, (data & WR3_AUTO_ENABLES) ? 1 : 0);
- LOG("Z80SIO \"%s\" Channel %c : Receiver Bits/Character %u\n", owner()->tag(), 'A' + m_index, get_rx_word_length());
+
+ if (!was_allowed && receive_allowed())
+ {
+ receive_enabled();
+ }
+ 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();
+ }
+ }
}
void z80sio_channel::do_sioreg_wr4(uint8_t data)
@@ -1110,30 +1578,38 @@ void z80sio_channel::do_sioreg_wr4(uint8_t data)
m_wr4 = data;
LOG("Z80SIO \"%s\" Channel %c : Parity Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR4_PARITY_ENABLE) ? 1 : 0);
LOG("Z80SIO \"%s\" Channel %c : Parity %s\n", owner()->tag(), 'A' + m_index, (data & WR4_PARITY_EVEN) ? "Even" : "Odd");
- LOG("Z80SIO \"%s\" Channel %c : Stop Bits %s\n", owner()->tag(), 'A' + m_index, stop_bits_tostring(get_stop_bits()));
+ if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC)
+ LOG("Z80SIO \"%s\" Channel %c : Synchronous Mode\n", owner()->tag(), 'A' + m_index);
+ else
+ LOG("Z80SIO \"%s\" Channel %c : Stop Bits %g\n", owner()->tag(), 'A' + m_index, (((m_wr4 & WR4_STOP_BITS_MASK) >> 2) + 1) / 2.);
LOG("Z80SIO \"%s\" Channel %c : Clock Mode %uX\n", owner()->tag(), 'A' + m_index, get_clock_mode());
}
void z80sio_channel::do_sioreg_wr5(uint8_t data)
{
m_wr5 = data;
- LOG("Z80SIO \"%s\" Channel %c : Transmitter Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR5_TX_ENABLE) ? 1 : 0);
- LOG("Z80SIO \"%s\" Channel %c : Transmitter Bits/Character %u\n", owner()->tag(), 'A' + m_index, get_tx_word_length());
- LOG("Z80SIO \"%s\" Channel %c : Send Break %u\n", owner()->tag(), 'A' + m_index, (data & WR5_SEND_BREAK) ? 1 : 0);
- LOG("Z80SIO \"%s\" Channel %c : Request to Send %u\n", owner()->tag(), 'A' + m_index, (data & WR5_RTS) ? 1 : 0);
- LOG("Z80SIO \"%s\" Channel %c : Data Terminal Ready %u\n", owner()->tag(), 'A' + m_index, (data & WR5_DTR) ? 1 : 0);
+ LOG("Z80SIO Channel %c : Transmitter Enable %u\n", 'A' + m_index, (data & WR5_TX_ENABLE) ? 1 : 0);
+ LOG("Z80SIO Channel %c : Transmitter Bits/Character %u\n", 'A' + m_index, get_tx_word_length());
+ LOG("Z80SIO Channel %c : Transmit CRC Enable %u\n", 'A' + m_index, (data & WR5_TX_CRC_ENABLE) ? 1 : 0);
+ LOG("Z80SIO Channel %c : %s Frame Check Polynomial\n", 'A' + m_index, (data & WR5_CRC16) ? "CRC-16" : "SDLC");
+ LOG("Z80SIO Channel %c : Send Break %u\n", 'A' + m_index, (data & WR5_SEND_BREAK) ? 1 : 0);
+ LOG("Z80SIO Channel %c : Request to Send %u\n", 'A' + m_index, (data & WR5_RTS) ? 1 : 0);
+ LOG("Z80SIO Channel %c : Data Terminal Ready %u\n", 'A' + m_index, (data & WR5_DTR) ? 1 : 0);
+
+ if (~data & WR5_TX_ENABLE)
+ m_uart->clear_interrupt(m_index, INT_TRANSMIT);
}
void z80sio_channel::do_sioreg_wr6(uint8_t data)
{
- LOG("Z80SIO \"%s\" Channel %c : Transmit Sync %02x\n", owner()->tag(), 'A' + m_index, data);
- m_sync = (m_sync & 0xff00) | data;
+ LOG("Z80SIO \"%s\" Channel %c : Transmit Sync/Sync 1/SDLC Address %02x\n", owner()->tag(), 'A' + m_index, data);
+ m_wr6 = data;
}
void z80sio_channel::do_sioreg_wr7(uint8_t data)
{
- LOG("Z80SIO \"%s\" Channel %c : Receive Sync %02x\n", owner()->tag(), 'A' + m_index, data);
- m_sync = (data << 8) | (m_sync & 0xff);
+ LOG("Z80SIO \"%s\" Channel %c : Receive Sync/Sync 2/SDLC Flag %02x\n", owner()->tag(), 'A' + m_index, data);
+ m_wr7 = data;
}
//-------------------------------------------------
@@ -1151,16 +1627,16 @@ void z80sio_channel::control_write(uint8_t data)
m_wr0 &= ~WR0_REGISTER_MASK;
}
- LOG("\n%s(%02x) reg %02x\n", FUNCNAME, data, reg);
+ LOG("%s(%02x) reg %02x\n", FUNCNAME, data, reg);
switch (reg)
{
case REG_WR0_COMMAND_REGPT: do_sioreg_wr0(data); break;
case REG_WR1_INT_DMA_ENABLE: do_sioreg_wr1(data); m_uart->check_interrupts(); break;
case REG_WR2_INT_VECTOR: do_sioreg_wr2(data); break;
- case REG_WR3_RX_CONTROL: do_sioreg_wr3(data); update_serial(); break;
- case REG_WR4_RX_TX_MODES: do_sioreg_wr4(data); update_serial(); break;
- case REG_WR5_TX_CONTROL: do_sioreg_wr5(data); update_serial(); update_rts(); break;
+ case REG_WR3_RX_CONTROL: do_sioreg_wr3(data); break;
+ case REG_WR4_RX_TX_MODES: do_sioreg_wr4(data); update_dtr_rts_break(); break;
+ case REG_WR5_TX_CONTROL: do_sioreg_wr5(data); update_dtr_rts_break(); transmit_enable(); break;
case REG_WR6_SYNC_OR_SDLC_A: do_sioreg_wr6(data); break;
case REG_WR7_SYNC_OR_SDLC_F: do_sioreg_wr7(data); break;
default:
@@ -1174,25 +1650,18 @@ void z80sio_channel::control_write(uint8_t data)
//-------------------------------------------------
uint8_t z80sio_channel::data_read()
{
- uint8_t data = 0;
+ uint8_t const data = uint8_t(m_rx_data_fifo & 0x000000ffU);
- if (!m_rx_data_fifo.empty())
+ if (!machine().side_effect_disabled())
{
- // load data from the FIFO
- data = m_rx_data_fifo.dequeue();
+ // framing and overrun errors need to be cleared to advance the FIFO in interrupt-on-first mode
+ // TODO: Intel 8274 manual doesn't mention this behaviour - is it specific to Z80 SIO?
+ if ((WR1_RX_INT_FIRST != (m_wr1 & WR1_RX_INT_MODE_MASK)) || !(m_rr1 & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR)))
+ advance_rx_fifo();
- // load error status from the FIFO
- m_rr1 = (m_rr1 & ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR)) | m_rx_error_fifo.dequeue();
-
- if (m_rx_data_fifo.empty())
- {
- // no more characters available in the FIFO
- m_rr0 &= ~ RR0_RX_CHAR_AVAILABLE;
- }
+ LOG("Z80SIO \"%s\" Channel %c : Data Register Read '%02x'\n", owner()->tag(), 'A' + m_index, data);
}
- LOG("Z80SIO \"%s\" Channel %c : Data Register Read '%02x'\n", owner()->tag(), 'A' + m_index, data);
-
return data;
}
@@ -1202,85 +1671,218 @@ uint8_t z80sio_channel::data_read()
//-------------------------------------------------
void z80sio_channel::data_write(uint8_t data)
{
+ if (!(m_rr0 & RR0_TX_BUFFER_EMPTY))
+ LOGTX("Z80SIO \"%s\" Channel %c : Dropped Data Byte '%02x'\n", owner()->tag(), 'A' + m_index, m_tx_data);
+ LOGTX("Z80SIO Channel %c : Queue Data Byte '%02x'\n", 'A' + m_index, 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);
+
+ // 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);
- if ((m_wr5 & WR5_TX_ENABLE) && is_transmit_register_empty())
+ // may be possible to transmit immediately (synchronous mode will load when sync pattern completes)
+ if (async && !m_tx_bits && transmit_allowed())
+ async_tx_setup();
+}
+
+
+//-------------------------------------------------
+// advance_rx_fifo - move to next received byte
+//-------------------------------------------------
+void z80sio_channel::advance_rx_fifo()
+{
+ if (m_rx_fifo_depth)
{
- LOGTX("Z80SIO \"%s\" Channel %c : Transmit Data Byte '%02x'\n", owner()->tag(), 'A' + m_index, m_tx_data);
+ if (--m_rx_fifo_depth)
+ {
+ // shift the FIFO
+ m_rx_data_fifo >>= 8;
+ m_rx_error_fifo >>= 8;
- transmit_register_setup(m_tx_data);
+ // load error status from the FIFO
+ m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(m_rx_error_fifo & 0x000000ffU);
- // empty transmit buffer
- m_rr0 |= RR0_TX_BUFFER_EMPTY;
+ // if we're in interrupt-on-first mode, clear interrupt if there's no pending error condition
+ if ((m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_FIRST)
+ {
+ for (int i = 0; m_rx_fifo_depth > i; ++i)
+ {
+ if (uint8_t(m_rx_error_fifo >> (i * 8)) & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR))
+ return;
+ }
+ m_uart->clear_interrupt(m_index, INT_RECEIVE);
+ }
+ }
+ else
+ {
+ // no more characters available in the FIFO
+ m_rr0 &= ~RR0_RX_CHAR_AVAILABLE;
+ if ((m_wr1 & WR1_WRDY_ENABLE) && (m_wr1 & WR1_WRDY_ON_RX_TX))
+ set_ready(false);
+ m_uart->clear_interrupt(m_index, INT_RECEIVE);
+ }
+ }
+}
- if (m_wr1 & WR1_TX_INT_ENABLE)
- m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
+
+//-------------------------------------------------
+// receive_enabled - conditions have changed
+// allowing reception to begin
+//-------------------------------------------------
+
+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;
+}
+
+
+//-------------------------------------------------
+// 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.
+
+ 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)
+ {
+ // check for sync detection
+ bool acquired(false);
+ int limit(16);
+ 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;
+ break;
+ case WR4_SYNC_MODE_16_BIT:
+ acquired = (m_rx_bit >= 16) && (m_rx_sr == ((uint16_t(m_wr7) << 8) | uint16_t(m_wr6)));
+ break;
+ }
+ if (acquired)
+ {
+ // 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;
+ m_rx_bit = 0;
+ trigger_ext_int();
+ }
+ else
+ {
+ // track number of bits we have
+ m_rx_bit = (std::min)(m_rx_bit + 1, limit);
+ }
}
else
{
- LOGTX(" Transmitter %s, data byte dropped\n", m_wr5 & WR5_TX_ENABLE ? "not enabled" : "not emptied");
- m_rr0 &= ~RR0_TX_BUFFER_EMPTY;
+ // 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)
+ {
+ 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_rr1 &= ~RR1_ALL_SENT;
+ 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);
}
-
//-------------------------------------------------
// receive_data - receive data word
//-------------------------------------------------
-void z80sio_channel::receive_data(uint8_t data)
+
+void z80sio_channel::receive_data()
{
- LOGRCV("%s(%02x) %s:%c\n",FUNCNAME, data, tag(), 'A' + m_index);
+}
- if (m_rx_data_fifo.full())
+//-------------------------------------------------
+// queue_recevied - queue recevied character
+//-------------------------------------------------
+
+void z80sio_channel::queue_received(uint16_t data, uint32_t error)
+{
+ if (m_wr4 & WR4_PARITY_ENABLE)
{
- LOG(" Overrun detected\n");
- // receive overrun error detected
- m_rx_error |= RR1_RX_OVERRUN_ERROR;
+ int const word_length = get_rx_word_length();
+ uint16_t par(data);
+ for (int i = 1; word_length >= i; ++i)
+ par ^= BIT(par, i);
- switch (m_wr1 & WR1_RX_INT_MODE_MASK)
+ if (bool(BIT(par, 0)) == bool(m_wr4 & WR4_PARITY_EVEN))
{
- case WR1_RX_INT_FIRST:
- if (!m_rx_first)
- {
- m_uart->trigger_interrupt(m_index, INT_SPECIAL);
- }
- break;
-
- case WR1_RX_INT_ALL_PARITY:
- case WR1_RX_INT_ALL:
- m_uart->trigger_interrupt(m_index, INT_SPECIAL);
- break;
+ LOGRCV(" Parity error detected\n");
+ error |= RR1_PARITY_ERROR;
}
}
+
+ if (3 == m_rx_fifo_depth)
+ {
+ LOG(" Receive FIFO overrun detected\n");
+ // receive overrun error detected
+ error |= RR1_RX_OVERRUN_ERROR;
+
+ m_rx_data_fifo = (m_rx_data_fifo & 0x0000ffffU) | (uint32_t(data & 0x00ffU) << 16);
+ m_rx_error_fifo = (m_rx_error_fifo & 0x0000ffffU) | (error << 16);
+ }
else
{
// store received character and error status into FIFO
- m_rx_data_fifo.enqueue(data);
- m_rx_error_fifo.enqueue(m_rx_error);
+ if (!m_rx_fifo_depth)
+ m_rx_data_fifo = m_rx_error_fifo = 0U;
+ 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_rx_fifo_depth;
}
m_rr0 |= RR0_RX_CHAR_AVAILABLE;
+ if ((m_wr1 & WR1_WRDY_ENABLE) && (m_wr1 & WR1_WRDY_ON_RX_TX))
+ set_ready(true);
// receive interrupt
switch (m_wr1 & WR1_RX_INT_MODE_MASK)
{
case WR1_RX_INT_FIRST:
- if (m_rx_first)
- {
+ if (m_rx_first || (error & (RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR)))
m_uart->trigger_interrupt(m_index, INT_RECEIVE);
- m_rx_first = 0;
- }
+ m_rx_first = 0;
break;
case WR1_RX_INT_ALL_PARITY:
case WR1_RX_INT_ALL:
m_uart->trigger_interrupt(m_index, INT_RECEIVE);
break;
- default:
- LOG("No interrupt triggered\n");
+ default:
+ LOG("No receive interrupt triggered\n");
}
}
@@ -1290,35 +1892,16 @@ void z80sio_channel::receive_data(uint8_t data)
//-------------------------------------------------
WRITE_LINE_MEMBER( z80sio_channel::cts_w )
{
- LOG("%s(%02x) %s:%c\n",FUNCNAME, state, tag(), 'A' + m_index);
-
- if (m_cts != state)
+ if (bool(m_cts) != bool(state))
{
- // enable transmitter if in auto enables mode
- if (!state)
- if (m_wr3 & WR3_AUTO_ENABLES)
- m_wr5 |= WR5_TX_ENABLE;
+ LOGCTS("Z80SIO Channel %c : CTS %u\n", 'A' + m_index, state);
- // set clear to send
m_cts = state;
+ trigger_ext_int();
- if (!m_rx_rr0_latch)
- {
- if (!m_cts)
- m_rr0 |= RR0_CTS;
- else
- m_rr0 &= ~RR0_CTS;
-
- // trigger interrupt
- if (m_wr1 & WR1_EXT_INT_ENABLE)
- {
- // trigger interrupt
- m_uart->trigger_interrupt(m_index, INT_EXTERNAL);
-
- // latch read register 0
- m_rx_rr0_latch = 1;
- }
- }
+ // this may enable transmission
+ if (!state)
+ transmit_enable();
}
}
@@ -1328,34 +1911,17 @@ WRITE_LINE_MEMBER( z80sio_channel::cts_w )
//-------------------------------------------------
WRITE_LINE_MEMBER( z80sio_channel::dcd_w )
{
- LOG("Z80SIO \"%s\" Channel %c : DCD %u\n", owner()->tag(), 'A' + m_index, state);
-
- if (m_dcd != state)
+ if (bool(m_dcd) != bool(state))
{
- // enable receiver if in auto enables mode
- if (!state)
- if (m_wr3 & WR3_AUTO_ENABLES)
- m_wr3 |= WR3_RX_ENABLE;
+ LOG("Z80SIO Channel %c : DCD %u\n", 'A' + m_index, state);
- // set data carrier detect
+ bool const was_allowed(receive_allowed());
m_dcd = state;
+ trigger_ext_int();
- if (!m_rx_rr0_latch)
- {
- if (!m_dcd)
- m_rr0 |= RR0_DCD;
- else
- m_rr0 &= ~RR0_DCD;
-
- if (m_wr1 & WR1_EXT_INT_ENABLE)
- {
- // trigger interrupt
- m_uart->trigger_interrupt(m_index, INT_EXTERNAL);
-
- // latch read register 0
- m_rx_rr0_latch = 1;
- }
- }
+ // in auto-enable mode, this can start the receiver
+ if (!was_allowed && receive_allowed())
+ receive_enabled();
}
}
@@ -1365,29 +1931,15 @@ WRITE_LINE_MEMBER( z80sio_channel::dcd_w )
//-------------------------------------------------
WRITE_LINE_MEMBER( z80sio_channel::sync_w )
{
- LOG("Z80SIO \"%s\" Channel %c : Sync %u\n", owner()->tag(), 'A' + m_index, state);
-
- if (m_sh != state)
+ if (bool(m_sync) != bool(state))
{
- // set ring indicator state
- m_sh = state;
-
- if (!m_rx_rr0_latch)
- {
- if (m_sh)
- m_rr0 |= RR0_SYNC_HUNT;
- else
- m_rr0 &= ~RR0_SYNC_HUNT;
+ LOG("Z80SIO Channel %c : Sync %u\n", 'A' + m_index, state);
- if (m_wr1 & WR1_EXT_INT_ENABLE)
- {
- // trigger interrupt
- m_uart->trigger_interrupt(m_index, INT_EXTERNAL);
+ m_sync = state;
- // latch read register 0
- m_rx_rr0_latch = 1;
- }
- }
+ // sync is a general-purpose input in asynchronous mode
+ if ((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC)
+ trigger_ext_int();
}
}
@@ -1398,18 +1950,100 @@ 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');
- int clocks = get_clock_mode();
- if (clocks == 1)
- rx_clock_w(state);
- else if(state)
+ if (receive_allowed() && state && !m_rx_clock)
{
- rx_clock_w(m_rx_clock < clocks/2);
+ // 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();
+ m_rx_count = clocks;
+ }
+ else
+ {
+ --m_rx_count;
+ }
+ }
+ else if (!m_rx_bit)
+ {
+ // 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)
+ {
+ // this is the stop bit - framing error adds a half bit period
+ m_rx_count = m_rxd ? (clocks / 2) : clocks;
+ m_rx_bit = 0;
- m_rx_clock++;
- if (m_rx_clock == clocks)
- m_rx_clock = 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))
+ {
+ 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
+ {
+ // bit period hasn't expired
+ --m_rx_count;
+ }
}
+ m_rx_clock = state;
}
@@ -1419,83 +2053,91 @@ WRITE_LINE_MEMBER( z80sio_channel::rxc_w )
WRITE_LINE_MEMBER( z80sio_channel::txc_w )
{
//LOG("Z80SIO \"%s\" Channel %c : Transmitter Clock Pulse\n", owner()->tag(), m_index + 'A');
- int clocks = get_clock_mode();
- if (clocks == 1)
- tx_clock_w(state);
- else if(state)
+ if (!state && m_tx_clock)
{
- tx_clock_w(m_tx_clock < clocks/2);
-
- m_tx_clock++;
- if (m_tx_clock == clocks)
- m_tx_clock = 0;
-
- }
-}
-
-
-//-------------------------------------------------
-// update_serial -
-//-------------------------------------------------
-void z80sio_channel::update_serial()
-{
- int data_bit_count = get_rx_word_length();
- stop_bits_t stop_bits = get_stop_bits();
- parity_t parity;
+ // falling edge active
+ if (m_tx_count)
+ {
+ // 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);
+ }
- LOG("%s\n", FUNCNAME);
+ 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 (m_wr4 & WR4_PARITY_ENABLE)
- {
- LOG("- Parity enabled\n");
- if (m_wr4 & WR4_PARITY_EVEN)
- parity = PARITY_EVEN;
+ // if transmit buffer is empty in asynchronous mode then all characters have been sent
+ m_rr1 |= RR1_ALL_SENT;
+ }
+ }
else
- parity = PARITY_ODD;
- }
- else
- parity = PARITY_NONE;
-
- set_data_frame(1, data_bit_count, parity, stop_bits);
+ {
+ 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));
- int clocks = get_clock_mode();
+ // 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
+ {
+ LOGBIT("%s() Channel %c stuff bit %d m_wr5:%02x\n", FUNCNAME, 'A' + m_index, db, m_wr5);
+ }
+ m_tx_hist = (m_tx_hist << 1) | db;
- if (m_rxc > 0)
- {
- LOG("- RxC:%d/%d = %d\n", m_rxc, clocks, m_rxc / clocks);
- set_rcv_rate(m_rxc / clocks);
- }
+ // update output line state
+ if (bool(m_txd) != bool(db))
+ {
+ m_txd = db;
+ if (!(m_wr5 & WR5_SEND_BREAK))
+ out_txd_cb(m_txd);
+ }
- if (m_txc > 0)
- {
- LOG("- TxC:%d/%d = %d\n", m_txc, clocks, m_txc / clocks);
- set_tra_rate(m_txc / clocks);
+ // calculate next bit time
+ m_tx_count = get_clock_mode();
+ if (!stuff_zero && !--m_tx_bits)
+ {
+ switch (m_wr4 & WR4_STOP_BITS_MASK)
+ {
+ 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;
+ }
+ transmit_complete();
+ }
+ --m_tx_count;
+ }
}
- receive_register_reset(); // if stop bits is changed from 0, receive register has to be reset
-}
-
-
-//-------------------------------------------------
-// set_dtr -
-//-------------------------------------------------
-void z80sio_channel::set_dtr(int state)
-{
- LOG("%s(%d)\n", FUNCNAME, state);
- m_dtr = state;
-
- if (m_index == z80sio_device::CHANNEL_A)
- m_uart->m_out_dtra_cb(m_dtr);
- else
- m_uart->m_out_dtrb_cb(m_dtr);
-}
-
-//-------------------------------------------------
-// write_rx -
-//-------------------------------------------------
-WRITE_LINE_MEMBER(z80sio_channel::write_rx)
-{
- m_rxd = state;
- //only use rx_w when self-clocked
- if(m_rxc)
- device_serial_interface::rx_w(state);
+ m_tx_clock = state;
}
diff --git a/src/devices/machine/z80sio.h b/src/devices/machine/z80sio.h
index 4e18b377872..f4363d23ce6 100644
--- a/src/devices/machine/z80sio.h
+++ b/src/devices/machine/z80sio.h
@@ -67,9 +67,6 @@
#define SIO_CHANB_TAG "chb"
/* Generic macros */
-#define MCFG_Z80SIO_OFFSETS(_rxa, _txa, _rxb, _txb) \
- z80sio_device::configure_channels(*device, _rxa, _txa, _rxb, _txb);
-
#define MCFG_Z80SIO_OUT_INT_CB(_devcb) \
devcb = &z80sio_device::set_out_int_callback(*device, DEVCB_##_devcb);
@@ -78,47 +75,47 @@
// Port A callbacks
#define MCFG_Z80SIO_OUT_TXDA_CB(_devcb) \
- devcb = &z80sio_device::set_out_txda_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_txd_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_DTRA_CB(_devcb) \
- devcb = &z80sio_device::set_out_dtra_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_dtr_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_RTSA_CB(_devcb) \
- devcb = &z80sio_device::set_out_rtsa_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_rts_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_WRDYA_CB(_devcb) \
- devcb = &z80sio_device::set_out_wrdya_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_wrdy_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_SYNCA_CB(_devcb) \
- devcb = &z80sio_device::set_out_synca_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_sync_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_RXDRQA_CB(_devcb) \
- devcb = &z80sio_device::set_out_rxdrqa_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_rxdrq_callback<0>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_TXDRQA_CB(_devcb) \
- devcb = &z80sio_device::set_out_txdrqa_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_txdrq_callback<0>(*device, DEVCB_##_devcb);
// Port B callbacks
#define MCFG_Z80SIO_OUT_TXDB_CB(_devcb) \
- devcb = &z80sio_device::set_out_txdb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_txd_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_DTRB_CB(_devcb) \
- devcb = &z80sio_device::set_out_dtrb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_dtr_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_RTSB_CB(_devcb) \
- devcb = &z80sio_device::set_out_rtsb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_rts_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_WRDYB_CB(_devcb) \
- devcb = &z80sio_device::set_out_wrdyb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_wrdy_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_SYNCB_CB(_devcb) \
- devcb = &z80sio_device::set_out_syncb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_sync_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_RXDRQB_CB(_devcb) \
- devcb = &z80sio_device::set_out_rxdrqb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_rxdrq_callback<1>(*device, DEVCB_##_devcb);
#define MCFG_Z80SIO_OUT_TXDRQB_CB(_devcb) \
- devcb = &z80sio_device::set_out_txdrqb_callback(*device, DEVCB_##_devcb);
+ devcb = &z80sio_device::set_out_txdrq_callback<1>(*device, DEVCB_##_devcb);
//**************************************************************************
@@ -129,24 +126,15 @@
class z80sio_device;
-class z80sio_channel : public device_t,
- public device_serial_interface
+class z80sio_channel : public device_t
{
friend class z80sio_device;
+ friend class i8274_new_device;
+ friend class upd7201_new_device;
public:
z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
- // device-level overrides
- virtual void device_start() override;
- virtual void device_reset() override;
-
- // device_serial_interface overrides
- virtual void tra_callback() override;
- virtual void tra_complete() override;
- virtual void rcv_callback() override;
- virtual void rcv_complete() override;
-
// read register handlers
uint8_t do_sioreg_rr0();
uint8_t do_sioreg_rr1();
@@ -169,23 +157,17 @@ public:
uint8_t data_read();
void data_write(uint8_t data);
- void receive_data(uint8_t data);
-
- DECLARE_WRITE_LINE_MEMBER( write_rx );
+ DECLARE_WRITE_LINE_MEMBER( write_rx ) { m_rxd = state; }
DECLARE_WRITE_LINE_MEMBER( cts_w );
DECLARE_WRITE_LINE_MEMBER( dcd_w );
DECLARE_WRITE_LINE_MEMBER( rxc_w );
DECLARE_WRITE_LINE_MEMBER( txc_w );
DECLARE_WRITE_LINE_MEMBER( sync_w );
- int m_rxc;
- int m_txc;
-
// Register state
// read registers enum
uint8_t m_rr0; // REG_RR0_STATUS
uint8_t m_rr1; // REG_RR1_SPEC_RCV_COND
- uint8_t m_rr2; // REG_RR2_INTERRUPT_VECT
// write registers enum
uint8_t m_wr0; // REG_WR0_COMMAND_REGPT
uint8_t m_wr1; // REG_WR1_INT_DMA_ENABLE
@@ -196,15 +178,12 @@ public:
uint8_t m_wr6; // REG_WR6_SYNC_OR_SDLC_A
uint8_t m_wr7; // REG_WR7_SYNC_OR_SDLC_F
- int m_variant; // Set in device
-
protected:
enum
{
INT_TRANSMIT = 0,
INT_EXTERNAL,
- INT_RECEIVE,
- INT_SPECIAL
+ INT_RECEIVE
};
enum
@@ -235,174 +214,116 @@ protected:
REG_WR7_SYNC_OR_SDLC_F = 7
};
- enum
- {
- RR0_RX_CHAR_AVAILABLE = 0x01,
- RR0_INTERRUPT_PENDING = 0x02,
- RR0_TX_BUFFER_EMPTY = 0x04,
- RR0_DCD = 0x08,
- RR0_SYNC_HUNT = 0x10,
- RR0_CTS = 0x20,
- RR0_TX_UNDERRUN = 0x40,
- RR0_BREAK_ABORT = 0x80
- };
-
- enum
+ // used in a flag bitmap variable
+ enum : uint8_t
{
- RR1_ALL_SENT = 0x01,
- RR1_RESIDUE_CODE_MASK = 0x0e,
- RR1_PARITY_ERROR = 0x10,
- RR1_RX_OVERRUN_ERROR = 0x20,
- RR1_CRC_FRAMING_ERROR = 0x40,
- RR1_END_OF_FRAME = 0x80
+ 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
};
- enum
- {
- RR2_INT_VECTOR_MASK = 0xff,
- RR2_INT_VECTOR_V1 = 0x02,
- RR2_INT_VECTOR_V2 = 0x04,
- RR2_INT_VECTOR_V3 = 0x08
- };
+ z80sio_channel(
+ const machine_config &mconfig,
+ device_type type,
+ const char *tag,
+ device_t *owner,
+ uint32_t clock,
+ uint8_t rr1_auto_reset);
- enum
- {
- WR0_REGISTER_MASK = 0x07,
- WR0_COMMAND_MASK = 0x38,
- WR0_NULL = 0x00,
- WR0_SEND_ABORT = 0x08,
- WR0_RESET_EXT_STATUS = 0x10,
- WR0_CHANNEL_RESET = 0x18,
- WR0_ENABLE_INT_NEXT_RX = 0x20,
- WR0_RESET_TX_INT = 0x28,
- WR0_ERROR_RESET = 0x30,
- WR0_RETURN_FROM_INT = 0x38,
- WR0_CRC_RESET_CODE_MASK = 0xc0,
- WR0_CRC_RESET_NULL = 0x00,
- WR0_CRC_RESET_RX = 0x40,
- WR0_CRC_RESET_TX = 0x80,
- WR0_CRC_RESET_TX_UNDERRUN = 0xc0
- };
-
- enum
- {
- WR1_EXT_INT_ENABLE = 0x01,
- WR1_TX_INT_ENABLE = 0x02,
- WR1_STATUS_VECTOR = 0x04,
- WR1_RX_INT_MODE_MASK = 0x18,
- WR1_RX_INT_DISABLE = 0x00,
- WR1_RX_INT_FIRST = 0x08,
- WR1_RX_INT_ALL_PARITY = 0x10, // not supported
- WR1_RX_INT_ALL = 0x18,
- WR1_WRDY_ON_RX_TX = 0x20, // not supported
- WR1_WRDY_FUNCTION = 0x40, // not supported
- WR1_WRDY_ENABLE = 0x80 // not supported
- };
-
- enum
- {
- WR2_DATA_XFER_INT = 0x00, // not supported
- WR2_DATA_XFER_DMA_INT = 0x01, // not supported
- WR2_DATA_XFER_DMA = 0x02, // not supported
- WR2_DATA_XFER_ILLEGAL = 0x03, // not supported
- WR2_DATA_XFER_MASK = 0x03, // not supported
- WR2_PRIORITY = 0x04, // not supported
- WR2_MODE_8085_1 = 0x00, // not supported
- WR2_MODE_8085_2 = 0x08, // not supported
- WR2_MODE_8086_8088 = 0x10, // not supported
- WR2_MODE_ILLEGAL = 0x18, // not supported
- WR2_MODE_MASK = 0x18, // not supported
- WR2_VECTORED_INT = 0x20, // not supported
- WR2_PIN10_SYNDETB_RTSB = 0x80 // not supported
- };
-
- enum
- {
- 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_ENTER_HUNT_PHASE = 0x10, // not supported
- WR3_AUTO_ENABLES = 0x20,
- WR3_RX_WORD_LENGTH_MASK = 0xc0,
- WR3_RX_WORD_LENGTH_5 = 0x00,
- WR3_RX_WORD_LENGTH_7 = 0x40,
- WR3_RX_WORD_LENGTH_6 = 0x80,
- WR3_RX_WORD_LENGTH_8 = 0xc0
- };
-
- enum
- {
- WR4_PARITY_ENABLE = 0x01,
- WR4_PARITY_EVEN = 0x02,
- WR4_STOP_BITS_MASK = 0x0c,
- WR4_STOP_BITS_1 = 0x04,
- WR4_STOP_BITS_1_5 = 0x08, // not supported
- WR4_STOP_BITS_2 = 0x0c,
- WR4_SYNC_MODE_MASK = 0x30, // not supported
- WR4_SYNC_MODE_8_BIT = 0x00, // not supported
- WR4_SYNC_MODE_16_BIT = 0x10, // not supported
- WR4_SYNC_MODE_SDLC = 0x20, // not supported
- WR4_SYNC_MODE_EXT = 0x30, // not supported
- WR4_CLOCK_RATE_MASK = 0xc0,
- WR4_CLOCK_RATE_X1 = 0x00,
- WR4_CLOCK_RATE_X16 = 0x40,
- WR4_CLOCK_RATE_X32 = 0x80,
- WR4_CLOCK_RATE_X64 = 0xc0
- };
-
- enum
- {
- WR5_TX_CRC_ENABLE = 0x01, // not supported
- WR5_RTS = 0x02,
- WR5_CRC16 = 0x04, // not supported
- WR5_TX_ENABLE = 0x08,
- WR5_SEND_BREAK = 0x10,
- WR5_TX_WORD_LENGTH_MASK = 0x60,
- WR5_TX_WORD_LENGTH_5 = 0x00,
- WR5_TX_WORD_LENGTH_6 = 0x40,
- WR5_TX_WORD_LENGTH_7 = 0x20,
- WR5_TX_WORD_LENGTH_8 = 0x60,
- WR5_DTR = 0x80
- };
+ // device-level overrides
+ virtual void device_resolve_objects() override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
- void update_serial();
- void update_rts();
+ void update_dtr_rts_break();
void set_dtr(int state);
void set_rts(int state);
int get_clock_mode();
- stop_bits_t get_stop_bits();
int get_rx_word_length();
- int get_tx_word_length();
+ int get_tx_word_length() const;
+ int get_tx_word_length(uint8_t data) const;
// receiver state
- util::fifo<uint8_t, 3> m_rx_data_fifo;
- util::fifo<uint8_t, 3> m_rx_error_fifo;
- uint8_t m_rx_error; // current receive error
+ int m_rx_fifo_depth;
+ uint32_t m_rx_data_fifo;
+ uint32_t m_rx_error_fifo;
+
+ int m_rx_clock; // receive clock line state
+ int m_rx_count; // clocks until next sample
+ int m_rx_bit; // receive data bit (0 = start bit, 1 = LSB, etc.)
+ uint16_t m_rx_sr; // receive shift register
- int m_rx_clock; // receive clock pulse count
int m_rx_first; // first character received
int m_rx_break; // receive break condition
- uint8_t m_rx_rr0_latch; // read register 0 latched
int m_rxd;
int m_sh; // sync hunt
- int m_cts; // clear to send latch
- int m_dcd; // data carrier detect latch
// transmitter state
- uint8_t m_tx_data; // transmit data register
- int m_tx_clock; // transmit clock pulse count
-
+ 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
+ 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
+
+ int m_txd;
int m_dtr; // data terminal ready
int m_rts; // request to send
+ // external/status monitoring
+ int m_ext_latched; // changed data lines
+ int m_brk_latched; // break status latched
+ int m_cts; // clear to send line state
+ int m_dcd; // data carrier detect line state
+ int m_sync; // sync line state
+
// synchronous state
- uint16_t m_sync; // sync character
int m_index;
z80sio_device *m_uart;
+
+private:
+ // helpers
+ void out_txd_cb(int state);
+ void out_rts_cb(int state);
+ void out_dtr_cb(int state);
+ void set_ready(bool ready);
+ bool receive_allowed() const;
+ bool transmit_allowed() const;
+
+ void receive_enabled();
+ void sync_receive();
+ void receive_data();
+ void queue_received(uint16_t data, uint32_t error);
+ void advance_rx_fifo();
+
+ 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_idle();
+
+ void reset_ext_status();
+ void read_ext();
+ void trigger_ext_int();
+
+ uint8_t const m_rr1_auto_reset;
+};
+
+
+// ======================> i8274_channel
+
+class i8274_channel : public z80sio_channel
+{
+public:
+ i8274_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
@@ -417,21 +338,14 @@ public:
// construction/destruction
z80sio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
- template <class Object> static devcb_base &set_out_txda_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_txda_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_dtra_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_dtra_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_rtsa_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_rtsa_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_wrdya_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_wrdya_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_synca_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_synca_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_txdb_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_txdb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_dtrb_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_dtrb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_rtsb_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_rtsb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_wrdyb_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_wrdyb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_syncb_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_syncb_cb.set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_txd_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_txd_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_dtr_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_dtr_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_rts_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_rts_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_wrdy_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_wrdy_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_sync_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_sync_cb[N].set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &set_out_int_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_int_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_rxdrqa_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_rxdrqa_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_txdrqa_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_txdrqa_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_rxdrqb_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_rxdrqb_cb.set_callback(std::forward<Object>(cb)); }
- template <class Object> static devcb_base &set_out_txdrqb_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_txdrqb_cb.set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_rxdrq_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_rxdrq_cb[N].set_callback(std::forward<Object>(cb)); }
+ template <unsigned N, class Object> static devcb_base &set_out_txdrq_callback(device_t &device, Object &&cb) { return downcast<z80sio_device &>(device).m_out_txdrq_cb[N].set_callback(std::forward<Object>(cb)); }
static void static_set_cputag(device_t &device, const char *tag)
{
@@ -439,15 +353,6 @@ public:
dev.m_cputag = tag;
}
- static void configure_channels(device_t &device, int rxa, int txa, int rxb, int txb)
- {
- z80sio_device &dev = downcast<z80sio_device &>(device);
- dev.m_rxca = rxa;
- dev.m_txca = txa;
- dev.m_rxcb = rxb;
- dev.m_txcb = txb;
- }
-
DECLARE_READ8_MEMBER( cd_ba_r );
DECLARE_WRITE8_MEMBER( cd_ba_w );
DECLARE_READ8_MEMBER( ba_cd_r );
@@ -464,7 +369,7 @@ public:
DECLARE_WRITE8_MEMBER( cb_w ) { m_chanB->control_write(data); }
// interrupt acknowledge
- int m1_r();
+ virtual int m1_r();
DECLARE_WRITE_LINE_MEMBER( rxa_w ) { m_chanA->write_rx(state); }
DECLARE_WRITE_LINE_MEMBER( rxb_w ) { m_chanB->write_rx(state); }
@@ -481,9 +386,10 @@ public:
DECLARE_WRITE_LINE_MEMBER( syncb_w ) { m_chanB->sync_w(state); }
protected:
- z80sio_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t variant);
+ z80sio_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
// device-level overrides
+ virtual void device_resolve_objects() override;
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
@@ -496,18 +402,13 @@ protected:
// internal interrupt management
void check_interrupts();
void reset_interrupts();
- int get_interrupt_prio(int index, int type);
- uint8_t modify_vector(int index, int type);
- void trigger_interrupt(int index, int state);
- int get_channel_index(z80sio_channel *ch) { return (ch == m_chanA) ? 0 : 1; }
+ void trigger_interrupt(int index, int type);
+ void clear_interrupt(int index, int type);
+ void return_from_interrupt();
+ virtual uint8_t read_vector();
+ virtual int const *interrupt_priorities() const;
- // CPU types that has slightly different behaviour
- enum
- {
- TYPE_Z80SIO = 0x001,
- TYPE_UPD7201 = 0x002,
- TYPE_I8274 = 0x004
- };
+ int get_channel_index(z80sio_channel const *ch) const { return (ch == m_chanA) ? 0 : 1; }
enum
{
@@ -519,51 +420,51 @@ protected:
required_device<z80sio_channel> m_chanB;
// internal state
- int m_rxca;
- int m_txca;
- int m_rxcb;
- int m_txcb;
-
- devcb_write_line m_out_txda_cb;
- devcb_write_line m_out_dtra_cb;
- devcb_write_line m_out_rtsa_cb;
- devcb_write_line m_out_wrdya_cb;
- devcb_write_line m_out_synca_cb;
-
- devcb_write_line m_out_txdb_cb;
- devcb_write_line m_out_dtrb_cb;
- devcb_write_line m_out_rtsb_cb;
- devcb_write_line m_out_wrdyb_cb;
- devcb_write_line m_out_syncb_cb;
+ devcb_write_line m_out_txd_cb[2];
+ devcb_write_line m_out_dtr_cb[2];
+ devcb_write_line m_out_rts_cb[2];
+ devcb_write_line m_out_wrdy_cb[2];
+ devcb_write_line m_out_sync_cb[2];
devcb_write_line m_out_int_cb;
- devcb_write_line m_out_rxdrqa_cb;
- devcb_write_line m_out_txdrqa_cb;
- devcb_write_line m_out_rxdrqb_cb;
- devcb_write_line m_out_txdrqb_cb;
+ devcb_write_line m_out_rxdrq_cb[2];
+ devcb_write_line m_out_txdrq_cb[2];
int m_int_state[8]; // interrupt state
int m_int_source[8]; // interrupt source
- int m_variant;
const char *m_cputag;
};
-class upd7201_new_device : public z80sio_device
+class i8274_new_device : public z80sio_device
{
public:
- upd7201_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ i8274_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual int m1_r() override;
+
+protected:
+ i8274_new_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+
+ // device_t overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+
+ // device_z80daisy_interface overrides
+ virtual int z80daisy_irq_ack() override;
+ virtual void z80daisy_irq_reti() override;
+
+ virtual uint8_t read_vector() override;
+ virtual int const *interrupt_priorities() const override;
};
-class i8274_new_device : public z80sio_device
+class upd7201_new_device : public i8274_new_device
{
public:
- i8274_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ upd7201_new_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
-// device type definition
+// device type declaration
DECLARE_DEVICE_TYPE(Z80SIO, z80sio_device)
-DECLARE_DEVICE_TYPE(Z80SIO_CHANNEL, z80sio_channel)
-DECLARE_DEVICE_TYPE(UPD7201_NEW, upd7201_new_device)
DECLARE_DEVICE_TYPE(I8274_NEW, i8274_new_device)
+DECLARE_DEVICE_TYPE(UPD7201_NEW, upd7201_new_device)
#endif // MAME_MACHINE_Z80SIO_H