diff --git a/src/devices/machine/k056230.cpp b/src/devices/machine/k056230.cpp index 487a26da35daa..8aa0ab182f393 100644 --- a/src/devices/machine/k056230.cpp +++ b/src/devices/machine/k056230.cpp @@ -1,8 +1,8 @@ // license:BSD-3-Clause -// copyright-holders:Fabio Priuli +// copyright-holders:Fabio Priuli, Ariane Fugmann /************************************************************************************************** -Konami IC 056230 (LANC) +Konami IC 056230 (LANC) / KS40011 Device Notes: -The custom IC itself @@ -13,15 +13,20 @@ Device Notes: -HYC2485S RS485 transceiver TODO: -- nearly everything, currently a wrapper to make games happy and don't fail POSTs +- "manual" TX/RX mode - LANC part name for konami/viper.cpp **************************************************************************************************/ #include "emu.h" - #include "k056230.h" +#include "emuopts.h" + +#include "asio.h" + +#include <iostream> + #define LOG_REG_READS (1U << 1) #define LOG_REG_WRITES (1U << 2) #define LOG_RAM_READS (1U << 3) @@ -32,13 +37,469 @@ Device Notes: #define VERBOSE (0) #include "logmacro.h" +class k056230_device::context +{ +public: + context(k056230_device &device) : + m_device(device), + m_acceptor(m_ioctx), + m_sock_rx(m_ioctx), + m_sock_tx(m_ioctx), + m_timeout_tx(m_ioctx), + m_stopping(false), + m_state_rx(0U), + m_state_tx(0U) + { + } + + void start() + { + + m_thread = std::thread( + [this] () + { + LOG("network thread started\n"); + try { + m_ioctx.run(); + } catch (const std::exception& e) { + LOG("Exception in network thread: %s\n", e.what()); + } catch (...) { // Catch any other unknown exceptions + LOG("Unknown exception in network thread\n"); + } + LOG("network thread completed\n"); + }); + } + + void reset(std::string localhost, std::string localport, std::string remotehost, std::string remoteport) + { + m_ioctx.post( + [this, localhost = std::move(localhost), localport = std::move(localport), remotehost = std::move(remotehost), remoteport = std::move(remoteport)] () + { + std::error_code err; + asio::ip::tcp::resolver resolver(m_ioctx); + + for (auto &&resolveIte : resolver.resolve(localhost, localport, asio::ip::tcp::resolver::flags::address_configured, err)) + { + m_localaddr = resolveIte.endpoint(); + LOG("localhost = %s\n", *m_localaddr); + } + if (err) + { + LOG("localhost resolve error: %s\n", err.message()); + } + + for (auto &&resolveIte : resolver.resolve(remotehost, remoteport, asio::ip::tcp::resolver::flags::address_configured, err)) + { + m_remoteaddr = resolveIte.endpoint(); + LOG("remotehost = %s\n", *m_remoteaddr); + } + if (err) + { + LOG("remotehost resolve error: %s\n", err.message()); + } + + if (m_acceptor.is_open()) + m_acceptor.close(err); + if (m_sock_rx.is_open()) + m_sock_rx.close(err); + if (m_sock_tx.is_open()) + m_sock_tx.close(err); + m_timeout_tx.cancel(); + m_state_rx.store(0); + m_state_tx.store(0); + start_accept(); + start_connect(); + }); + } + + void stop() + { + m_ioctx.post( + [this] () + { + m_stopping = true; + std::error_code err; + if (m_acceptor.is_open()) + m_acceptor.close(err); + if (m_sock_rx.is_open()) + m_sock_rx.close(err); + if (m_sock_tx.is_open()) + m_sock_tx.close(err); + m_timeout_tx.cancel(); + m_state_rx.store(0); + m_state_tx.store(0); + m_ioctx.stop(); + }); + m_work_guard.reset(); + if (m_thread.joinable()) { + m_thread.join(); + } + } + + void check_sockets() + { + } + + bool connected() + { + return m_state_rx.load() == 2 && m_state_tx.load() == 2; + } + + unsigned receive(uint8_t *buffer, unsigned data_size) + { + if (m_state_rx.load() < 2) + return UINT_MAX; + + if (data_size > m_fifo_rx.used()) + return 0; + + return m_fifo_rx.read(&buffer[0], data_size, false); + } + + unsigned send(uint8_t *buffer, unsigned data_size) + { + if (m_state_tx.load() < 2) + return UINT_MAX; + + if (data_size > m_fifo_tx.free()) + { + LOG("TX buffer overflow\n"); + return UINT_MAX; + } + + bool const sending = m_fifo_tx.used(); + m_fifo_tx.write(&buffer[0], data_size); + if (!sending) + m_ioctx.post( + [this] () + { + start_send_tx(); + }); + return data_size; + } + +private: + class fifo + { + public: + fifo() : + m_wp(0), + m_rp(0) + { + } + + unsigned write(uint8_t* buffer, unsigned data_size) + { + unsigned current_rp = m_rp.load(std::memory_order_acquire); + unsigned current_wp = m_wp.load(std::memory_order_relaxed); + + // calculate free space + unsigned data_free = (BUFFER_SIZE + current_rp - current_wp - 1) % BUFFER_SIZE; + + // sanity checks + if (data_size > data_free) + return UINT_MAX; + if (data_size == 0) + return 0; + + unsigned data_used = 0; + + // first part (up to end) + unsigned block = std::min(data_size, BUFFER_SIZE - current_wp); + std::copy_n(&buffer[0], block, &m_buffer[current_wp]); + data_used += block; + current_wp = (current_wp + block) % BUFFER_SIZE; + + // second part (from beginning, if wrapped) + if (data_used < data_size) + { + block = data_size - data_used; + std::copy_n(&buffer[data_used], block, &m_buffer[current_wp]); + data_used += block; + current_wp += block; + } + + m_wp.store(current_wp, std::memory_order_release); + return data_used; + } + + unsigned read(uint8_t* buffer, unsigned data_size, bool peek) + { + unsigned current_wp = m_wp.load(std::memory_order_acquire); + unsigned current_rp = m_rp.load(std::memory_order_relaxed); + + // calculate available data + unsigned data_avail = (BUFFER_SIZE + current_wp - current_rp) % BUFFER_SIZE; + + // sanity checks + if (data_size > data_avail) + return UINT_MAX; + if (data_size == 0) + return 0; + + unsigned data_used = 0; + + // first part (up to end) + unsigned block = std::min(data_size, BUFFER_SIZE - current_rp); + std::copy_n(&m_buffer[current_rp], block, &buffer[0]); + data_used += block; + current_rp = (current_rp + data_used) % BUFFER_SIZE; + + // second part (from beginning, if wrapped) + if (data_used < data_size) + { + block = data_size - data_used; + std::copy_n(&m_buffer[current_rp], block, &buffer[data_used]); + data_used += block; + current_rp += block; + } + if (!peek) + { + m_rp.store(current_rp, std::memory_order_release); + } + return data_used; + } + + void consume(unsigned data_size) + { + unsigned current_wp = m_wp.load(std::memory_order_acquire); + unsigned current_rp = m_rp.load(std::memory_order_relaxed); + + // available data + unsigned data_avail = (BUFFER_SIZE + current_wp - current_rp) % BUFFER_SIZE; + + // sanity check + if (data_size > data_avail) + data_size = data_avail; + + current_rp = (current_rp + data_size) % BUFFER_SIZE; + m_rp.store(current_rp, std::memory_order_release); + } + + unsigned used() + { + unsigned current_wp = m_wp.load(std::memory_order_acquire); + unsigned current_rp = m_rp.load(std::memory_order_acquire); + return (BUFFER_SIZE + current_wp - current_rp) % BUFFER_SIZE; + } + + unsigned free() + { + unsigned current_wp = m_wp.load(std::memory_order_acquire); + unsigned current_rp = m_rp.load(std::memory_order_acquire); + return (BUFFER_SIZE + current_rp - current_wp - 1 + BUFFER_SIZE) % BUFFER_SIZE; + } + + void clear() + { + m_wp.store(0, std::memory_order_release); + m_rp.store(0, std::memory_order_release); + } + + private: + static constexpr unsigned BUFFER_SIZE = 0x80000; + std::atomic<unsigned> m_wp; + std::atomic<unsigned> m_rp; + std::array<uint8_t, BUFFER_SIZE> m_buffer; + }; + + void start_accept() + { + if (m_stopping) + return; + + std::error_code err; + m_acceptor.open(m_localaddr->protocol(), err); + m_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + if (!err) + { + m_acceptor.bind(*m_localaddr, err); + if (!err) + { + m_acceptor.listen(1, err); + if (!err) + { + osd_printf_verbose("k056230: RX listen on %s\n", *m_localaddr); + m_acceptor.async_accept( + [this] (std::error_code const &err, asio::ip::tcp::socket sock) + { + if (err) + { + LOG("RX error accepting - %d %s\n", err.value(), err.message()); + std::error_code e; + m_acceptor.close(e); + m_state_rx.store(0); + start_accept(); + } + else + { + LOG("RX connection from %s\n", sock.remote_endpoint()); + std::error_code e; + m_acceptor.close(e); + m_sock_rx = std::move(sock); + m_sock_rx.set_option(asio::socket_base::keep_alive(true)); + m_state_rx.store(2); + start_receive_rx(); + } + }); + m_state_rx.store(1); + } + } + } + if (err) + { + LOG("RX failed - %d %s\n", err.value(), err.message()); + } + } + + void start_connect() + { + if (m_stopping) + return; + + std::error_code err; + if (m_sock_tx.is_open()) + m_sock_tx.close(err); + m_sock_tx.open(m_remoteaddr->protocol(), err); + if (!err) + { + m_sock_tx.set_option(asio::ip::tcp::no_delay(true)); + m_sock_tx.set_option(asio::socket_base::keep_alive(true)); + osd_printf_verbose("k056230: TX connecting to %s\n", *m_remoteaddr); + m_timeout_tx.expires_after(std::chrono::seconds(10)); + m_timeout_tx.async_wait( + [this] (std::error_code const &err) + { + if (!err && m_state_tx.load() == 1) + { + osd_printf_verbose("k056230: TX connect timed out\n"); + std::error_code e; + m_sock_tx.close(e); + m_state_tx.store(0); + start_connect(); + } + }); + m_sock_tx.async_connect( + *m_remoteaddr, + [this] (std::error_code const &err) + { + m_timeout_tx.cancel(); + if (err) + { + osd_printf_verbose("k056230: TX connect error - %d %s\n", err.value(), err.message()); + std::error_code e; + m_sock_tx.close(e); + m_state_tx.store(0); + start_connect(); + } + else + { + LOG("TX connection established\n"); + m_state_tx.store(2); + } + }); + m_state_tx.store(1); + } + } + + void start_send_tx() + { + if (m_stopping) + return; + + unsigned used = m_fifo_tx.read(&m_buffer_tx[0], std::min<unsigned>(m_fifo_tx.used(), m_buffer_tx.size()), true); + m_sock_tx.async_write_some( + asio::buffer(&m_buffer_tx[0], used), + [this] (std::error_code const &err, std::size_t length) + { + m_fifo_tx.consume(length); + if (err) + { + LOG("TX connection error: %s\n", err.message().c_str()); + m_sock_tx.close(); + m_state_tx.store(0); + m_fifo_tx.clear(); + start_connect(); + } + else if (m_fifo_tx.used()) + { + start_send_tx(); + } + }); + } + + void start_receive_rx() + { + if (m_stopping) + return; + + m_sock_rx.async_read_some( + asio::buffer(m_buffer_rx), + [this] (std::error_code const &err, std::size_t length) + { + if (err || !length) + { + if (err) + LOG("RX connection error: %s\n", err.message()); + else + LOG("RX connection lost\n"); + m_sock_rx.close(); + m_state_rx.store(0); + m_fifo_rx.clear(); + start_accept(); + } + else + { + if (UINT_MAX == m_fifo_rx.write(&m_buffer_rx[0], length)) + { + LOG("RX buffer overflow\n"); + m_sock_rx.close(); + m_state_rx.store(0); + m_fifo_rx.clear(); + start_accept(); + return; + } + start_receive_rx(); + } + }); + } + + template <typename Format, typename... Params> + void logerror(Format &&fmt, Params &&... args) const + { + util::stream_format( + std::cerr, + "[%s] %s", + m_device.tag(), + util::string_format(std::forward<Format>(fmt), std::forward<Params>(args)...)); + } + + k056230_device &m_device; + std::thread m_thread; + asio::io_context m_ioctx; + asio::executor_work_guard<asio::io_context::executor_type> m_work_guard{m_ioctx.get_executor()}; + std::optional<asio::ip::tcp::endpoint> m_localaddr; + std::optional<asio::ip::tcp::endpoint> m_remoteaddr; + asio::ip::tcp::acceptor m_acceptor; + asio::ip::tcp::socket m_sock_rx; + asio::ip::tcp::socket m_sock_tx; + asio::steady_timer m_timeout_tx; + bool m_stopping; + std::atomic_uint m_state_rx; + std::atomic_uint m_state_tx; + fifo m_fifo_rx; + fifo m_fifo_tx; + std::array<uint8_t, 0x400> m_buffer_rx; + std::array<uint8_t, 0x400> m_buffer_tx; +}; + DEFINE_DEVICE_TYPE(K056230, k056230_device, "k056230", "K056230 LANC") DEFINE_DEVICE_TYPE(K056230_VIPER, k056230_viper_device, "k056230_viper", "Konami Viper LANC") k056230_device::k056230_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) - : device_t(mconfig, type, tag, owner, clock) - , m_ram(*this, "lanc_ram", 0x800U * 4, ENDIANNESS_BIG) - , m_irq_cb(*this) + : device_t(mconfig, type, tag, owner, clock), + m_ram(*this, "lanc_ram", 0x2000U, ENDIANNESS_BIG), + m_irq_cb(*this) { } @@ -49,76 +510,320 @@ k056230_device::k056230_device(const machine_config &mconfig, const char *tag, d void k056230_device::device_start() { + m_tick_timer = timer_alloc(FUNC(k056230_device::tick_timer_callback), this); + m_tick_timer->adjust(attotime::never); + + auto ctx = std::make_unique<context>(*this); + m_context = std::move(ctx); + m_context->start(); + + // state saving + save_item(NAME(m_irq_enable)); save_item(NAME(m_irq_state)); save_item(NAME(m_status)); + + save_item(NAME(m_linkenable)); + save_item(NAME(m_linkmaster)); + save_item(NAME(m_linkid)); + save_item(NAME(m_linkidm)); + save_item(NAME(m_linksize)); + save_item(NAME(m_txmode)); } void k056230_device::device_reset() { + m_irq_enable = false; m_irq_state = CLEAR_LINE; - m_status = 0x08; + m_status = 0; + + std::fill(std::begin(m_buffer), std::end(m_buffer), 0); + + auto const &opts = mconfig().options(); + m_context->reset(opts.comm_localhost(), opts.comm_localport(), opts.comm_remotehost(), opts.comm_remoteport()); + + m_linkenable = false; + m_linkmaster = false; + m_linkid = 0; + m_linkidm = 0; + m_linksize = 0; + m_txmode = 0; + + m_tick_timer->adjust(attotime::from_hz(800), 0, attotime::from_hz(800)); +} + + +void k056230_device::device_stop() +{ + m_tick_timer->adjust(attotime::never); + + m_context->stop(); + m_context.reset(); } void k056230_device::regs_map(address_map &map) { map(0x00, 0x00).lrw8( NAME([this] (offs_t offset) { - LOGMASKED(LOG_REG_READS, "%s: Status Register read %02x\n", machine().describe_context(), m_status); - return m_status; + /* + status register bits: + 2,1,0 = TID, transmit status + 3 = RXD + 4 = INTST-, 0 when INT + 5 = RUNST, 1 when comms active + 7,6 = unused + */ + u8 data = 0x08 | m_status; + if (!machine().side_effects_disabled()) + LOGMASKED(LOG_REG_READS, "%s: Status Register read %02x\n", machine().describe_context(), data); + return data; }), NAME([this] (offs_t offset, u8 data) { - LOGMASKED(LOG_REG_WRITES, "%s: Mode Register read %02x\n", machine().describe_context(), data); + /* + mode register bits: + 2,1,0 = ID - 0 to 7 + 4,3 = IDM - 0, 1, 3 valid, 2 invalid + 5 = M/S - 1 means MASTER + 6 = TXD + 7 = TXM - 1 means TX line will be whatever TXD is set to. (not implemented) + */ + LOGMASKED(LOG_REG_WRITES, "%s: Mode Register write %02x\n", machine().describe_context(), data); + set_mode(data); }) ), map(0x01, 0x01).lrw8( NAME([this] (offs_t offset) { + /* + crc error register bits: + each bit signals a receive error from the corrosponding main-id (bit 0 = id 0 etc.) + */ const u8 res = 0x00; - LOGMASKED(LOG_REG_READS, "%s: CRC Error Register read %02x\n", machine().describe_context(), res); + if (!machine().side_effects_disabled()) + LOGMASKED(LOG_REG_READS, "%s: CRC Error Register read %02x\n", machine().describe_context(), res); return res; }), NAME([this] (offs_t offset, u8 data) { + /* + control register bits: + 0 = START + 1 = INTPR - 1 enables int + 2 = CRCR - 1 enables crc error register + 4,3 = SIZE - 0 32byte, 1 64 byte, 2 128 byte, 3, 256 byte + 5 = SLEEP- - 0 means disabled, 1 means enabled? + 6 = H/S - comm speed - 0 = clk / 16, 1 = clk / 8 + 7 = unused + */ LOGMASKED(LOG_REG_WRITES, "%s: Control Register write %02x\n", machine().describe_context(), data); - // TODO: This is a literal translation of the previous device behaviour, and is incorrect. - // Namely it can't possibly ping irq state on the fly, needs some transaction from the receiver. - const int old_state = m_irq_state; - if (BIT(data, 5)) - { - LOGMASKED(LOG_REG_WRITES, "%s: regs_w: Asserting IRQ\n", machine().describe_context()); - m_irq_state = ASSERT_LINE; - } - if (!BIT(data, 0)) - { - LOGMASKED(LOG_REG_WRITES, "%s: regs_w: Clearing IRQ\n", machine().describe_context()); - m_irq_state = CLEAR_LINE; - } - if (old_state != m_irq_state) - { - m_irq_cb(m_irq_state); - } + set_ctrl(data); }) ); map(0x02, 0x02).lw8( NAME([this] (offs_t offset, u8 data) { + /* + sub-id register bits: + 1,0 = SID, sub-id + 3,2 = MSID, master sub-id + 7,6,5,4 = unused + */ LOGMASKED(LOG_REG_WRITES, "%s: regs_w: Sub ID Register = %02x\n", machine().describe_context(), data); }) ); } -u32 k056230_device::ram_r(offs_t offset, u32 mem_mask) +u8 k056230_device::ram_r(offs_t offset) { - const auto lanc_ram = util::big_endian_cast<const u32>(m_ram.target()); - u32 data = lanc_ram[offset & 0x7ff]; - LOGMASKED(LOG_RAM_READS, "%s: Network RAM read [%04x (%03x)]: %08x & %08x\n", machine().describe_context(), offset << 2, (offset & 0x7ff) << 2, data, mem_mask); + const auto lanc_ram = (u8*)m_ram.target(); + u8 data = lanc_ram[offset & 0x1fff]; + if (!machine().side_effects_disabled()) + LOGMASKED(LOG_RAM_READS, "%s: Network RAM read [%04x (%04x)]: %02x\n", machine().describe_context(), offset, offset & 0x1fff, data); return data; } -void k056230_device::ram_w(offs_t offset, u32 data, u32 mem_mask) +void k056230_device::ram_w(offs_t offset, u8 data) { - const auto lanc_ram = util::big_endian_cast<u32>(m_ram.target()); - LOGMASKED(LOG_RAM_WRITES, "%s: Network RAM write [%04x (%03x)] = %08x & %08x\n", machine().describe_context(), offset << 2, (offset & 0x7ff) << 2, data, mem_mask); - COMBINE_DATA(&lanc_ram[offset & 0x7ff]); + const auto lanc_ram = (u8*)m_ram.target(); + LOGMASKED(LOG_RAM_WRITES, "%s: Network RAM write [%04x (%04x)] = %02x\n", machine().describe_context(), offset, offset & 0x1fff, data); + lanc_ram[offset & 0x1fff] = data; } +void k056230_device::set_irq(int state) +{ + if (state != m_irq_state) + { + if (state == CLEAR_LINE) + m_status |= 0x10; + else + m_status &= 0xef; + + m_irq_state = state; + + if (m_irq_enable) + m_irq_cb(m_irq_state); + } +} + +void k056230_device::set_mode(u8 data) +{ + m_linkid = data & 0x07; + m_linkidm = (data & 0x18) >> 3; + if (m_linkidm == 2) + logerror("set_mode: %02x invalid IDM selected\n", data); + m_linkmaster = bool(BIT(data, 5)); + // ignore bit 6 (TXD) for now + if (data & 0x80) + logerror("set_mode: %02x manual tx/rx mode NOT implemented\n", data); +} + +void k056230_device::set_ctrl(u8 data) +{ + bool start = bool(BIT(data, 0)); + if (!bool(BIT(data, 1))) + { + set_irq(CLEAR_LINE); + } + m_irq_enable = bool(BIT(data, 1)); + // ignore bit 2 (CRCR) for now + m_linksize = (data & 0x18) >> 3; + m_linkenable = bool(BIT(data, 5)); + // ignore bit 6 (H/S) for now + // ignore bit 7 (unused) + + if (m_linkenable) + { + if (m_linkid == 0x00) + { + if (start) + { + // master (start) + m_txmode = 0x01; + m_status |= 0x20; + } + else + { + // master (stop) + m_txmode = 0x02; + m_status &= 0xdf; + } + } + else + { + // slave + m_txmode = 0x03; + m_status |= 0x20; + } + } else { + // (disabled) + m_status &= 0xdf; + m_txmode = 0x00; + } +} + +TIMER_CALLBACK_MEMBER(k056230_device::tick_timer_callback) +{ + comm_tick(); +} + +void k056230_device::comm_tick() +{ + m_context->check_sockets(); + + if (m_linkenable) + { + // if both sockets are there check ring + if (m_context->connected()) + { + unsigned frame_size = 1 << (5 + m_linksize); + unsigned data_size = frame_size + 1; + + bool raise_irq = (m_irq_state == ASSERT_LINE); + + if (m_txmode >= 0x00) + { + // try to read one message + unsigned recv = read_frame(data_size); + while (recv > 0) + { + // check if valid id + u8 idx = m_buffer[0]; + + if (idx <= 0x07) + { + // if not own message + if ((idx | m_linkidm) != (m_linkid | m_linkidm)) + { + // save message to ram + unsigned frame_start = idx * 0x0100; + + const auto lanc_ram = (u8*)m_ram.target(); + std::copy_n(&m_buffer[1], frame_size, &lanc_ram[frame_start]); + + // forward message to other nodes + send_frame(data_size); + } + + if (idx == 0) + m_txmode &= 0xfd; + } + + // try to read another message + recv = read_frame(data_size); + } + } + + if (m_txmode == 0x01) + { + // send local data to network + for (unsigned j = 0; j <= m_linkidm; j++) + { + comm_send(m_linkid + j, frame_size, data_size); + } + + // disable transmission + m_txmode = 0x02; + + raise_irq = true; + } + + // raise irq + if (raise_irq) + { + set_irq(ASSERT_LINE); + } + } + } +} + +void k056230_device::comm_send(u8 idx, unsigned frame_size, unsigned data_size) +{ + unsigned frame_start = idx * 0x100; + + m_buffer[0] = idx; + const auto lanc_ram = (u8*)m_ram.target(); + std::copy_n(&lanc_ram[frame_start], frame_size, &m_buffer[1]); + + send_frame(data_size); +} + +unsigned k056230_device::read_frame(unsigned data_size) +{ + unsigned bytes_read = m_context->receive(&m_buffer[0], data_size); + if (bytes_read == UINT_MAX) + { + // error case, do nothing + return 0; + } + return bytes_read; +} + +void k056230_device::send_frame(unsigned data_size) +{ + unsigned bytes_sent = m_context->send(&m_buffer[0], data_size); + if (bytes_sent == UINT_MAX) + { + // error case, do nothing + } +} + + /**************************************** * * konami/viper.cpp superset overrides @@ -134,7 +839,6 @@ void k056230_viper_device::device_reset() { k056230_device::device_reset(); m_control = 0; - m_irq_enable = false; m_unk[0] = m_unk[1] = 0; } @@ -155,7 +859,7 @@ void k056230_viper_device::regs_map(address_map &map) } else { - if(m_irq_enable) + if (m_irq_enable) m_irq_state = ASSERT_LINE; } @@ -171,14 +875,16 @@ void k056230_viper_device::regs_map(address_map &map) ); map(0x02, 0x02).lr8( NAME([this] (offs_t offset) { - LOGMASKED(LOG_REG_READS, "%s: status_r: %02x\n", machine().describe_context(), m_status); + if (!machine().side_effects_disabled()) + LOGMASKED(LOG_REG_READS, "%s: status_r: %02x\n", machine().describe_context(), m_status); return m_status; }) ); // TODO: unknown regs map(0x03, 0x04).lrw8( NAME([this] (offs_t offset) { - LOGMASKED(LOG_REG_READS, "%s: unk%d_r\n", machine().describe_context(), offset + 3, m_unk[offset]); + if (!machine().side_effects_disabled()) + LOGMASKED(LOG_REG_READS, "%s: unk%d_r\n", machine().describe_context(), offset + 3, m_unk[offset]); return m_unk[offset]; }), NAME([this] (offs_t offset, u8 data) { @@ -194,4 +900,3 @@ void k056230_viper_device::regs_map(address_map &map) }) ); } - diff --git a/src/devices/machine/k056230.h b/src/devices/machine/k056230.h index 35e847aae2a8f..0c1f4b35928ee 100644 --- a/src/devices/machine/k056230.h +++ b/src/devices/machine/k056230.h @@ -1,8 +1,8 @@ // license:BSD-3-Clause -// copyright-holders:Fabio Priuli +// copyright-holders:Fabio Priuli, Ariane Fugmann /*************************************************************************** - Konami 056230 LAN controller skeleton device + Konami 056230 LAN controller device ***************************************************************************/ @@ -11,32 +11,58 @@ #pragma once + class k056230_device : public device_t { public: // construction/destruction - k056230_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + k056230_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0U); auto irq_cb() { return m_irq_cb.bind(); } - u32 ram_r(offs_t offset, u32 mem_mask = ~0); - void ram_w(offs_t offset, u32 data, u32 mem_mask = ~0); + u8 ram_r(offs_t offset); + void ram_w(offs_t offset, u8 data); virtual void regs_map(address_map &map) ATTR_COLD; protected: - k056230_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + k056230_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); // device-level overrides virtual void device_start() override ATTR_COLD; + virtual void device_stop() override ATTR_COLD; virtual void device_reset() override ATTR_COLD; - memory_share_creator<u32> m_ram; + memory_share_creator<u8> m_ram; devcb_write_line m_irq_cb; - int m_irq_state = 0; - u8 m_ctrl_reg = 0; - u8 m_status = 0; + bool m_irq_enable; + int m_irq_state; + u8 m_status; + +private: + emu_timer *m_tick_timer; + + class context; + std::unique_ptr<context> m_context; + + u8 m_buffer[0x101]; + bool m_linkenable; + bool m_linkmaster; + u8 m_linkid; + u8 m_linkidm; + u8 m_linksize; + u8 m_txmode; + + TIMER_CALLBACK_MEMBER(tick_timer_callback); + + void set_irq(int state); + void set_mode(u8 data); + void set_ctrl(u8 data); + void comm_tick(); + void comm_send(u8 idx, unsigned frame_size, unsigned data_size); + unsigned read_frame(unsigned data_size); + void send_frame(unsigned data_size); }; class k056230_viper_device : public k056230_device @@ -51,9 +77,8 @@ class k056230_viper_device : public k056230_device virtual void device_reset() override ATTR_COLD; private: - u8 m_control = 0; - bool m_irq_enable = false; - u8 m_unk[2]{}; + u8 m_control; + u8 m_unk[2]; }; // device type definition diff --git a/src/mame/konami/gticlub.cpp b/src/mame/konami/gticlub.cpp index fa6db848efdac..2c5d20c722fd8 100644 --- a/src/mame/konami/gticlub.cpp +++ b/src/mame/konami/gticlub.cpp @@ -902,7 +902,7 @@ void gticlub_state::gticlub(machine_config &config) m_adc1038->set_input_callback(FUNC(gticlub_state::adc1038_input_callback)); m_adc1038->set_gti_club_hack(true); - K056230(config, m_k056230); + K056230(config, m_k056230, 0U); m_k056230->irq_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ2); // video hardware @@ -985,7 +985,7 @@ void hangplt_state::hangplt(machine_config &config) ADC1038(config, m_adc1038, 0); m_adc1038->set_input_callback(FUNC(hangplt_state::adc1038_input_callback)); - K056230(config, m_k056230); + K056230(config, m_k056230, 0U); m_k056230->irq_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ2); VOODOO_1(config, m_voodoo[0], voodoo_1_device::NOMINAL_CLOCK); @@ -1418,12 +1418,12 @@ void hangplt_state::init_hangpltu() /*************************************************************************/ -GAME( 1996, gticlub, 0, gticlub, gticlub, gticlub_state, init_gticlub, ROT0, "Konami", "GTI Club: Rally Cote D'Azur (ver EAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_LAN ) -GAME( 1996, gticlubu, gticlub, gticlub, gticlub, gticlub_state, init_gticlub, ROT0, "Konami", "GTI Club: Rally Cote D'Azur (ver UAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_LAN ) -GAME( 1996, gticluba, gticlub, gticlub, gticlub, gticlub_state, init_gticlub, ROT0, "Konami", "GTI Club: Rally Cote D'Azur (ver AAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_LAN ) -GAME( 1996, gticlubj, gticlub, gticlub, gticlub, gticlub_state, init_gticlub, ROT0, "Konami", "GTI Club: Rally Cote D'Azur (ver JAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_LAN ) -GAME( 1997, thunderh, 0, thunderh, thunderh, thunderh_state, init_gticlub, ROT0, "Konami", "Operation Thunder Hurricane (ver EAA)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_LAN ) -GAME( 1997, thunderhu, thunderh, thunderh, thunderh, thunderh_state, init_gticlub, ROT0, "Konami", "Operation Thunder Hurricane (ver UAA)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_LAN ) +GAME( 1996, gticlub, 0, gticlub, gticlub, gticlub_state, init_gticlub, ROT0, "Konami", "GTI Club: Rally Cote D'Azur (ver EAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) +GAME( 1996, gticlubu, gticlub, gticlub, gticlub, gticlub_state, init_gticlub, ROT0, "Konami", "GTI Club: Rally Cote D'Azur (ver UAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) +GAME( 1996, gticluba, gticlub, gticlub, gticlub, gticlub_state, init_gticlub, ROT0, "Konami", "GTI Club: Rally Cote D'Azur (ver AAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) +GAME( 1996, gticlubj, gticlub, gticlub, gticlub, gticlub_state, init_gticlub, ROT0, "Konami", "GTI Club: Rally Cote D'Azur (ver JAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) +GAME( 1997, thunderh, 0, thunderh, thunderh, thunderh_state, init_gticlub, ROT0, "Konami", "Operation Thunder Hurricane (ver EAA)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) +GAME( 1997, thunderhu, thunderh, thunderh, thunderh, thunderh_state, init_gticlub, ROT0, "Konami", "Operation Thunder Hurricane (ver UAA)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) GAME( 1997, slrasslt, 0, slrasslt, slrasslt, gticlub_state, init_gticlub, ROT0, "Konami", "Solar Assault (ver UAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) // Based on Revised code GAME( 1997, slrassltj, slrasslt, slrasslt, slrasslt, gticlub_state, init_gticlub, ROT0, "Konami", "Solar Assault Revised (ver JAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) GAME( 1997, slrassltj1, slrasslt, slrasslt, slrasslt, gticlub_state, init_gticlub, ROT0, "Konami", "Solar Assault (ver JAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) diff --git a/src/mame/konami/konamigx.cpp b/src/mame/konami/konamigx.cpp index bb099011451fa..b1bbc2eaa6b35 100644 --- a/src/mame/konami/konamigx.cpp +++ b/src/mame/konami/konamigx.cpp @@ -105,7 +105,6 @@ #include "cpu/z80/z80.h" #include "machine/eepromser.h" #include "sound/k054539.h" -//#include "machine/k056230.h" #include "sound/k056800.h" #include "sound/okim6295.h" #include "speaker.h" @@ -1085,8 +1084,8 @@ void konamigx_state::gx_type1_map(address_map &map) void konamigx_state::racinfrc_map(address_map &map) { gx_type1_map(map); - map(0xdc0000, 0xdc1fff).ram(); // 056230 RAM? - map(0xdd0000, 0xdd00ff).nopr().nopw(); // 056230 regs? + map(0xdc0000, 0xdc1fff).rw(m_k056230, FUNC(k056230_device::ram_r), FUNC(k056230_device::ram_w)); + map(0xdd0000, 0xdd00ff).m(m_k056230, FUNC(k056230_device::regs_map)); } void konamigx_state::gx_type2_map(address_map &map) @@ -1917,6 +1916,9 @@ void konamigx_state::racinfrc(machine_config &config) adc0834_device &adc(ADC0834(config, "adc0834", 0)); adc.set_input_callback(FUNC(konamigx_state::adc0834_callback)); + + K056230(config, m_k056230, 0U); + m_k056230->irq_cb().set_inputline(m_maincpu, M68K_IRQ_5); } void konamigx_state::gxtype3(machine_config &config) @@ -4201,8 +4203,8 @@ GAME( 1994, konamigx, 0, konamigx_bios, common, konamigx_state, init_k needs the ROZ layer to be playable --------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ -GAME( 1994, racinfrc, konamigx, racinfrc, racinfrc, konamigx_state, init_posthack, ROT0, "Konami", "Racin' Force (ver EAC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING | MACHINE_NODEVICE_LAN ) -GAME( 1994, racinfrcu, racinfrc, racinfrc, racinfrc, konamigx_state, init_posthack, ROT0, "Konami", "Racin' Force (ver UAB)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING | MACHINE_NODEVICE_LAN ) +GAME( 1994, racinfrc, konamigx, racinfrc, racinfrc, konamigx_state, init_posthack, ROT0, "Konami", "Racin' Force (ver EAC)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING ) +GAME( 1994, racinfrcu, racinfrc, racinfrc, racinfrc, konamigx_state, init_posthack, ROT0, "Konami", "Racin' Force (ver UAB)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING ) GAME( 1994, opengolf, konamigx, opengolf, opengolf, konamigx_state, init_posthack, ROT0, "Konami", "Konami's Open Golf Championship (ver EAE)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING ) GAME( 1994, opengolf2, opengolf, opengolf, opengolf, konamigx_state, init_posthack, ROT0, "Konami", "Konami's Open Golf Championship (ver EAD)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING ) diff --git a/src/mame/konami/konamigx.h b/src/mame/konami/konamigx.h index 82d9601d1a7c5..c7117636b874a 100644 --- a/src/mame/konami/konamigx.h +++ b/src/mame/konami/konamigx.h @@ -14,6 +14,7 @@ #include "cpu/tms57002/tms57002.h" #include "machine/adc083x.h" #include "machine/k053252.h" +#include "machine/k056230.h" #include "machine/timer.h" #include "sound/k054539.h" #include "sound/k056800.h" @@ -35,6 +36,7 @@ class konamigx_state : public driver_device , m_k053252(*this, "k053252") , m_k055673(*this, "k055673") , m_k055555(*this, "k055555") + , m_k056230(*this, "k056230") , m_k056832(*this, "k056832") , m_k054338(*this, "k054338") , m_k056800(*this, "k056800") @@ -212,6 +214,7 @@ class konamigx_state : public driver_device optional_device<k053252_device> m_k053252; required_device<k055673_device> m_k055673; required_device<k055555_device> m_k055555; + optional_device<k056230_device> m_k056230; required_device<k056832_device> m_k056832; optional_device<k054338_device> m_k054338; optional_device<k056800_device> m_k056800; diff --git a/src/mame/konami/plygonet.cpp b/src/mame/konami/plygonet.cpp index 8f7940e213a54..c113911b151ac 100644 --- a/src/mame/konami/plygonet.cpp +++ b/src/mame/konami/plygonet.cpp @@ -30,9 +30,6 @@ - Controls - Palettes - Driver needs: - - Network at 580800 (K056230) - Tech info by Phil Bennett, from the schematics: 68000 address map @@ -1058,7 +1055,7 @@ void polygonet_state::plygonet(machine_config &config) WATCHDOG_TIMER(config, m_watchdog); // Networking hardware - K056230(config, m_k056230); + K056230(config, m_k056230, 0U); m_k056230->irq_cb().set_inputline(m_maincpu, M68K_IRQ_3); // Video hardware @@ -1147,5 +1144,5 @@ ROM_END //------------------------------------------------- // YEAR NAME PARENT MACHINE INPUT STATE INIT -GAME( 1993, plygonet, 0, plygonet, polygonet, polygonet_state, empty_init, ROT90, "Konami", "Polygonet Commanders (ver UAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE | MACHINE_NODEVICE_LAN ) -GAME( 1993, polynetw, 0, plygonet, polynetw, polygonet_state, empty_init, ROT90, "Konami", "Poly-Net Warriors (ver JAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE | MACHINE_NODEVICE_LAN ) +GAME( 1993, plygonet, 0, plygonet, polygonet, polygonet_state, empty_init, ROT90, "Konami", "Polygonet Commanders (ver UAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE ) +GAME( 1993, polynetw, 0, plygonet, polynetw, polygonet_state, empty_init, ROT90, "Konami", "Poly-Net Warriors (ver JAA)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE ) diff --git a/src/mame/konami/viper.cpp b/src/mame/konami/viper.cpp index 1bbfaf1543372..261bb1c9695c5 100644 --- a/src/mame/konami/viper.cpp +++ b/src/mame/konami/viper.cpp @@ -2583,7 +2583,7 @@ void viper_state::viper(machine_config &config) NS16550(config, "duart_com:chan0", XTAL(19'660'800)); NS16550(config, "duart_com:chan1", XTAL(19'660'800)).out_int_callback().set(FUNC(viper_state::uart_int)); - K056230_VIPER(config, m_lanc); + K056230_VIPER(config, m_lanc, 0U); m_lanc->irq_cb().set(FUNC(viper_state::lanc_int)); VOODOO_3(config, m_voodoo, voodoo_3_device::NOMINAL_CLOCK); diff --git a/src/mame/konami/zr107.cpp b/src/mame/konami/zr107.cpp index 8d3eb6486eb94..f19fdd5c049d7 100644 --- a/src/mame/konami/zr107.cpp +++ b/src/mame/konami/zr107.cpp @@ -758,7 +758,7 @@ void zr107_state::zr107(machine_config &config) EEPROM_93C46_16BIT(config, "eeprom"); - K056230(config, m_k056230); + K056230(config, m_k056230, 0U); m_k056230->irq_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ2); WATCHDOG_TIMER(config, m_watchdog); @@ -1143,14 +1143,14 @@ ROM_END /*****************************************************************************/ -GAME( 1995, midnrun, 0, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Midnight Run: Road Fighter 2 (EAA, Euro v1.11)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1995, midnrunj, midnrun, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Midnight Run: Road Fighter 2 (JAD, Japan v1.10)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1995, midnruna, midnrun, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Midnight Run: Road Fighter 2 (AAA, Asia v1.10, older sound program)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1995, midnruna2, midnrun, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Midnight Run: Road Fighter 2 (AAA, Asia v1.10, newer sound program)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1996, windheat, 0, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Winding Heat (EAA, Euro v2.11)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1996, windheatu, windheat, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Winding Heat (UBC, USA v2.22)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1996, windheatj, windheat, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Winding Heat (JAA, Japan v2.11)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1996, windheata, windheat, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Winding Heat (AAA, Asia v2.11)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1996, jetwave, 0, jetwave, jetwave, jetwave_state, driver_init, ROT0, "Konami", "Jet Wave (EAB, Euro v1.04)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1996, waveshrk, jetwave, jetwave, jetwave, jetwave_state, driver_init, ROT0, "Konami", "Wave Shark (UAB, USA v1.04)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) -GAME( 1996, jetwavej, jetwave, jetwave, jetwave, jetwave_state, driver_init, ROT0, "Konami", "Jet Wave (JAB, Japan v1.04)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN ) +GAME( 1995, midnrun, 0, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Midnight Run: Road Fighter 2 (EAA, Euro v1.11)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1995, midnrunj, midnrun, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Midnight Run: Road Fighter 2 (JAD, Japan v1.10)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1995, midnruna, midnrun, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Midnight Run: Road Fighter 2 (AAA, Asia v1.10, older sound program)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1995, midnruna2, midnrun, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Midnight Run: Road Fighter 2 (AAA, Asia v1.10, newer sound program)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1996, windheat, 0, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Winding Heat (EAA, Euro v2.11)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1996, windheatu, windheat, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Winding Heat (UBC, USA v2.22)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1996, windheatj, windheat, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Winding Heat (JAA, Japan v2.11)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1996, windheata, windheat, midnrun, midnrun, midnrun_state, driver_init, ROT0, "Konami", "Winding Heat (AAA, Asia v2.11)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1996, jetwave, 0, jetwave, jetwave, jetwave_state, driver_init, ROT0, "Konami", "Jet Wave (EAB, Euro v1.04)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1996, waveshrk, jetwave, jetwave, jetwave, jetwave_state, driver_init, ROT0, "Konami", "Wave Shark (UAB, USA v1.04)", MACHINE_IMPERFECT_GRAPHICS ) +GAME( 1996, jetwavej, jetwave, jetwave, jetwave, jetwave_state, driver_init, ROT0, "Konami", "Jet Wave (JAB, Japan v1.04)", MACHINE_IMPERFECT_GRAPHICS )