diff options
Diffstat (limited to 'src/devices/machine')
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 ¶ms) +{ + 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 ¶ms) 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 |