From 320b608d7f92a48d91a19c07cd3080a610d872cd Mon Sep 17 00:00:00 2001 From: Ariane Fugmann Date: Sun, 22 Jun 2025 02:50:02 +0200 Subject: [PATCH] m2comm: ASIO rewrite (async polled) --- src/mame/sega/m2comm.cpp | 712 ++++++++++++++++++++++++++++----------- src/mame/sega/m2comm.h | 51 ++- src/mame/sega/model2.cpp | 16 +- src/mame/sega/model2.h | 2 +- 4 files changed, 554 insertions(+), 227 deletions(-) diff --git a/src/mame/sega/m2comm.cpp b/src/mame/sega/m2comm.cpp index f590e0d3edbfd..883de32479056 100644 --- a/src/mame/sega/m2comm.cpp +++ b/src/mame/sega/m2comm.cpp @@ -3,7 +3,7 @@ /* Sega MODEL2 COMMUNICATION BOARD 837-10537 -( http://images.arianchen.de/sega-comm/daytona_comm.jpg ) +( http://images.arianchen.de/sega-comm/model2-front.jpg / http://images.arianchen.de/sega-comm/model2-back.jpg ) |-----------------------------------------------------------------------------| | |-------------------| |-------------------| | | |-------------------| |-------------------| | @@ -42,7 +42,7 @@ Sega MODEL2 COMMUNICATION BOARD 837-10537 Sega PC BD MODEL2 A-CRX COMMUNICATION 837-11525 -( http://images.arianchen.de/sega-comm/srally_comm.jpg ) +( http://images.arianchen.de/sega-comm/model2a-front.jpg / http://images.arianchen.de/sega-comm/model2a-back.jpg ) |-------------------------------------------------------------------------------------------| | |-------------------| |-------------------| |---------| | | |-------------------| |-------------------| |---------| | @@ -84,7 +84,7 @@ Sega PC BD MODEL2 A-CRX COMMUNICATION 837-11525 Sega PC BD MODEL2 B-CRX COMMUNICATION 837-11615 -( http://images.arianchen.de/sega-comm/model2b-com_top.jpg ) +( http://images.arianchen.de/sega-comm/model2b-com_top.jpg / http://images.arianchen.de/sega-comm/model2b-com_bot.jpg ) |-------------------------------------------------------------------------------------------| | | | -- | @@ -127,6 +127,7 @@ Sega PC BD MODEL2 B-CRX COMMUNICATION 837-11615 Sega PC BD MODEL2 C-CRX COMMUNICATION 837-12839 +( http://images.arianchen.de/sega-comm/model2c-com_top.jpg ) |-------------------------------------------------------------------------------------------| | | | -- | @@ -165,24 +166,400 @@ Sega PC BD MODEL2 C-CRX COMMUNICATION 837-12839 */ #include "emu.h" -#include "emuopts.h" #include "m2comm.h" +#include "emuopts.h" + +#include "asio.h" + +#include + #define VERBOSE 0 #include "logmacro.h" +#ifdef M2COMM_SIMULATION +class sega_m2comm_device::context +{ +public: + context() : + m_acceptor(m_ioctx), + m_sock_rx(m_ioctx), + m_sock_tx(m_ioctx), + m_timeout_tx(m_ioctx), + m_state_rx(0U), + m_state_tx(0U) + { + } + + void start() + { + } + + void reset(std::string localhost, std::string localport, std::string remotehost, std::string remoteport) + { + 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); + + 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("M2COMM: localhost = %s\n", *m_localaddr); + } + if (err) + { + LOG("M2COMM: 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("M2COMM: remotehost = %s\n", *m_remoteaddr); + } + if (err) + { + LOG("M2COMM: remotehost resolve error: %s\n", err.message()); + } + } + + void stop() + { + 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(); + } + + void check_sockets() + { + // if async operation in progress, poll context + if ((m_state_rx > 0) || (m_state_tx > 0)) + m_ioctx.poll(); + + // start acceptor if needed + if (m_localaddr && m_state_rx.load() == 0) + { + start_accept(); + } + + // connect socket if needed + if (m_remoteaddr && m_state_tx.load() == 0) + { + start_connect(); + } + } + + 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; + + m_ioctx.poll(); + + 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("M2COMM: TX buffer overflow\n"); + return UINT_MAX; + } + + bool const sending = m_fifo_tx.used(); + m_fifo_tx.write(&buffer[0], data_size); + if (!sending) + start_send_tx(); + + m_ioctx.poll(); + + return data_size; + } + +private: + class fifo + { + public: + unsigned write(uint8_t *buffer, unsigned data_size) + { + unsigned used = 0; + if (m_wp >= m_rp) + { + used = std::min(m_buffer.size() - m_wp, data_size); + std::copy_n(&buffer[0], used, &m_buffer[m_wp]); + m_wp = (m_wp + used) % m_buffer.size(); + } + unsigned const block = std::min(data_size - used, m_rp - m_wp); + if (block) + { + std::copy_n(&buffer[used], block, &m_buffer[m_wp]); + used += block; + m_wp += block; + } + m_used += used; + return used; + } + + unsigned read(uint8_t *buffer, unsigned data_size, bool peek) + { + unsigned rp = m_rp; + unsigned used = 0; + if (rp >= m_wp) + { + used = std::min(m_buffer.size() - rp, data_size); + std::copy_n(&m_buffer[rp], used, &buffer[0]); + rp = (rp + used) % m_buffer.size(); + } + unsigned const block = std::min(data_size - used, m_wp - rp); + if (block) + { + std::copy_n(&m_buffer[rp], block, &buffer[used]); + used += block; + rp += block; + } + if (!peek) + { + m_rp = (m_rp + used) % m_buffer.size(); + m_used -= used; + } + return used; + } + + void consume(unsigned data_size) + { + m_rp = (m_rp + data_size) % m_buffer.size(); + m_used -= data_size; + } + + unsigned used() + { + return m_used; + } + + unsigned free() + { + return m_buffer.size() - m_used; + } + + void clear() + { + m_wp = m_rp = m_used = 0; + } + + + private: + unsigned m_wp = 0; + unsigned m_rp = 0; + unsigned m_used = 0; + std::array m_buffer; + }; + + void start_accept() + { + 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("M2COMM: 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("M2COMM: RX error accepting - %d %s\n", err.value(), err.message()); + std::error_code e; + m_acceptor.close(e); + m_state_rx.store(0); + } + else + { + LOG("M2COMM: 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("M2COMM: RX failed - %d %s\n", err.value(), err.message()); + } + } + + void start_connect() + { + 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("M2COMM: 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("M2COMM: TX connect timed out\n"); + std::error_code e; + m_sock_tx.close(e); + m_state_tx.store(0); + } + }); + m_sock_tx.async_connect( + *m_remoteaddr, + [this] (std::error_code const &err) + { + m_timeout_tx.cancel(); + if (err) + { + osd_printf_verbose("M2COMM: TX connect error - %d %s\n", err.value(), err.message()); + std::error_code e; + m_sock_tx.close(e); + m_state_tx.store(0); + } + else + { + LOG("M2COMM: TX connection established\n"); + m_state_tx.store(2); + } + }); + m_state_tx.store(1); + } + } + + void start_send_tx() + { + unsigned used = m_fifo_tx.read(&m_buffer_tx[0], std::min(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("M2COMM: TX connection error: %s\n", err.message().c_str()); + m_sock_tx.close(); + m_state_tx.store(0); + m_fifo_tx.clear(); + } + else if (m_fifo_tx.used()) + { + start_send_tx(); + } + }); + } + + void start_receive_rx() + { + 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("M2COMM: RX connection error: %s\n", err.message()); + else + LOG("M2COMM: RX connection lost\n"); + m_sock_rx.close(); + m_state_rx.store(0); + m_fifo_rx.clear(); + } + else + { + if (UINT_MAX == m_fifo_rx.write(&m_buffer_rx[0], length)) + { + LOG("M2COMM: RX buffer overflow\n"); + m_sock_rx.close(); + m_state_rx.store(0); + m_fifo_rx.clear(); + } + start_receive_rx(); + } + }); + } + + template + void logerror(Format &&fmt, Params &&... args) const + { + util::stream_format( + std::cerr, + "%s", + util::string_format(std::forward(fmt), std::forward(args)...)); + } + + asio::io_context m_ioctx; + std::optional m_localaddr; + std::optional 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; + std::atomic_uint m_state_rx; + std::atomic_uint m_state_tx; + fifo m_fifo_rx; + fifo m_fifo_tx; + std::array m_buffer_rx; + std::array m_buffer_tx; +}; +#endif //************************************************************************** // GLOBAL VARIABLES //************************************************************************** -DEFINE_DEVICE_TYPE(M2COMM, m2comm_device, "m2comm", "Model 2 Communication Board") +DEFINE_DEVICE_TYPE(SEGA_MODEL2_COMM, sega_m2comm_device, "m2comm", "Sega Model2 Communication Board") //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- -void m2comm_device::device_add_mconfig(machine_config &config) +void sega_m2comm_device::device_add_mconfig(machine_config &config) { } @@ -191,82 +568,96 @@ void m2comm_device::device_add_mconfig(machine_config &config) //************************************************************************** //------------------------------------------------- -// m2comm_device - constructor +// sega_m2comm_device - constructor //------------------------------------------------- -m2comm_device::m2comm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - device_t(mconfig, M2COMM, tag, owner, clock) +sega_m2comm_device::sega_m2comm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, SEGA_MODEL2_COMM, tag, owner, clock) { - // prepare localhost "filename" - m_localhost[0] = 0; - strcat(m_localhost, "socket."); - strcat(m_localhost, mconfig.options().comm_localhost()); - strcat(m_localhost, ":"); - strcat(m_localhost, mconfig.options().comm_localport()); - - // prepare remotehost "filename" - m_remotehost[0] = 0; - strcat(m_remotehost, "socket."); - strcat(m_remotehost, mconfig.options().comm_remotehost()); - strcat(m_remotehost, ":"); - strcat(m_remotehost, mconfig.options().comm_remoteport()); - - m_framesync = mconfig.options().comm_framesync() ? 0x01 : 0x00; - m_frameoffset = 0x1c0; // default +#ifdef M2COMM_SIMULATION + m_framesync = mconfig.options().comm_framesync() ? 0x01 : 0x00; +#endif } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- -void m2comm_device::device_start() +void sega_m2comm_device::device_start() { +#ifdef M2COMM_SIMULATION + auto ctx = std::make_unique(); + m_context = std::move(ctx); + m_context->start(); +#endif } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- -void m2comm_device::device_reset() +void sega_m2comm_device::device_reset() { + std::fill(std::begin(m_shared), std::end(m_shared), 0); m_zfg = 0; m_cn = 0; m_fg = 0; +#ifdef M2COMM_SIMULATION + 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 = 0; + m_linktimer = 0; + m_linkalive = 0; + m_linkid = 0; + m_linkcount = 0; + m_zfg_delay = 0; +#endif } -uint8_t m2comm_device::zfg_r(offs_t offset) +void sega_m2comm_device::device_stop() +{ +#ifdef M2COMM_SIMULATION + m_context->stop(); + m_context.reset(); +#endif +} + +uint8_t sega_m2comm_device::zfg_r(offs_t offset) { uint8_t result = m_zfg | (~m_fg << 7) | 0x7e; LOG("m2comm-zfg_r: read register %02x for value %02x\n", offset, result); return result; } -void m2comm_device::zfg_w(uint8_t data) +void sega_m2comm_device::zfg_w(uint8_t data) { LOG("m2comm-zfg_w: %02x\n", data); m_zfg = data & 0x01; } -uint8_t m2comm_device::share_r(offs_t offset) +uint8_t sega_m2comm_device::share_r(offs_t offset) { uint8_t result = m_shared[offset]; LOG("m2comm-share_r: read shared memory %02x for value %02x\n", offset, result); return result; } -void m2comm_device::share_w(offs_t offset, uint8_t data) +void sega_m2comm_device::share_w(offs_t offset, uint8_t data) { LOG("m2comm-share_w: %02x %02x\n", offset, data); m_shared[offset] = data; } -uint8_t m2comm_device::cn_r() +uint8_t sega_m2comm_device::cn_r() { return m_cn | 0xfe; } -void m2comm_device::cn_w(uint8_t data) +void sega_m2comm_device::cn_w(uint8_t data) { m_cn = data & 0x01; @@ -277,7 +668,7 @@ void m2comm_device::cn_w(uint8_t data) if (!m_cn) { // reset command - osd_printf_verbose("M2COMM: board disabled\n"); + LOG("M2COMM: board disabled\n"); m_linkenable = 0x00; m_zfg = 0; m_cn = 0; @@ -286,7 +677,7 @@ void m2comm_device::cn_w(uint8_t data) else { // init command - osd_printf_verbose("M2COMM: board enabled\n"); + LOG("M2COMM: board enabled\n"); m_linkenable = 0x01; m_linkid = 0x00; m_linkalive = 0x00; @@ -294,7 +685,7 @@ void m2comm_device::cn_w(uint8_t data) m_linktimer = 0x00e8; // 58 fps * 4s // zero memory - for (int i = 0; i < 0x4000; i++) + for (unsigned i = 0; i < 0x4000; i++) { m_shared[i] = 0x00; } @@ -303,11 +694,11 @@ void m2comm_device::cn_w(uint8_t data) // TODO - check EPR-16726 on Daytona USA and Sega Rally Championship // EPR-18643(A) - these are accessed by VirtuaON and Sega Touring Car Championship - // frameSize - 0x0e00. is it = frameoffset*8 ? if true - it should be 0xC00 for Power Sled + // frame_size - 0x0e00. is it = frameoffset*8 ? if true - it should be 0xc00 for Power Sled m_shared[0x12] = 0x00; m_shared[0x13] = 0x0e; - // frameOffset - 0x01c0 in most games or 0x180 in Power Sled + // frame_offset - 0x01c0 in most games or 0x180 in Power Sled m_shared[0x14] = m_frameoffset & 0xff; m_shared[0x15] = m_frameoffset >> 8; @@ -316,7 +707,7 @@ void m2comm_device::cn_w(uint8_t data) #endif } -uint8_t m2comm_device::fg_r() +uint8_t sega_m2comm_device::fg_r() { #ifdef M2COMM_SIMULATION read_fg(); @@ -324,12 +715,12 @@ uint8_t m2comm_device::fg_r() return m_fg | (~m_zfg << 7) | 0x7e; } -void m2comm_device::fg_w(uint8_t data) +void sega_m2comm_device::fg_w(uint8_t data) { m_fg = data & 0x01; } -void m2comm_device::check_vint_irq() +void sega_m2comm_device::check_vint_irq() { #ifndef M2COMM_SIMULATION #else @@ -338,23 +729,20 @@ void m2comm_device::check_vint_irq() } #ifdef M2COMM_SIMULATION -void m2comm_device::comm_tick() +void sega_m2comm_device::comm_tick() { + m_context->check_sockets(); + if (m_linkenable == 0x01) { - int frameStart = 0x2000; - int frameSize = m_shared[0x13] << 8 | m_shared[0x12]; - int frameOffset = frameStart | m_shared[0x15] << 8 | m_shared[0x14]; - - int dataSize = frameSize + 1; - int recv = 0; - int idx = 0; + unsigned frame_size = m_shared[0x13] << 8 | m_shared[0x12]; + unsigned data_size = frame_size + 1; // EPR-16726 uses m_fg for Master/Slave // EPR-18643(A) seems to check m_shared[1], with a fallback to m_fg - bool isMaster = (m_fg == 0x01 || m_shared[1] == 0x01); - bool isSlave = (m_fg == 0x00 && m_shared[1] == 0x02); - bool isRelay = (m_fg == 0x00 && m_shared[1] == 0x00); + bool is_master = (m_fg == 0x01 || m_shared[1] == 0x01); + bool is_slave = (m_fg == 0x00 && m_shared[1] == 0x02); + bool is_relay = (m_fg == 0x00 && m_shared[1] == 0x00); if (m_linkalive == 0x02) { @@ -369,82 +757,66 @@ void m2comm_device::comm_tick() m_shared[2] = 0xff; m_shared[3] = 0xff; - // check rx socket - if (!m_line_rx) - { - osd_printf_verbose("M2COMM: listen on %s\n", m_localhost); - uint64_t filesize; // unused - osd_file::open(m_localhost, OPEN_FLAG_CREATE, m_line_rx, filesize); - } - - // check tx socket - if (!m_line_tx) - { - osd_printf_verbose("M2COMM: connect to %s\n", m_remotehost); - uint64_t filesize; // unused - osd_file::open(m_remotehost, 0, m_line_tx, filesize); - } - // if both sockets are there check ring - if (m_line_rx && m_line_tx) + if (m_context->connected()) { m_zfg ^= 0x01; // try to read one message - recv = read_frame(dataSize); + unsigned recv = read_frame(data_size); while (recv > 0) { // check if message id - idx = m_buffer0[0]; + uint8_t idx = m_buffer[0]; // 0xFF - link id if (idx == 0xff) { - if (isMaster) + if (is_master) { // master gets first id and starts next state m_linkid = 0x01; - m_linkcount = m_buffer0[1]; + m_linkcount = m_buffer[1]; m_linktimer = 0x00; } else { // slave get own id, relay does nothing - if (isSlave) + if (is_slave) { - m_buffer0[1]++; + m_buffer[1]++; } // forward message to other nodes - send_frame(dataSize); + send_frame(data_size); } } // 0xFE - link size else if (idx == 0xfe) { - if (isSlave) + if (is_slave) { // fetch linkid and linkcount, then decrease linkid - m_linkid = m_buffer0[1]; - m_linkcount = m_buffer0[2]; - m_buffer0[1]--; + m_linkid = m_buffer[1]; + m_linkcount = m_buffer[2]; + m_buffer[1]--; // forward message to other nodes - send_frame(dataSize); + send_frame(data_size); } - else if (isRelay) + else if (is_relay) { // fetch linkid and linkcount, then decrease linkid m_linkid = 0x00; - m_linkcount = m_buffer0[2]; + m_linkcount = m_buffer[2]; // forward message to other nodes - send_frame(dataSize); + send_frame(data_size); } // consider it done - osd_printf_verbose("M2COMM: link established - id %02x of %02x\n", m_linkid, m_linkcount); + LOG("M2COMM: link established - id %02x of %02x\n", m_linkid, m_linkcount); m_linkalive = 0x01; // write to shared mem @@ -454,33 +826,33 @@ void m2comm_device::comm_tick() } if (m_linkalive == 0x00) - recv = read_frame(dataSize); + recv = read_frame(data_size); else recv = 0; } // if we are master and link is not yet established - if (isMaster && (m_linkalive == 0x00)) + if (is_master && (m_linkalive == 0x00)) { // send first packet if (m_linktimer == 0x01) { - m_buffer0[0] = 0xff; - m_buffer0[1] = 0x01; - m_buffer0[2] = 0x00; - send_frame(dataSize); + m_buffer[0] = 0xff; + m_buffer[1] = 0x01; + m_buffer[2] = 0x00; + send_frame(data_size); } // send second packet else if (m_linktimer == 0x00) { - m_buffer0[0] = 0xfe; - m_buffer0[1] = m_linkcount; - m_buffer0[2] = m_linkcount; - send_frame(dataSize); + m_buffer[0] = 0xfe; + m_buffer[1] = m_linkcount; + m_buffer[2] = m_linkcount; + send_frame(data_size); // consider it done - osd_printf_verbose("M2COMM: link established - id %02x of %02x\n", m_linkid, m_linkcount); + LOG("M2COMM: link established - id %02x of %02x\n", m_linkid, m_linkcount); m_linkalive = 0x01; // write to shared mem @@ -501,26 +873,27 @@ void m2comm_device::comm_tick() // if link established if (m_linkalive == 0x01) { + unsigned frame_start = 0x2000; + unsigned frame_offset = frame_start | m_shared[0x15] << 8 | m_shared[0x14]; + do { // try to read a message - recv = read_frame(dataSize); + unsigned recv = read_frame(data_size); while (recv > 0) { // check if valid id - idx = m_buffer0[0]; + uint8_t idx = m_buffer[0]; if (idx >= 0 && idx <= m_linkcount) { // save message to "ring buffer" - for (int j = 0x00 ; j < frameSize ; j++) - { - m_shared[frameOffset + j] = m_buffer0[1 + j]; - } + std::copy_n(&m_buffer[1], frame_size, &m_shared[frame_offset]); + m_zfg ^= 0x01; - if (isSlave) - send_data(m_linkid, frameStart, frameSize, dataSize); - else if (isRelay) - send_frame(dataSize); + if (is_slave) + send_data(m_linkid, frame_start, frame_size, data_size); + else if (is_relay) + send_frame(data_size); } else { @@ -529,14 +902,14 @@ void m2comm_device::comm_tick() // 0xFC - VSYNC m_linktimer = 0x00; - if (!isMaster) + if (!is_master) // forward message to other nodes - send_frame(dataSize); + send_frame(data_size); } } // try to read another message - recv = read_frame(dataSize); + recv = read_frame(data_size); } } while (m_linktimer == 0x01); @@ -544,15 +917,15 @@ void m2comm_device::comm_tick() // enable wait for vsync m_linktimer = m_framesync; - if (isMaster) + if (is_master) { // update "ring buffer" if link established - send_data(m_linkid, frameStart, frameSize, dataSize); + send_data(m_linkid, frame_start, frame_size, data_size); // send vsync - m_buffer0[0] = 0xfc; - m_buffer0[1] = 0x01; - send_frame(dataSize); + m_buffer[0] = 0xfc; + m_buffer[1] = 0x01; + send_frame(data_size); } m_zfg_delay = 0x02; @@ -560,7 +933,7 @@ void m2comm_device::comm_tick() } } -void m2comm_device::read_fg() +void sega_m2comm_device::read_fg() { if (m_zfg_delay > 0x00) { @@ -569,40 +942,38 @@ void m2comm_device::read_fg() } if (m_linkalive == 0x01) { - int frameStart = 0x2000; - int frameSize = m_shared[0x13] << 8 | m_shared[0x12]; - int frameOffset = frameStart | m_shared[0x15] << 8 | m_shared[0x14]; + unsigned frame_start = 0x2000; + unsigned frame_offset = frame_start | m_shared[0x15] << 8 | m_shared[0x14]; - int dataSize = frameSize + 1; - int recv = 0; - int idx = 0; + unsigned frame_size = m_shared[0x13] << 8 | m_shared[0x12]; + unsigned data_size = frame_size + 1; // EPR-16726 uses m_fg for Master/Slave // EPR-18643(A) seems to check m_shared[1], with a fallback to m_fg - bool isMaster = (m_fg == 0x01 || m_shared[1] == 0x01); - bool isSlave = (m_fg == 0x00 && m_shared[1] == 0x02); - bool isRelay = (m_fg == 0x00 && m_shared[1] == 0x00); + bool is_master = (m_fg == 0x01 || m_shared[1] == 0x01); + bool is_slave = (m_fg == 0x00 && m_shared[1] == 0x02); + bool is_relay = (m_fg == 0x00 && m_shared[1] == 0x00); do { // try to read a message - recv = read_frame(dataSize); + unsigned recv = read_frame(data_size); while (recv > 0) { // check if valid id - idx = m_buffer0[0]; + uint8_t idx = m_buffer[0]; if (idx >= 0 && idx <= m_linkcount) { // save message to "ring buffer" - for (int j = 0x00 ; j < frameSize ; j++) + for (unsigned j = 0x00; j < frame_size; j++) { - m_shared[frameOffset + j] = m_buffer0[1 + j]; + m_shared[frame_offset + j] = m_buffer[1 + j]; } m_zfg ^= 0x01; - if (isSlave) - send_data(m_linkid, frameStart, frameSize, dataSize); - else if (isRelay) - send_frame(dataSize); + if (is_slave) + send_data(m_linkid, frame_start, frame_size, data_size); + else if (is_relay) + send_frame(data_size); } else { @@ -611,94 +982,53 @@ void m2comm_device::read_fg() // 0xFC - VSYNC m_linktimer = 0x00; - if (!isMaster) + if (!is_master) // forward message to other nodes - send_frame(dataSize); + send_frame(data_size); } } // try to read another message - recv = read_frame(dataSize); + recv = read_frame(data_size); } } while (m_linktimer == 0x01); } } -int m2comm_device::read_frame(int dataSize) +unsigned sega_m2comm_device::read_frame(unsigned data_size) { - if (!m_line_rx) - return 0; - - // try to read a message - std::uint32_t recv = 0; - std::error_condition filerr = m_line_rx->read(m_buffer0, 0, dataSize, recv); - if (recv > 0) - { - // check if message complete - if (recv != dataSize) - { - // only part of a message - read on - std::uint32_t togo = dataSize - recv; - int offset = recv; - while (togo > 0) - { - filerr = m_line_rx->read(m_buffer1, 0, togo, recv); - if (recv > 0) - { - for (int i = 0 ; i < recv ; i++) - { - m_buffer0[offset + i] = m_buffer1[i]; - } - togo -= recv; - offset += recv; - } - else if (!filerr && recv == 0) - { - togo = 0; - } - } - } - } - else if (!filerr && recv == 0) + unsigned bytes_read = m_context->receive(&m_buffer[0], data_size); + if (bytes_read == UINT_MAX) { if (m_linkalive == 0x01) { - osd_printf_verbose("M2COMM: rx connection lost\n"); + LOG("M2COMM: link lost\n"); m_linkalive = 0x02; m_linktimer = 0x00; - m_line_rx.reset(); } + return 0; } - return recv; + return bytes_read; } -void m2comm_device::send_data(uint8_t frameType, int frameStart, int frameSize, int dataSize) +void sega_m2comm_device::send_data(uint8_t frame_type, unsigned frame_start, unsigned frame_size, unsigned data_size) { - m_buffer0[0] = frameType; - for (int i = 0x0000 ; i < frameSize ; i++) - { - m_buffer0[1 + i] = m_shared[frameStart + i]; - } - send_frame(dataSize); + m_buffer[0] = frame_type; + std::copy_n(&m_shared[frame_start], frame_size, &m_buffer[1]); + send_frame(data_size); } -void m2comm_device::send_frame(int dataSize){ - if (!m_line_tx) - return; - - std::error_condition filerr; - std::uint32_t written; - - filerr = m_line_tx->write(&m_buffer0, 0, dataSize, written); - if (filerr) +void sega_m2comm_device::send_frame(unsigned data_size) +{ + unsigned bytes_sent = m_context->send(&m_buffer[0], data_size); + if (bytes_sent == UINT_MAX) { if (m_linkalive == 0x01) { - osd_printf_verbose("M2COMM: tx connection lost\n"); + LOG("M2COMM: link lost\n"); m_linkalive = 0x02; m_linktimer = 0x00; - m_line_tx.reset(); } } } diff --git a/src/mame/sega/m2comm.h b/src/mame/sega/m2comm.h index 3fe67883ec92a..846265829262d 100644 --- a/src/mame/sega/m2comm.h +++ b/src/mame/sega/m2comm.h @@ -7,17 +7,15 @@ #define M2COMM_SIMULATION -#include "osdfile.h" //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -class m2comm_device : public device_t +class sega_m2comm_device : public device_t { public: - // construction/destruction - m2comm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + sega_m2comm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); // single bit registers (74LS74) uint8_t zfg_r(offs_t offset); @@ -45,43 +43,42 @@ class m2comm_device : public device_t void set_frameoffset(uint16_t offset) { m_frameoffset = offset; } protected: - // device-level overrides virtual void device_start() override ATTR_COLD; + virtual void device_stop() override ATTR_COLD; virtual void device_reset() override ATTR_COLD; virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; private: - uint8_t m_shared[0x4000]{}; // 16k shared memory - uint8_t m_zfg = 0; // z80 flip gate - bit 0 switches memory banks, bit7 is connected to FG bit 0 - uint8_t m_cn = 0; // bit0 is used to enable/disable the comm board - uint8_t m_fg = 0; // i960 flip gate - bit0 is stored, bit7 is connected to ZFG bit 0 - - osd_file::ptr m_line_rx; // rx line - can be either differential, simple serial or toslink - osd_file::ptr m_line_tx; // tx line - is differential, simple serial and toslink - char m_localhost[256]{}; - char m_remotehost[256]{}; - uint8_t m_buffer0[0x1000]{}; - uint8_t m_buffer1[0x1000]{}; - uint8_t m_framesync; + uint8_t m_shared[0x4000]; // 16k shared memory + uint8_t m_zfg; // z80 flip gate - bit 0 switches memory banks, bit7 is connected to FG bit 0 + uint8_t m_cn; // bit0 is used to enable/disable the comm board + uint8_t m_fg; // i960 flip gate - bit0 is stored, bit7 is connected to ZFG bit 0 + uint16_t m_frameoffset; #ifdef M2COMM_SIMULATION - uint8_t m_linkenable = 0; - uint16_t m_linktimer = 0; - uint8_t m_linkalive = 0; - uint8_t m_linkid = 0; - uint8_t m_linkcount = 0; - uint8_t m_zfg_delay = 0; + class context; + std::unique_ptr m_context; + + uint8_t m_buffer[0x1000]; + uint8_t m_framesync; + + uint8_t m_linkenable; + uint16_t m_linktimer; + uint8_t m_linkalive; + uint8_t m_linkid; + uint8_t m_linkcount; + uint8_t m_zfg_delay; void comm_tick(); void read_fg(); - int read_frame(int dataSize); - void send_data(uint8_t frameType, int frameStart, int frameSize, int dataSize); - void send_frame(int dataSize); + unsigned read_frame(unsigned data_size); + void send_data(uint8_t frame_type, unsigned frame_start, unsigned frame_size, unsigned data_size); + void send_frame(unsigned data_size); #endif }; // device type definition -DECLARE_DEVICE_TYPE(M2COMM, m2comm_device) +DECLARE_DEVICE_TYPE(SEGA_MODEL2_COMM, sega_m2comm_device) #endif // MAME_SEGA_M2COMM_H diff --git a/src/mame/sega/model2.cpp b/src/mame/sega/model2.cpp index 65071a6c85c93..ae9a256488cd9 100644 --- a/src/mame/sega/model2.cpp +++ b/src/mame/sega/model2.cpp @@ -1109,9 +1109,9 @@ void model2_state::model2_base_mem(address_map &map) map(0x01800000, 0x01803fff).rw(FUNC(model2_state::palette_r), FUNC(model2_state::palette_w)).flags(i960_cpu_device::BURST); map(0x01810000, 0x0181bfff).rw(FUNC(model2_state::colorxlat_r), FUNC(model2_state::colorxlat_w)).flags(i960_cpu_device::BURST); map(0x0181c000, 0x0181c003).w(FUNC(model2_state::model2_3d_zclip_w)); - map(0x01a00000, 0x01a03fff).rw(m_m2comm, FUNC(m2comm_device::share_r), FUNC(m2comm_device::share_w)).mirror(0x10000).flags(i960_cpu_device::BURST); // Power Sled access comm.board at 0x01A0XXXX, not sure if really a mirror, or slightly different comm.device - map(0x01a04000, 0x01a04000).rw(m_m2comm, FUNC(m2comm_device::cn_r), FUNC(m2comm_device::cn_w)).mirror(0x10000); - map(0x01a04002, 0x01a04002).rw(m_m2comm, FUNC(m2comm_device::fg_r), FUNC(m2comm_device::fg_w)).mirror(0x10000); + map(0x01a00000, 0x01a03fff).rw(m_m2comm, FUNC(sega_m2comm_device::share_r), FUNC(sega_m2comm_device::share_w)).mirror(0x10000).flags(i960_cpu_device::BURST); // Power Sled access comm.board at 0x01A0XXXX, not sure if really a mirror, or slightly different comm.device + map(0x01a04000, 0x01a04000).rw(m_m2comm, FUNC(sega_m2comm_device::cn_r), FUNC(sega_m2comm_device::cn_w)).mirror(0x10000); + map(0x01a04002, 0x01a04002).rw(m_m2comm, FUNC(sega_m2comm_device::fg_r), FUNC(sega_m2comm_device::fg_w)).mirror(0x10000); map(0x01d00000, 0x01d03fff).ram().share("backup1").flags(i960_cpu_device::BURST); // Backup sram map(0x02000000, 0x03ffffff).rom().region("main_data", 0).flags(i960_cpu_device::BURST); @@ -2613,7 +2613,7 @@ void model2o_state::model2o(machine_config &config) uart_clock.signal_handler().set(m_uart, FUNC(i8251_device::write_txc)); uart_clock.signal_handler().append(m_uart, FUNC(i8251_device::write_rxc)); - M2COMM(config, "m2comm", 0); + SEGA_MODEL2_COMM(config, m_m2comm, 0U); } u8 model2_state::driveio_portg_r() @@ -2753,7 +2753,7 @@ void model2a_state::model2a(machine_config &config) model2_screen(config); model2_scsp(config); - M2COMM(config, "m2comm", 0); + SEGA_MODEL2_COMM(config, m_m2comm, 0U); SEGA_BILLBOARD(config, m_billboard, 0); @@ -2894,7 +2894,7 @@ void model2b_state::model2b(machine_config &config) model2_screen(config); model2_scsp(config); - M2COMM(config, "m2comm", 0); + SEGA_MODEL2_COMM(config, m_m2comm, 0U); SEGA_BILLBOARD(config, m_billboard, 0); @@ -2953,7 +2953,7 @@ void model2b_state::powsled(machine_config &config) io.an_port_callback<7>().set_ioport("P2_L"); // 0 and 2 is Motion AD - subdevice("m2comm")->set_frameoffset(0x180); + m_m2comm->set_frameoffset(0x180); } @@ -3043,7 +3043,7 @@ void model2c_state::model2c(machine_config &config) model2_screen(config); model2_scsp(config); - M2COMM(config, "m2comm", 0); + SEGA_MODEL2_COMM(config, m_m2comm, 0U); } void model2c_state::skisuprg(machine_config &config) diff --git a/src/mame/sega/model2.h b/src/mame/sega/model2.h index 680417e3ff121..71e21f1bb4b22 100644 --- a/src/mame/sega/model2.h +++ b/src/mame/sega/model2.h @@ -119,7 +119,7 @@ class model2_state : public driver_device optional_device m_dsb2; // 68k-based MPEG Digital Sound Board optional_device m_m1audio; // Model 1 standard sound board required_device m_uart; - optional_device m_m2comm; // Model 2 communication board + optional_device m_m2comm; // Model 2 communication board optional_device m_audiocpu; required_device m_copro_fifo_in; required_device m_copro_fifo_out;