From 74f7016d52f2f7c8475a6974ecf8868afae3ecd2 Mon Sep 17 00:00:00 2001 From: Ariane Fugmann Date: Sun, 22 Jun 2025 05:08:15 +0200 Subject: [PATCH] xbdcomm: first implementation (ASIO, polled) --- src/mame/sega/segaxbd.cpp | 73 ++- src/mame/sega/segaxbd.h | 4 +- src/mame/sega/xbdcomm.cpp | 962 ++++++++++++++++++++++++++++++++++++++ src/mame/sega/xbdcomm.h | 85 ++++ 4 files changed, 1075 insertions(+), 49 deletions(-) create mode 100644 src/mame/sega/xbdcomm.cpp create mode 100644 src/mame/sega/xbdcomm.h diff --git a/src/mame/sega/segaxbd.cpp b/src/mame/sega/segaxbd.cpp index 9695dab762fca..e279571b84130 100644 --- a/src/mame/sega/segaxbd.cpp +++ b/src/mame/sega/segaxbd.cpp @@ -298,6 +298,7 @@ segaxbd_state::segaxbd_state(const machine_config &mconfig, device_type type, co , m_segaic16vid(*this, "segaic16vid") , m_segaic16road(*this, "segaic16road") , m_subram0(*this, "subram0") + , m_xbdcomm(*this, "xbdcomm") , m_road_priority(1) , m_scanline_timer(nullptr) , m_timer_irq_state(0) @@ -570,7 +571,7 @@ void segaxbd_state::loffire_sync0_w(offs_t offset, uint16_t data, uint16_t mem_m uint16_t segaxbd_state::smgp_excs_r(offs_t offset) { //logerror("%06X:smgp_excs_r(%04X)\n", m_maincpu->pc(), offset*2); - return 0xffff; + return 0xff00 | m_xbdcomm->ex_r(offset % 0x20); } @@ -582,6 +583,7 @@ uint16_t segaxbd_state::smgp_excs_r(offs_t offset) void segaxbd_state::smgp_excs_w(offs_t offset, uint16_t data, uint16_t mem_mask) { //logerror("%06X:smgp_excs_w(%04X) = %04X & %04X\n", m_maincpu->pc(), offset*2, data, mem_mask); + m_xbdcomm->ex_w(offset % 0x20, data & 0xff); } @@ -1042,27 +1044,6 @@ void segaxbd_state::smgp_airdrive_portmap(address_map &map) -//************************************************************************** -// SUPER MONACO GP LINK BOARD CPU ADDRESS MAPS -//************************************************************************** - -// Link Board, not yet emulated -void segaxbd_state::smgp_comm_map(address_map &map) -{ - map.unmap_value_high(); - map(0x0000, 0x1fff).rom(); - map(0x2000, 0x3fff).ram(); - map(0x4000, 0x47ff).ram(); // MB8421 Dual-Port SRAM -} - -void segaxbd_state::smgp_comm_portmap(address_map &map) -{ - map.unmap_value_high(); - map.global_mask(0xff); -} - - - //************************************************************************** // RASCOT LINK CPU ADDRESS MAP //************************************************************************** @@ -1858,7 +1839,7 @@ void segaxbd_lastsurv_fd1094_state::device_add_mconfig(machine_config &config) m_maincpu->reset_cb().set(FUNC(segaxbd_lastsurv_fd1094_state::m68k_reset_callback)); // basic machine hardware - // TODO: network board + SEGA_XBOARD_COMM(config, m_xbdcomm, 0U); m_iochip[0]->out_portd_cb().set(FUNC(segaxbd_state::lastsurv_muxer_w)); m_iochip[1]->in_portb_cb().set(FUNC(segaxbd_state::lastsurv_port_r)); @@ -1887,7 +1868,7 @@ void segaxbd_lastsurv_state::device_add_mconfig(machine_config &config) segaxbd_state::xboard_base_mconfig(config); // basic machine hardware - // TODO: network board + SEGA_XBOARD_COMM(config, m_xbdcomm, 0U); m_iochip[0]->out_portd_cb().set(FUNC(segaxbd_state::lastsurv_muxer_w)); m_iochip[1]->in_portb_cb().set(FUNC(segaxbd_state::lastsurv_port_r)); @@ -1925,9 +1906,7 @@ void segaxbd_smgp_fd1094_state::device_add_mconfig(machine_config &config) m_soundcpu2->set_addrmap(AS_PROGRAM, &segaxbd_smgp_fd1094_state::smgp_sound2_map); m_soundcpu2->set_addrmap(AS_IO, &segaxbd_smgp_fd1094_state::smgp_sound2_portmap); - z80_device &commcpu(Z80(config, "commcpu", XTAL(16'000'000)/2)); // Z80E - commcpu.set_addrmap(AS_PROGRAM, &segaxbd_smgp_fd1094_state::smgp_comm_map); - commcpu.set_addrmap(AS_IO, &segaxbd_smgp_fd1094_state::smgp_comm_portmap); + SEGA_XBOARD_COMM(config, m_xbdcomm, 0U); z80_device &motorcpu(Z80(config, "motorcpu", XTAL(16'000'000)/2)); // not verified motorcpu.set_addrmap(AS_PROGRAM, &segaxbd_smgp_fd1094_state::smgp_airdrive_map); @@ -1969,9 +1948,7 @@ void segaxbd_smgp_state::device_add_mconfig(machine_config &config) m_soundcpu2->set_addrmap(AS_PROGRAM, &segaxbd_smgp_state::smgp_sound2_map); m_soundcpu2->set_addrmap(AS_IO, &segaxbd_smgp_state::smgp_sound2_portmap); - z80_device &commcpu(Z80(config, "commcpu", XTAL(16'000'000)/2)); // Z80E - commcpu.set_addrmap(AS_PROGRAM, &segaxbd_smgp_state::smgp_comm_map); - commcpu.set_addrmap(AS_IO, &segaxbd_smgp_state::smgp_comm_portmap); + SEGA_XBOARD_COMM(config, m_xbdcomm, 0U); z80_device &motorcpu(Z80(config, "motorcpu", XTAL(16'000'000)/2)); // not verified motorcpu.set_addrmap(AS_PROGRAM, &segaxbd_smgp_state::smgp_airdrive_map); @@ -4706,6 +4683,8 @@ void segaxbd_state::install_smgp(void) // map /EXCS space m_maincpu->space(AS_PROGRAM).install_read_handler(0x2f0000, 0x2f3fff, read16sm_delegate(*this, FUNC(segaxbd_state::smgp_excs_r))); m_maincpu->space(AS_PROGRAM).install_write_handler(0x2f0000, 0x2f3fff, write16s_delegate(*this, FUNC(segaxbd_state::smgp_excs_w))); + m_subcpu->space(AS_PROGRAM).install_read_handler(0x0f0000, 0x0f3fff, read16sm_delegate(*this, FUNC(segaxbd_state::smgp_excs_r))); + m_subcpu->space(AS_PROGRAM).install_write_handler(0x0f0000, 0x0f3fff, write16s_delegate(*this, FUNC(segaxbd_state::smgp_excs_w))); } void segaxbd_new_state::init_smgp() @@ -4760,7 +4739,7 @@ GAME( 1987, aburner2g, aburner2, sega_aburner2, aburner2, segaxbd_new_stat GAME( 1987, thndrbld, 0, sega_xboard_fd1094, thndrbld, segaxbd_new_state, empty_init, ROT0, "Sega", "Thunder Blade (upright) (FD1094 317-0056)", 0 ) GAME( 1987, thndrbld1, thndrbld, sega_xboard, thndrbd1, segaxbd_new_state, empty_init, ROT0, "Sega", "Thunder Blade (deluxe/standing) (unprotected)", 0 ) -GAME( 1989, lastsurv, 0, sega_lastsurv_fd1094,lastsurv, segaxbd_new_state, empty_init, ROT0, "Sega", "Last Survivor (Japan) (FD1094 317-0083)", MACHINE_NODEVICE_LAN ) +GAME( 1989, lastsurv, 0, sega_lastsurv_fd1094,lastsurv, segaxbd_new_state, init_smgp, ROT0, "Sega", "Last Survivor (Japan) (FD1094 317-0083)", 0 ) GAME( 1989, loffire, 0, sega_xboard_fd1094, loffire, segaxbd_new_state, init_loffire, ROT0, "Sega", "Line of Fire / Bakudan Yarou (World) (FD1094 317-0136)", 0 ) GAME( 1989, loffireu, loffire, sega_xboard_fd1094, loffire, segaxbd_new_state, init_loffire, ROT0, "Sega", "Line of Fire / Bakudan Yarou (US) (FD1094 317-0135)", 0 ) @@ -4768,14 +4747,14 @@ GAME( 1989, loffirej, loffire, sega_xboard_fd1094, loffire, segaxbd_new_stat GAME( 1989, rachero, 0, sega_xboard_fd1094, rachero, segaxbd_new_state, empty_init, ROT0, "Sega", "Racing Hero (FD1094 317-0144)", 0 ) -GAME( 1989, smgp, 0, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (World, Rev B) (FD1094 317-0126a)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgp6, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (World, Rev A) (FD1094 317-0126a)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgp5, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (World) (FD1094 317-0126)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgpu, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (US, Rev C) (FD1094 317-0125a)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgpu1, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (US, Rev B) (FD1094 317-0125a)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgpu2, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (US, Rev A) (FD1094 317-0125a)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgpj, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (Japan, Rev B) (FD1094 317-0124a)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgpja, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (Japan, Rev A) (FD1094 317-0124a)", MACHINE_NODEVICE_LAN ) +GAME( 1989, smgp, 0, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (World, Rev B) (FD1094 317-0126a)", 0 ) +GAME( 1989, smgp6, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (World, Rev A) (FD1094 317-0126a)", 0 ) +GAME( 1989, smgp5, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (World) (FD1094 317-0126)", 0 ) +GAME( 1989, smgpu, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (US, Rev C) (FD1094 317-0125a)", 0 ) +GAME( 1989, smgpu1, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (US, Rev B) (FD1094 317-0125a)", 0 ) +GAME( 1989, smgpu2, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (US, Rev A) (FD1094 317-0125a)", 0 ) +GAME( 1989, smgpj, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (Japan, Rev B) (FD1094 317-0124a)", 0 ) +GAME( 1989, smgpja, smgp, sega_smgp_fd1094, smgp, segaxbd_new_state, init_smgp, ROT0, "Sega", "Super Monaco GP (Japan, Rev A) (FD1094 317-0124a)", 0 ) GAME( 1990, abcop, 0, sega_xboard_fd1094, abcop, segaxbd_new_state, empty_init, ROT0, "Sega", "A.B. Cop (World) (FD1094 317-0169b)", 0 ) GAME( 1990, abcopj, abcop, sega_xboard_fd1094, abcop, segaxbd_new_state, empty_init, ROT0, "Sega", "A.B. Cop (Japan) (FD1094 317-0169b)", 0 ) @@ -4798,15 +4777,15 @@ GAME( 1987, thndrbldd, thndrbld, sega_xboard, thndrbld, segaxbd_new_state, empt GAME( 1989, racherod, rachero, sega_xboard, rachero, segaxbd_new_state, empty_init, ROT0, "bootleg", "Racing Hero (bootleg of FD1094 317-0144 set)", 0 ) -GAME( 1989, smgpd, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (World, Rev B) (bootleg of FD1094 317-0126a set)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgp6d, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (World, Rev A) (bootleg of FD1094 317-0126a set)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgp5d, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (World) (bootleg of FD1094 317-0126 set)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgpud, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (US, Rev C) (bootleg of FD1094 317-0125a set)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgpu1d, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (US, Rev B) (bootleg of FD1094 317-0125a set)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgpu2d, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (US, Rev A) (bootleg of FD1094 317-0125a set)", MACHINE_NODEVICE_LAN ) -GAME( 1989, smgpjd, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (Japan, Rev B) (bootleg of FD1094 317-0124a set)", MACHINE_NODEVICE_LAN ) +GAME( 1989, smgpd, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (World, Rev B) (bootleg of FD1094 317-0126a set)", 0 ) +GAME( 1989, smgp6d, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (World, Rev A) (bootleg of FD1094 317-0126a set)", 0 ) +GAME( 1989, smgp5d, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (World) (bootleg of FD1094 317-0126 set)", 0 ) +GAME( 1989, smgpud, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (US, Rev C) (bootleg of FD1094 317-0125a set)", 0 ) +GAME( 1989, smgpu1d, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (US, Rev B) (bootleg of FD1094 317-0125a set)", 0 ) +GAME( 1989, smgpu2d, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (US, Rev A) (bootleg of FD1094 317-0125a set)", 0 ) +GAME( 1989, smgpjd, smgp, sega_smgp, smgp, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Super Monaco GP (Japan, Rev B) (bootleg of FD1094 317-0124a set)", 0 ) -GAME( 1989, lastsurvd, lastsurv, sega_lastsurv,lastsurv, segaxbd_new_state, empty_init, ROT0, "bootleg", "Last Survivor (Japan) (bootleg of FD1094 317-0083 set)", MACHINE_NODEVICE_LAN ) +GAME( 1989, lastsurvd, lastsurv, sega_lastsurv,lastsurv, segaxbd_new_state, init_smgp, ROT0, "bootleg", "Last Survivor (Japan) (bootleg of FD1094 317-0083 set)", 0 ) GAME( 1990, abcopd, abcop, sega_xboard, abcop, segaxbd_new_state, empty_init, ROT0, "bootleg", "A.B. Cop (World) (bootleg of FD1094 317-0169b set)", 0 ) GAME( 1990, abcopjd, abcop, sega_xboard, abcop, segaxbd_new_state, empty_init, ROT0, "bootleg", "A.B. Cop (Japan) (bootleg of FD1094 317-0169b set)", 0 ) diff --git a/src/mame/sega/segaxbd.h b/src/mame/sega/segaxbd.h index 52dd5e0eb8cea..62ee1a8d9087f 100644 --- a/src/mame/sega/segaxbd.h +++ b/src/mame/sega/segaxbd.h @@ -13,6 +13,7 @@ #include "segaic16.h" #include "segaic16_road.h" #include "sega16sp.h" +#include "xbdcomm.h" #include "cpu/m68000/m68000.h" #include "cpu/mcs51/mcs51.h" @@ -73,8 +74,6 @@ class segaxbd_state : public device_t void main_map(address_map &map) ATTR_COLD; void smgp_airdrive_map(address_map &map) ATTR_COLD; void smgp_airdrive_portmap(address_map &map) ATTR_COLD; - void smgp_comm_map(address_map &map) ATTR_COLD; - void smgp_comm_portmap(address_map &map) ATTR_COLD; void smgp_sound2_map(address_map &map) ATTR_COLD; void smgp_sound2_portmap(address_map &map) ATTR_COLD; void sound_map(address_map &map) ATTR_COLD; @@ -112,6 +111,7 @@ class segaxbd_state : public device_t required_device m_segaic16vid; required_device m_segaic16road; required_shared_ptr m_subram0; + optional_device m_xbdcomm; // configuration bool m_adc_reverse[8]{}; diff --git a/src/mame/sega/xbdcomm.cpp b/src/mame/sega/xbdcomm.cpp new file mode 100644 index 0000000000000..34704715d4926 --- /dev/null +++ b/src/mame/sega/xbdcomm.cpp @@ -0,0 +1,962 @@ +// license:BSD-3-Clause +// copyright-holders:Ariane Fugmann + +/* +Top : 834-6780 +Sticker: 834-7112 +|---------| |--| |----------------------| +| RX TX 315-5336 | +| 315-5337 | +| | +| 16MHz 6264 | +| EPR-12587.14 | +| MB89372P-SH Z80E MB8421 | +|---------------------------------------| +Notes: + 315-5337 - PAL16L8 + 315-5336 - PAL16L8 + Z80 clock: 8.000MHz [16/2] + 6264 : 8k x8 SRAM + MB8421 : Fujitsu 2k x8 Dual-Port SRAM (SDIP52) + MB89372 : Fujitsu Multi-Protocol Controller (SDIP64) + EPR-12587: 27C256 EPROM +*/ + +#include "emu.h" +#include "xbdcomm.h" + +#include "asio.h" + +#include + +#include "emuopts.h" + +#define VERBOSE 0 +#include "logmacro.h" + +#define Z80_TAG "commcpu" + +#ifdef XBDCOMM_SIMULATION +class sega_xbdcomm_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("XBDCOMM: localhost = %s\n", *m_localaddr); + } + if (err) + { + LOG("XBDCOMM: 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("XBDCOMM: remotehost = %s\n", *m_remoteaddr); + } + if (err) + { + LOG("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: 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("XBDCOMM: RX connection error: %s\n", err.message()); + else + LOG("XBDCOMM: 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("XBDCOMM: 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 + +/************************************* + * XBDCOMM Memory Map + *************************************/ +void sega_xbdcomm_device::xbdcomm_mem(address_map &map) +{ + map(0x0000, 0x1fff).rom(); + map(0x2000, 0x3fff).ram(); + map(0x4000, 0x47ff).rw(m_dpram, FUNC(mb8421_device::left_r), FUNC(mb8421_device::left_w)); +} + +/************************************* + * XBDCOMM I/O Map + *************************************/ +void sega_xbdcomm_device::xbdcomm_io(address_map &map) +{ + map.global_mask(0xff); + //map(0x00, 0x3f).rw(m_mpc, FUNC(mb89372_device::read), FUNC(mb89372_device::write)); + map(0x40, 0x40).rw(FUNC(sega_xbdcomm_device::z80_stat_r), FUNC(sega_xbdcomm_device::z80_debug_w)); + map(0x80, 0x80).w(FUNC(sega_xbdcomm_device::z80_stat_w)); +} + +ROM_START( xbdcomm ) + ROM_REGION( 0x10000, Z80_TAG, ROMREGION_ERASEFF ) + ROM_DEFAULT_BIOS("epr12587") + + // found on Super Monaco GP + ROM_SYSTEM_BIOS( 0, "epr12587", "EPR-12587" ) + ROMX_LOAD( "epr-12587.14", 0x00000, 0x08000, CRC(2afe648b) SHA1(b5bf86f3acbcc23c136185110acecf2c971294fa), ROM_BIOS(0) ) +ROM_END + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +DEFINE_DEVICE_TYPE(SEGA_XBOARD_COMM, sega_xbdcomm_device, "xbdcomm", "Sega X-Board Communication Board") + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +void sega_xbdcomm_device::device_add_mconfig(machine_config &config) +{ + Z80(config, m_cpu, 16_MHz_XTAL / 2); + m_cpu->set_memory_map(&sega_xbdcomm_device::xbdcomm_mem); + m_cpu->set_io_map(&sega_xbdcomm_device::xbdcomm_io); + + MB8421(config, m_dpram).intl_callback().set(FUNC(sega_xbdcomm_device::dpram_int5_w)); + + /* + MB89372(config, m_mpc, 16_MHz_XTAL / 2); + m_mpc->out_hreq_callback().set(FUNC(sega_xbdcomm_device::mpc_hreq_w)); + m_mpc->out_irq_callback().set(FUNC(sega_xbdcomm_device::mpc_int7_w)); + m_mpc->in_memr_callback().set(FUNC(sega_xbdcomm_device::mpc_mem_r)); + m_mpc->out_memw_callback().set(FUNC(sega_xbdcomm_device::mpc_mem_w)); + */ +} + +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- +const tiny_rom_entry *sega_xbdcomm_device::device_rom_region() const +{ + return ROM_NAME( xbdcomm ); +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sega_xbdcomm_device - constructor +//------------------------------------------------- + +sega_xbdcomm_device::sega_xbdcomm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, SEGA_XBOARD_COMM, tag, owner, clock), + m_cpu(*this, Z80_TAG), + m_dpram(*this, "dpram") + //m_mpc(*this, "commmpc") +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sega_xbdcomm_device::device_start() +{ + // state saving + save_item(NAME(m_ex_page)); + save_item(NAME(m_xbd_stat)); + save_item(NAME(m_z80_stat)); + +#ifdef XBDCOMM_SIMULATION + auto ctx = std::make_unique(); + m_context = std::move(ctx); + m_context->start(); + + save_item(NAME(m_linkenable)); + save_item(NAME(m_linktimer)); + save_item(NAME(m_linkalive)); + save_item(NAME(m_linkid)); + save_item(NAME(m_linkcount)); +#endif +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void sega_xbdcomm_device::device_reset() +{ + m_ex_page = 0; + m_xbd_stat = 0; + m_z80_stat = 0; + +#ifdef XBDCOMM_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; +#endif +} + +void sega_xbdcomm_device::device_stop() +{ +#ifdef XBDCOMM_SIMULATION + m_context->stop(); + m_context.reset(); +#endif +} + +void sega_xbdcomm_device::device_reset_after_children() +{ + m_cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + //m_mpc->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); +} + +uint8_t sega_xbdcomm_device::ex_r(offs_t offset) +{ + switch (offset) + { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + return m_dpram->right_r(m_ex_page << 3 | offset); + + case 0x08: + // page latch + return m_ex_page; + + case 0x10: + // status register? + if (!machine().side_effects_disabled()) + LOG("xbdcomm-ex_r: %02x %02x\n", offset, m_z80_stat); +#ifdef XBDCOMM_SIMULATION + comm_tick(); +#endif + return m_z80_stat; + + default: + if (!machine().side_effects_disabled()) + logerror("xbdcomm-ex_r: %02x\n", offset); + return 0xff; + } +} + + +void sega_xbdcomm_device::ex_w(offs_t offset, uint8_t data) +{ + switch (offset) + { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + m_dpram->right_w(m_ex_page << 3 | offset, data); + break; + + case 0x08: + // page latch + m_ex_page = data; + break; + + case 0x10: + // status register? + // bit 7 = on/off toggle + // bit 1 = test flag? + // bit 0 = ready to send? + m_xbd_stat = data; +#ifndef XBDCOMM_SIMULATION + if (m_xbd_stat & 0x80) + { + m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); + //m_mpc->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); + } + else + { + device_reset(); + device_reset_after_children(); + } +#else + if (m_xbd_stat & 0x80) + { + // link active + if (!m_linkenable) + { + // init command + osd_printf_verbose("XBDCOMM: board enabled\n"); + m_linkenable = 0x01; + m_linkid = 0x00; + m_linkalive = 0x00; + m_linkcount = 0x00; + m_linktimer = 0x003a; + } + } + else + { + if (m_linkenable) + { + // reset command + osd_printf_verbose("XBDCOMM: board disabled\n"); + m_linkenable = 0x00; + } + } + comm_tick(); +#endif + break; + + case 0x19: + case 0x1d: + // unknown registers + // 19 - 11 byte writes (smgp) + // 1d - completes write cycle (smgp) + break; + + default: + logerror("xbdcomm-ex_w: %02x %02x\n", offset, data); + break; + } +} + +void sega_xbdcomm_device::mpc_hreq_w(int state) +{ + m_cpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE); + //m_mpc->hack_w(state); +} + +void sega_xbdcomm_device::dpram_int5_w(int state) +{ + m_cpu->set_input_line_and_vector(0, state ? ASSERT_LINE : CLEAR_LINE, 0xef); // Z80 INT5 +} + +void sega_xbdcomm_device::mpc_int7_w(int state) +{ + logerror("mpc_int7_w: %02x\n", state); + m_cpu->set_input_line_and_vector(0, state ? ASSERT_LINE : CLEAR_LINE, 0xff); // Z80 INT7 +} + +uint8_t sega_xbdcomm_device::mpc_mem_r(offs_t offset) +{ + return m_cpu->space(AS_PROGRAM).read_byte(offset); +} + +void sega_xbdcomm_device::mpc_mem_w(offs_t offset, uint8_t data) +{ + m_cpu->space(AS_PROGRAM).write_byte(offset, data); +} + +uint8_t sega_xbdcomm_device::z80_stat_r() +{ + return m_xbd_stat; +} + +void sega_xbdcomm_device::z80_stat_w(uint8_t data) +{ + m_z80_stat = data; +} + +void sega_xbdcomm_device::z80_debug_w(uint8_t data) +{ + m_ex_page = data; + m_z80_stat = 0; +} + + +#ifdef XBDCOMM_SIMULATION +void sega_xbdcomm_device::comm_tick() +{ + m_context->check_sockets(); + + if (m_linkenable == 0x01) + { + uint8_t cab_index = mpc_mem_r(0x4000); + uint8_t cab_count = mpc_mem_r(0x4001); + + unsigned frame_start_tx = 0x4010; + unsigned frame_start_rx = 0x4310; + unsigned frame_size = 0; + switch (cab_count) + { + case 1: + frame_size = 0x300; + break; + case 2: + frame_size = 0x180; + break; + case 3: + frame_size = 0x110; + break; + case 4: + frame_size = 0x0c0; + break; + case 5: + case 6: + frame_size = 0x080; + break; + case 7: + case 8: + frame_size = 0x060; + break; + default: + frame_size = 0x000; + break; + } + + unsigned data_size = frame_size + 1; + + bool is_master = (cab_index == 0x01); + bool is_slave = (cab_index > 0x01); + bool is_relay = (cab_index == 0x00); + + if (m_linkalive == 0x02) + { + // link failed... (guesswork) + m_z80_stat = 0xff; + return; + } + else if (m_linkalive == 0x00) + { + // link not yet established... (guesswork) + m_z80_stat = 0x01; + + // if both sockets are there check ring + if (m_context->connected()) + { + // try to read one message + int recv = read_frame(data_size); + while (recv > 0) + { + // check message id + uint8_t idx = m_buffer[0]; + + // 0xff - link id + if (idx == 0xff) + { + if (is_master) + { + // master gets first id and starts next state + m_linkid = 0x01; + m_linkcount = m_buffer[1]; + m_linktimer = 0x00; + } + else + { + // slave get own id, relay does nothing + if (is_slave) + { + m_buffer[1]++; + m_linkid = m_buffer[1]; + } + + // forward message to other nodes + send_frame(data_size); + } + } + + // 0xfe - link size + else if (idx == 0xfe) + { + if (is_slave || is_relay) + { + m_linkcount = m_buffer[1]; + + // forward message to other nodes + send_frame(data_size); + } + + // consider it done + osd_printf_verbose("XBDCOMM: link established - id %02x of %02x\n", m_linkid, m_linkcount); + m_linkalive = 0x01; + + // write to shared mem + m_z80_stat = 0x00; + } + + if (m_linkalive == 0x00) + recv = read_frame(data_size); + else + recv = 0; + } + + // if we are master and link is not yet established + if (is_master && (m_linkalive == 0x00)) + { + // send first packet + if (m_linktimer == 0x01) + { + m_buffer[0] = 0xff; + m_buffer[1] = 0x01; + send_frame(data_size); + } + + // send second packet + else if (m_linktimer == 0x00) + { + m_buffer[0] = 0xfe; + m_buffer[1] = m_linkcount; + send_frame(data_size); + + // consider it done + osd_printf_verbose("XBDCOMM: link established - id %02x of %02x\n", m_linkid, m_linkcount); + m_linkalive = 0x01; + + // write to shared mem + m_z80_stat = 0x00; + } + + else if (m_linktimer > 0x01) + { + // decrease delay timer + m_linktimer--; + } + } + } + } + + if (m_linkalive == 0x01) + { + // link established + // try to read a message + unsigned recv = read_frame(data_size); + while (recv > 0) + { + // check if valid id + uint8_t idx = m_buffer[0]; + if (idx > 0 && idx <= m_linkcount) + { + // save message to "ring buffer" + unsigned frame_offset = frame_start_tx + ((idx - 1) * frame_size); + for (unsigned j = 0x00; j < frame_size; j++) + { + mpc_mem_w(frame_offset + j, m_buffer[1 + j]); + } + + // if not own message + if (idx != cab_index) + { + // forward message to other nodes + send_frame(data_size); + } + else + { + m_z80_stat = 0x00; + } + } + + // try to read another message + recv = read_frame(data_size); + } + + // update buffers... guesswork + for (unsigned j = 0x00; j < 0x300; j++) + { + mpc_mem_w(frame_start_rx + j, mpc_mem_r(frame_start_tx + j)); + } + + // update "ring buffer" if link established + // live relay does not send data + if (cab_index != 0x00) + { + // check ready-to-send flag + if (m_xbd_stat & 0x01) + { + unsigned frame_offset = frame_start_tx + ((cab_index - 1) * frame_size); + send_data(cab_index, frame_offset, frame_size, data_size); + m_z80_stat = 0x01; + } + } + + // clear ready-to-send flag + m_xbd_stat &= 0xfe; + } + } +} + +unsigned sega_xbdcomm_device::read_frame(unsigned data_size) +{ + unsigned bytes_read = m_context->receive(&m_buffer[0], data_size); + if (bytes_read == UINT_MAX) + { + if (m_linkalive == 0x01) + { + LOG("XBDCOMM: link lost\n"); + m_linkalive = 0x02; + m_linktimer = 0x00; + m_z80_stat = 0xff; + } + return 0; + } + return bytes_read; +} + +void sega_xbdcomm_device::send_data(uint8_t frame_type, unsigned frame_offset, unsigned frame_size, unsigned data_size) +{ + m_buffer[0] = frame_type; + for (unsigned i = 0x00; i < frame_size; i++) + { + m_buffer[1 + i] = mpc_mem_r(frame_offset + i); + } + send_frame(data_size); +} + +void sega_xbdcomm_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) + { + LOG("XBDCOMM: link lost\n"); + m_linkalive = 0x02; + m_linktimer = 0x00; + m_z80_stat = 0xff; + } + } +} +#endif diff --git a/src/mame/sega/xbdcomm.h b/src/mame/sega/xbdcomm.h new file mode 100644 index 0000000000000..df41df05f21a6 --- /dev/null +++ b/src/mame/sega/xbdcomm.h @@ -0,0 +1,85 @@ +// license:BSD-3-Clause +// copyright-holders:Ariane Fugmann +#ifndef MAME_SEGA_XBDCOMM_H +#define MAME_SEGA_XBDCOMM_H + +#pragma once + +#define XBDCOMM_SIMULATION + +#include "cpu/z80/z80.h" +#include "machine/mb8421.h" +//#include "machine/mb89372.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class sega_xbdcomm_device : public device_t +{ +public: + sega_xbdcomm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // ex-bus connection to host + uint8_t ex_r(offs_t offset); + void ex_w(offs_t offset, uint8_t data); + +protected: + virtual void device_start() override ATTR_COLD; + virtual void device_stop() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_reset_after_children() override; + + virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD; + + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + +private: + required_device m_cpu; + required_device m_dpram; + //required_device m_mpc; + + uint8_t m_ex_page; // 74LS374 probably + uint8_t m_xbd_stat; // not sure about those yet - 7474 for top bit? and 74161 for lower 4 bits + uint8_t m_z80_stat; // not sure about those yet - 74LS374 + + void xbdcomm_mem(address_map &map) ATTR_COLD; + void xbdcomm_io(address_map &map) ATTR_COLD; + + // MB8421 + void dpram_int5_w(int state); + + // MB89372 + void mpc_hreq_w(int state); + void mpc_int7_w(int state); + uint8_t mpc_mem_r(offs_t offset); + void mpc_mem_w(offs_t offset, uint8_t data); + + uint8_t z80_stat_r(); + void z80_stat_w(uint8_t data); + void z80_debug_w(uint8_t data); + +#ifdef XBDCOMM_SIMULATION + class context; + std::unique_ptr m_context; + + uint8_t m_buffer[0x400]; + + uint8_t m_linkenable; + uint16_t m_linktimer; + uint8_t m_linkalive; + uint8_t m_linkid; + uint8_t m_linkcount; + + void comm_tick(); + unsigned read_frame(unsigned data_size); + void send_data(uint8_t frame_type, unsigned frame_offset, unsigned frame_size, unsigned data_size); + void send_frame(unsigned data_size); +#endif +}; + +// device type definition +DECLARE_DEVICE_TYPE(SEGA_XBOARD_COMM, sega_xbdcomm_device) + +#endif // MAME_MACHINE_XBDCOMM_H