diff --git a/src/mame/namco/namco_c139.cpp b/src/mame/namco/namco_c139.cpp index 882b1fb3bc323..c0b1e652ae9cb 100644 --- a/src/mame/namco/namco_c139.cpp +++ b/src/mame/namco/namco_c139.cpp @@ -1,25 +1,543 @@ // license:BSD-3-Clause -// copyright-holders:Angelo Salese +// copyright-holders:Angelo Salese, John Bennett, Ariane Fugmann /*************************************************************************** Namco C139 - Serial I/F Controller + (from assault schematics, page 5-18 and 5-19) + connected to M5M5179P RAM with a 13-bit address bus, and 9 bit data bus + connected to host cpu with a 14*-bit address bus, and 13 bit data bus + 2 clock inputs - 16M and 12M + currently there are 4 known modes of operation: + + mode 0x08: + - ridgera2 + - raverace + + mode 0x09: + - fourtrax + - suzuka8h + - suzuk8h2 + - winrungp + - winrun91 + - driveyes (center) + - cybsled + - cybrcomm + - acedrive + - victlap + - cybrcycc + - adillor + + mode 0x0c: + - ridgeracf + + mode 0x0d: + - finallap + - finallap2 + - finallap3 + - driveyes (sides) + - tokyowar + - aircomb + - dirtdash + - alpiner2b (uses 0xfd) + + mode 0x0f (configuration mode) + - 0x02 used to setup byte/word adressing + + NOTES: + apparently mode 0x09 and 0x0d modify the received data. + mode 0x09 does not update *anything* after data got changed. might be automatic. + mode 0x0d updates the tx offset pointing to the rx buffer (which is not supported right now) TODO: - - Make this to actually work! - - Is RAM shared with a specific CPU other than master/slave? - - is this another MCU with internal ROM? + - hook a real chip and test in detail + - mode 0x0d shows 1 machine in service mode and attract mode, however most games seem to work "okay" in multiplayer. + - C422 seems to be a pin compatible upgrade to C139, probably supporting higher clock speeds? hook up c139 in system23 + - mode 0x0b is used by s23 games to test interrupts. ***************************************************************************/ #include "emu.h" #include "namco_c139.h" +#include "emuopts.h" + +#include "asio.h" + +#include + +#define VERBOSE 0 +#include "logmacro.h" + + +class namco_c139_device::context +{ +public: + context(namco_c139_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("C139: network thread started\n"); + try { + m_ioctx.run(); + } catch (const std::exception& e) { + LOG("C139: Exception in network thread: %s\n", e.what()); + } catch (...) { // Catch any other unknown exceptions + LOG("C139: Unknown exception in network thread\n"); + } + LOG("C139: network thread completed\n"); + }); + } + + void reset(std::string localhost, std::string localport, std::string remotehost, std::string 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("C139: localhost = %s\n", *m_localaddr); + } + if (err) + { + LOG("C139: 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("C139: remotehost = %s\n", *m_remoteaddr); + } + if (err) + { + LOG("C139: remotehost resolve error: %s\n", err.message()); + } + + m_ioctx.post( + [this] () + { + 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); + 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("C139: 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 m_wp; + std::atomic m_rp; + std::array 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("C139: 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("C139: 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("C139: 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("C139: 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("C139: 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("C139: 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("C139: 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("C139: 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(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("C139: 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("C139: RX connection error: %s\n", err.message()); + else + LOG("C139: 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("C139: RX buffer overflow\n"); + m_sock_rx.close(); + m_state_rx.store(0); + m_fifo_rx.clear(); + start_accept(); + return; + } + start_receive_rx(); + } + }); + } + + template + void logerror(Format &&fmt, Params &&... args) const + { + util::stream_format( + std::cerr, + "[%s] %s", + m_device.tag(), + util::string_format(std::forward(fmt), std::forward(args)...)); + } + + namco_c139_device &m_device; + std::thread m_thread; + asio::io_context m_ioctx; + asio::executor_work_guard m_work_guard{m_ioctx.get_executor()}; + 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; + 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 m_buffer_rx; + std::array m_buffer_tx; +}; //************************************************************************** // GLOBAL VARIABLES //************************************************************************** +#define REG_0_STATUS 0 +#define REG_1_MODE 1 +#define REG_2_CONTROL 2 +#define REG_3_START 3 +#define REG_4_RXSIZE 4 +#define REG_5_TXSIZE 5 +#define REG_6_RXOFFSET 6 +#define REG_7_TXOFFSET 7 // device type definition DEFINE_DEVICE_TYPE(NAMCO_C139, namco_c139_device, "namco_c139", "Namco C139 Serial") @@ -31,30 +549,42 @@ DEFINE_DEVICE_TYPE(NAMCO_C139, namco_c139_device, "namco_c139", "Namco C139 Seri void namco_c139_device::data_map(address_map &map) { - map(0x0000, 0x3fff).ram().share("sharedram"); + map(0x0000, 0x3fff).rw(FUNC(namco_c139_device::ram_r),FUNC(namco_c139_device::ram_w)); } void namco_c139_device::regs_map(address_map &map) { - map(0x00, 0x01).r(FUNC(namco_c139_device::status_r)); // WRITE clears flags - map(0x02, 0x03).noprw(); // settings? -// map(0x0a, 0x0b) // WRITE tx_w -// map(0x0c, 0x0d) // READ rx_r -// map(0x0e, 0x0f) // + map(0x00, 0x0f).rw(FUNC(namco_c139_device::reg_r), FUNC(namco_c139_device::reg_w)); } + //------------------------------------------------- // namco_c139_device - constructor //------------------------------------------------- namco_c139_device::namco_c139_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, NAMCO_C139, tag, owner, clock), - device_memory_interface(mconfig, *this), - m_space_config("data", ENDIANNESS_BIG, 16, 14, 0, address_map_constructor(FUNC(namco_c139_device::data_map), this)) + m_irq_cb(*this) { -} + auto const &opts = mconfig.options(); + m_localhost = opts.comm_localhost(); + m_localport = opts.comm_localport(); + m_remotehost = opts.comm_remotehost(); + m_remoteport = opts.comm_remoteport(); + // come up with some magic number for identification + std::string remotehost = util::string_format("%s:%s", m_remotehost, m_remoteport); + m_linkid = 0; + for (int x = 0; x < sizeof(remotehost) && remotehost[x] != 0; x++) + { + m_linkid ^= remotehost[x]; + } + + LOG("C139: ID byte = %02d\n", m_linkid); + + std::fill(std::begin(m_buffer), std::end(m_buffer), 0); +} //------------------------------------------------- @@ -63,8 +593,24 @@ namco_c139_device::namco_c139_device(const machine_config &mconfig, const char * void namco_c139_device::device_start() { - m_ram = (uint16_t*)memshare("sharedram")->ptr(); + m_tick_timer = timer_alloc(FUNC(namco_c139_device::tick_timer_callback), this); + m_tick_timer->adjust(attotime::never); + + auto ctx = std::make_unique(*this); + m_context = std::move(ctx); + m_context->start(); + + // state saving + save_item(NAME(m_ram)); + save_item(NAME(m_reg)); + + save_item(NAME(m_linktimer)); + save_item(NAME(m_linkid)); + + save_item(NAME(m_txsize)); + save_item(NAME(m_txblock)); + save_item(NAME(m_reg_f3)); } @@ -74,20 +620,29 @@ void namco_c139_device::device_start() void namco_c139_device::device_reset() { -} + std::fill(std::begin(m_ram), std::end(m_ram), 0); + std::fill(std::begin(m_reg), std::end(m_reg), 0); -//------------------------------------------------- -// memory_space_config - return a description of -// any address spaces owned by this device -//------------------------------------------------- + m_context->reset(m_localhost, m_localport, m_remotehost, m_remoteport); + + m_linktimer = 0x0200; + + m_txsize = 0x00; + m_txblock = 0x00; + m_reg_f3 = 0x00; + + m_tick_timer->adjust(attotime::from_hz(800),0,attotime::from_hz(800)); +} -device_memory_interface::space_config_vector namco_c139_device::memory_space_config() const +void namco_c139_device::device_stop() { - return space_config_vector { - std::make_pair(AS_DATA, &m_space_config) - }; + m_tick_timer->adjust(attotime::never); + + m_context->stop(); + m_context.reset(); } + //************************************************************************** // READ/WRITE HANDLERS //************************************************************************** @@ -100,13 +655,342 @@ uint16_t namco_c139_device::ram_r(offs_t offset) void namco_c139_device::ram_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&m_ram[offset]); + m_txsize = offset; +} + +uint16_t namco_c139_device::reg_r(offs_t offset) +{ + uint16_t result = m_reg[offset]; + if (!machine().side_effects_disabled()) + LOG("C139: reg_r[%02x] = %04x\n", offset, result); + return result; } -uint16_t namco_c139_device::status_r() +void namco_c139_device::reg_w(offs_t offset, uint16_t data, uint16_t mem_mask) { - /* - x-- RX READY or irq pending? - -x- IRQ direction: 1 RX cause - 0 TX cause - */ - return 4; // STATUS bit? + if (!machine().side_effects_disabled()) + LOG("C139: reg_w[%02x] = %04x\n", offset, data); + m_reg[offset] = data; + + // status reset / irq ack? + if (offset == REG_0_STATUS && data == 0) + m_reg[offset] = 4; + + // hack to get alpinr2b working + if (offset == REG_1_MODE && data >= 0x000f) + m_reg[REG_1_MODE] &= 0x000f; + + // possibly setup/config mode? + if (m_reg[REG_1_MODE] == 0x0f && offset == REG_3_START) + { + m_reg_f3 = data; + logerror("C139: m_reg_f3 = %02x\n", m_reg_f3); + } + + // mode 09 tx trigger + if (offset == REG_1_MODE && data == 0x09 && m_txsize > 0) + m_txblock = 0x00; + + // mode 08 & 0c tx trigger + if (offset == REG_2_CONTROL && data == 0x03) + m_txblock = 0x00; + + // mode 0d tx trigger + if (offset == REG_5_TXSIZE && data > 0) + m_txblock = 0x00; + + // hack to get raverace working + if (m_reg[REG_1_MODE] == 0x08 && offset == REG_2_CONTROL) + { + if (data == 1) + m_txsize = 0; + if (data == 3) + m_reg[REG_5_TXSIZE] = m_txsize + 2; + } +} + +void namco_c139_device::sci_de_hack(uint8_t data) +{ + // prepare "filenames" + switch (data) + { + case 0: + m_localhost = "127.0.0.1"; + m_localport = "15112"; + m_remotehost = "127.0.0.1"; + m_remoteport = "15113"; + break; + case 1: + m_localhost = "127.0.0.1"; + m_localport = "15113"; + m_remotehost = "127.0.0.1"; + m_remoteport = "15114"; + break; + case 2: + m_localhost = "127.0.0.1"; + m_localport = "15114"; + m_remotehost = "127.0.0.1"; + m_remoteport = "15112"; + break; + default: + m_localhost = "127.0.0.1"; + m_localport = "15112"; + m_remotehost = "127.0.0.1"; + m_remoteport = "15112"; + break; + } + + // come up with some magic number for identification + std::string remotehost = util::string_format("%s:%s", m_remotehost, m_remoteport); + m_linkid = 0; + for (int x = 0; x < sizeof(remotehost) && remotehost[x] != 0; x++) + { + m_linkid ^= remotehost[x]; + } + + LOG("C139: ID byte = %02d\n", m_linkid); +} + +TIMER_CALLBACK_MEMBER(namco_c139_device::tick_timer_callback) +{ + comm_tick(); +} + +void namco_c139_device::comm_tick() +{ + m_context->check_sockets(); + + if (m_linktimer > 0x0000) + m_linktimer--; + + if (m_linktimer == 0x0000) + { + // if both sockets are connected + if (m_context->connected()) + { + // link established + unsigned data_size = 0x200; + switch (m_reg[REG_1_MODE]) + { + case 0x08: + // ridgera2, raverace + // 0b1000 + // reg2 - 1 > write mem > 3 > 1 > write mem > 3 etc. + // txcount NOT cleared on send + read_data(data_size); + if (m_reg[REG_2_CONTROL] == 0x03 && m_reg[REG_5_TXSIZE] > 0x00) + send_data(data_size); + break; + + case 0x09: + // suzuka8h, acedrive, winrungp, cybrcycc, driveyes (center) + // 0b1001 - auto-send via sync bit (and auto offset) + read_data(data_size); + send_data(data_size); + break; + + case 0x0c: + // ridgeracf + // 0b1100 - send by register / txwords + // txcount IS cleared on send + read_data(data_size); + if (m_reg[REG_2_CONTROL] == 0x03 && m_reg[REG_3_START] == 0x00) + send_data(data_size); + break; + + case 0x0d: + // final lap, driveyes (left & right) + // 0b1101 - auto-send via register? + read_data(data_size); + if (m_reg[REG_3_START] == 0x00 && m_reg[REG_5_TXSIZE] > 0x00) + send_data(data_size); + break; + + case 0x0f: + // init / reset + break; + + default: + // unknown mode + break; + } + } + } +} + +void namco_c139_device::read_data(unsigned data_size) +{ + if (m_reg[REG_0_STATUS] != 0x06) + { + // try to read a message + unsigned recv = read_frame(data_size); + if (recv > 0) + { + // (hack) update linkcount for mode 09 + if (m_buffer[0] == m_linkid && m_reg[REG_1_MODE] == 0x09 && (m_reg_f3 & 0x2) == 2) + m_buffer[0x07] = m_buffer[0x1ff]; + + // save message to "rx buffer" + unsigned rx_size = m_buffer[2] << 8 | m_buffer[1]; + unsigned rx_offset = m_reg[REG_6_RXOFFSET]; // rx offset in words + LOG("C139: rx_offset = %04x, rx_size == %02x\n", rx_offset, rx_size); + unsigned buf_offset = 3; + for (unsigned j = 0x00; j < rx_size; j++) + { + m_ram[0x1000 + (rx_offset & 0x0fff)] = m_buffer[buf_offset + 1] << 8 | m_buffer[buf_offset]; + rx_offset++; + buf_offset += 2; + } + + // relay messages + if (m_buffer[0] != m_linkid) + { + if (m_reg[REG_1_MODE] == 0x09 && (m_reg_f3 & 0x2) == 2) + m_buffer[0x1ff]++; + + send_frame(data_size); + } + else + { + if (m_reg[REG_1_MODE] == 0x09) + m_reg[REG_5_TXSIZE] = 0x00; + } + + // update regs + m_reg[REG_0_STATUS] = 0x06; + if (m_reg[REG_1_MODE] != 0x0d) + m_reg[REG_4_RXSIZE] += rx_size; + else + m_reg[REG_4_RXSIZE] -= rx_size; + m_reg[REG_6_RXOFFSET] += rx_size; + + // prevent overflow + m_reg[REG_4_RXSIZE] &= 0x0fff; + m_reg[REG_6_RXOFFSET] &= 0x0fff; + + // fire interrupt + m_irq_cb(ASSERT_LINE); + } + } + else + { + // fire interrupt (again) + m_irq_cb(ASSERT_LINE); + } +} + +unsigned namco_c139_device::read_frame(unsigned data_size) +{ + unsigned bytes_read = m_context->receive(&m_buffer[0], data_size); + if (bytes_read == UINT_MAX) + { + m_linktimer = 0x0100; + m_txblock = 0x00; + return 0; + } + return bytes_read; +} + +void namco_c139_device::send_data(unsigned data_size) +{ + if (m_txblock == 0x01) + return; + + if (m_reg[REG_0_STATUS] == 0x06) + return; + + unsigned tx_offset = m_reg[REG_7_TXOFFSET]; // tx offset in words + if ((m_reg_f3 & 0x02) == 0x02) + tx_offset >>= 1; // tx offset in bytes + + unsigned tx_mask = 0x0fff; + if (m_reg[REG_1_MODE] == 0x0d) + tx_mask = 0x1fff; // allow txPointer to rxBuffer + + unsigned tx_size = m_reg[REG_5_TXSIZE]; + if (m_reg[REG_1_MODE] == 0x09) + { + tx_size = find_sync_bit(tx_offset, tx_mask); + if (tx_size == 0x01) + { + m_reg[REG_7_TXOFFSET] += 0x100; + tx_size = 0; + } + } + + LOG("C139: tx_offset = %04x, tx_size == %02x\n", tx_offset, tx_size); + if (tx_size == 0) + return; + + m_buffer[0] = m_linkid; + m_buffer[1] = tx_size & 0xff; + m_buffer[2] = (tx_size & 0xff00) >> 8; + m_buffer[0x1ff] = 1; + + unsigned buf_offset = 3; + for (unsigned j = 0x00; j < tx_size; j++) + { + m_buffer[buf_offset] = m_ram[tx_offset & tx_mask] & 0xff; + m_buffer[buf_offset + 1] = 0; + + tx_offset++; + buf_offset += 2; + } + + // set bit-8 on last byte + m_buffer[buf_offset -1] |= 0x01; + + // based on mode, reset tx counter + switch (m_reg[REG_1_MODE]) + { + case 0x08: + case 0x09: + // do nothing + m_txblock = 0x01; + break; + case 0x0c: + case 0x0d: + m_reg[REG_5_TXSIZE] = 0; + m_txblock = 0x01; + break; + default: + // do nothing + break; + } + + m_txsize = 0; + send_frame(data_size); +} + +void namco_c139_device::send_frame(unsigned data_size) +{ + unsigned bytes_sent = m_context->send(&m_buffer[0], data_size); + if (bytes_sent == UINT_MAX) + { + m_linktimer = 0x0100; + m_txblock = 0x00; + } +} + +unsigned namco_c139_device::find_sync_bit(unsigned tx_offset, unsigned tx_mask) +{ + // cybrcycc + if ((m_ram[(tx_offset) & tx_mask] & 0x01ff) == 0x1ff) + return 0; + + // hack to find sync bit in data area + for (unsigned i = 0; i < 0x08; i++) + { + unsigned subOffset = i * 0x80; + for (unsigned j = 0; j < 0x100; j++) + { + if (m_ram[(tx_offset + subOffset + j) & tx_mask] & 0x0100) + { + if (i > 0) + m_reg[REG_7_TXOFFSET] += subOffset * 2; + return j + 1; + } + } + } + return 0; } diff --git a/src/mame/namco/namco_c139.h b/src/mame/namco/namco_c139.h index 1e3e6a1996264..afd1403f3501e 100644 --- a/src/mame/namco/namco_c139.h +++ b/src/mame/namco/namco_c139.h @@ -23,31 +23,66 @@ // ======================> namco_c139_device -class namco_c139_device : public device_t, - public device_memory_interface +class namco_c139_device : public device_t { public: // construction/destruction namco_c139_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + auto irq_cb() { return m_irq_cb.bind(); } + // I/O operations + void data_map(address_map &map) ATTR_COLD; void regs_map(address_map &map) ATTR_COLD; - uint16_t status_r(); - uint16_t ram_r(offs_t offset); void ram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); - void data_map(address_map &map) ATTR_COLD; + uint16_t reg_r(offs_t offset); + void reg_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + void sci_de_hack(uint8_t data); + + protected: // device-level overrides // virtual void device_validity_check(validity_checker &valid) const; virtual void device_start() override ATTR_COLD; + virtual void device_stop() override ATTR_COLD; virtual void device_reset() override ATTR_COLD; - virtual space_config_vector memory_space_config() const override; + + devcb_write_line m_irq_cb; + private: - const address_space_config m_space_config; - uint16_t* m_ram = nullptr; + uint16_t m_ram[0x2000]; + uint16_t m_reg[0x0010]; + + std::string m_localhost; + std::string m_localport; + std::string m_remotehost; + std::string m_remoteport; + + emu_timer *m_tick_timer; + + class context; + std::unique_ptr m_context; + + uint8_t m_buffer[0x200]; + + uint16_t m_linktimer; + uint8_t m_linkid; + uint8_t m_txsize; + uint8_t m_txblock; + uint8_t m_reg_f3; + + TIMER_CALLBACK_MEMBER(tick_timer_callback); + + void comm_tick(); + void read_data(unsigned data_size); + unsigned read_frame(unsigned data_size); + unsigned find_sync_bit(unsigned tx_offset, unsigned tx_mask); + void send_data(unsigned data_size); + void send_frame(unsigned data_size); }; @@ -55,11 +90,9 @@ class namco_c139_device : public device_t, DECLARE_DEVICE_TYPE(NAMCO_C139, namco_c139_device) - //************************************************************************** // GLOBAL VARIABLES //************************************************************************** - #endif // MAME_NAMCO_NAMCO_C139_H diff --git a/src/mame/namco/namcos2.cpp b/src/mame/namco/namcos2.cpp index e9cfa29f8f806..a0a7c592a5f44 100644 --- a/src/mame/namco/namcos2.cpp +++ b/src/mame/namco/namcos2.cpp @@ -691,7 +691,7 @@ void namcos2_state::namcos2_68k_default_cpu_board_am(address_map &map) map(0x420000, 0x42003f).rw(m_c123tmap, FUNC(namco_c123tmap_device::control16_r), FUNC(namco_c123tmap_device::control16_w)); map(0x440000, 0x44ffff).r(FUNC(namcos2_state::c116_r)).w(m_c116, FUNC(namco_c116_device::write)).umask16(0x00ff).cswidth(16); map(0x460000, 0x460fff).mirror(0x00f000).rw(FUNC(namcos2_state::dpram_word_r), FUNC(namcos2_state::dpram_word_w)); - map(0x480000, 0x483fff).rw(m_sci, FUNC(namco_c139_device::ram_r), FUNC(namco_c139_device::ram_w)); + map(0x480000, 0x483fff).m(m_sci, FUNC(namco_c139_device::data_map)); map(0x4a0000, 0x4a000f).m(m_sci, FUNC(namco_c139_device::regs_map)); } @@ -1707,7 +1707,8 @@ void namcos2_state::configure_common_standard(machine_config &config) NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1); - NAMCO_C139(config, m_sci, 0); + NAMCO_C139(config, m_sci, 0U); + m_sci->irq_cb().set(FUNC(namcos2_state::sci_int_w)); SCREEN(config, m_screen, SCREEN_TYPE_RASTER); m_screen->set_raw(MAIN_OSC_CLOCK/8, 384, 0*8, 36*8, 264, 0*8, 28*8); @@ -1750,6 +1751,12 @@ TIMER_DEVICE_CALLBACK_MEMBER(namcos2_state::screen_scanline) } } +void namcos2_state::sci_int_w(int state) +{ + m_master_intc->sci_irq_trigger(); + m_slave_intc->sci_irq_trigger(); +} + void namcos2_state::configure_c123tmap_standard(machine_config &config) { NAMCO_C123TMAP(config, m_c123tmap); diff --git a/src/mame/namco/namcos2.h b/src/mame/namco/namcos2.h index c613fba2746ab..61824242a02c0 100644 --- a/src/mame/namco/namcos2.h +++ b/src/mame/namco/namcos2.h @@ -226,6 +226,7 @@ enum int get_pos_irq_scanline() { return (m_c116->get_reg(5) - 32) & 0xff; } TIMER_DEVICE_CALLBACK_MEMBER(screen_scanline); + void sci_int_w(int state); required_shared_ptr m_dpram; /* 2Kx8 */ optional_shared_ptr m_spriteram; diff --git a/src/mame/namco/namcos21.cpp b/src/mame/namco/namcos21.cpp index 8d15bf7cb7274..c0df680d99f4c 100644 --- a/src/mame/namco/namcos21.cpp +++ b/src/mame/namco/namcos21.cpp @@ -385,6 +385,7 @@ class namcos21_state : public driver_device std::unique_ptr m_eeprom; TIMER_DEVICE_CALLBACK_MEMBER(screen_scanline); + void sci_int_w(int state); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); @@ -562,7 +563,7 @@ void namcos21_state::winrun_master_map(address_map &map) map(0x800000, 0x87ffff).rom().region("data", 0); map(0x900000, 0x90ffff).ram().share("sharedram"); map(0xa00000, 0xa00fff).rw(FUNC(namcos21_state::dpram_word_r), FUNC(namcos21_state::dpram_word_w)); - map(0xb00000, 0xb03fff).rw(m_sci, FUNC(namco_c139_device::ram_r), FUNC(namco_c139_device::ram_w)); + map(0xb00000, 0xb03fff).m(m_sci, FUNC(namco_c139_device::data_map)); map(0xb80000, 0xb8000f).m(m_sci, FUNC(namco_c139_device::regs_map)); } @@ -575,7 +576,7 @@ void namcos21_state::winrun_slave_map(address_map &map) map(0x800000, 0x87ffff).rom().region("data", 0); map(0x900000, 0x90ffff).ram().share("sharedram"); map(0xa00000, 0xa00fff).rw(FUNC(namcos21_state::dpram_word_r), FUNC(namcos21_state::dpram_word_w)); - map(0xb00000, 0xb03fff).rw(m_sci, FUNC(namco_c139_device::ram_r), FUNC(namco_c139_device::ram_w)); + map(0xb00000, 0xb03fff).m(m_sci, FUNC(namco_c139_device::data_map)); map(0xb80000, 0xb8000f).m(m_sci, FUNC(namco_c139_device::regs_map)); } @@ -869,6 +870,13 @@ TIMER_DEVICE_CALLBACK_MEMBER(namcos21_state::screen_scanline) m_gpu_intc->pos_irq_trigger(); } +void namcos21_state::sci_int_w(int state) +{ + m_master_intc->sci_irq_trigger(); + m_slave_intc->sci_irq_trigger(); + m_gpu_intc->sci_irq_trigger(); +} + void namcos21_state::configure_c148_standard(machine_config &config) { NAMCO_C148(config, m_master_intc, 0, m_maincpu, true); @@ -903,7 +911,9 @@ void namcos21_state::winrun(machine_config &config) configure_c148_standard(config); NAMCO_C148(config, m_gpu_intc, 0, "gpu", false); - NAMCO_C139(config, m_sci, 0); + + NAMCO_C139(config, m_sci, 0U); + m_sci->irq_cb().set(FUNC(namcos21_state::sci_int_w)); config.set_maximum_quantum(attotime::from_hz(6000)); /* 100 CPU slices per frame */ diff --git a/src/mame/namco/namcos21_c67.cpp b/src/mame/namco/namcos21_c67.cpp index 4ac77ad478e7e..d00dfd3e57c9a 100644 --- a/src/mame/namco/namcos21_c67.cpp +++ b/src/mame/namco/namcos21_c67.cpp @@ -353,6 +353,7 @@ class namcos21_c67_state : public driver_device TIMER_DEVICE_CALLBACK_MEMBER(screen_scanline); + void sci_int_w(int state); void yield_hack(int state); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); @@ -456,7 +457,7 @@ void namcos21_c67_state::common_map(address_map &map) map(0x800000, 0x8fffff).rom().region("data", 0); map(0x900000, 0x90ffff).ram().share("sharedram"); map(0xa00000, 0xa00fff).rw(FUNC(namcos21_c67_state::dpram_word_r), FUNC(namcos21_c67_state::dpram_word_w)); - map(0xb00000, 0xb03fff).rw(m_sci, FUNC(namco_c139_device::ram_r), FUNC(namco_c139_device::ram_w)); + map(0xb00000, 0xb03fff).m(m_sci, FUNC(namco_c139_device::data_map)); map(0xb80000, 0xb8000f).m(m_sci, FUNC(namco_c139_device::regs_map)); map(0xc00000, 0xcfffff).rom().mirror(0x100000).region("edata", 0); } @@ -781,6 +782,12 @@ TIMER_DEVICE_CALLBACK_MEMBER(namcos21_c67_state::screen_scanline) } } +void namcos21_c67_state::sci_int_w(int state) +{ + m_master_intc->sci_irq_trigger(); + m_slave_intc->sci_irq_trigger(); +} + void namcos21_c67_state::configure_c148_standard(machine_config &config) { NAMCO_C148(config, m_master_intc, 0, m_maincpu, true); @@ -827,7 +834,9 @@ void namcos21_c67_state::namcos21(machine_config &config) m_namcos21_3d->set_framebuffer_size(496,480); configure_c148_standard(config); - NAMCO_C139(config, m_sci, 0); + + NAMCO_C139(config, m_sci, 0U); + m_sci->irq_cb().set(FUNC(namcos21_c67_state::sci_int_w)); PALETTE(config, m_palette).set_format(palette_device::xBRG_888, 0x10000/2); diff --git a/src/mame/namco/namcos21_de.cpp b/src/mame/namco/namcos21_de.cpp index 6b41f9c75ce43..98479a14919f8 100644 --- a/src/mame/namco/namcos21_de.cpp +++ b/src/mame/namco/namcos21_de.cpp @@ -14,17 +14,6 @@ Driver's Eyes works, (probably wants angle sent by main board?) -On demo screen, should fog effects be turned off? - - NOTES: - - Driver's Eyes - not yet working - - TODO: - - Driver's Eyes - add communications for Left and Right screen (linked C139 or something else?) - */ #include "emu.h" @@ -61,6 +50,7 @@ class namco_de_pcbstack_device : public device_t namco_de_pcbstack_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); void configure_c148_standard(machine_config &config); + void sci_de_hack(uint8_t data); protected: virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; @@ -111,6 +101,7 @@ class namco_de_pcbstack_device : public device_t std::unique_ptr m_eeprom; TIMER_DEVICE_CALLBACK_MEMBER(screen_scanline); + void sci_int_w(int state); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); @@ -176,7 +167,8 @@ void namco_de_pcbstack_device::device_add_mconfig(machine_config &config) NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1); configure_c148_standard(config); - NAMCO_C139(config, m_sci, 0); + NAMCO_C139(config, m_sci, 0U); + m_sci->irq_cb().set(FUNC(namco_de_pcbstack_device::sci_int_w)); SCREEN(config, m_screen, SCREEN_TYPE_RASTER); // TODO: basic parameters to get 60.606060 Hz, x2 is for interlace @@ -353,7 +345,7 @@ void namco_de_pcbstack_device::driveyes_common_map(address_map &map) map(0x800000, 0x8fffff).rom().region("data", 0); map(0x900000, 0x90ffff).ram().share("sharedram"); map(0xa00000, 0xa00fff).rw(FUNC(namco_de_pcbstack_device::dpram_word_r), FUNC(namco_de_pcbstack_device::dpram_word_w)); - map(0xb00000, 0xb03fff).rw(m_sci, FUNC(namco_c139_device::ram_r), FUNC(namco_c139_device::ram_w)); + map(0xb00000, 0xb03fff).m(m_sci, FUNC(namco_c139_device::data_map)); map(0xb80000, 0xb8000f).m(m_sci, FUNC(namco_c139_device::regs_map)); } @@ -440,6 +432,17 @@ TIMER_DEVICE_CALLBACK_MEMBER(namco_de_pcbstack_device::screen_scanline) } } +void namco_de_pcbstack_device::sci_int_w(int state) +{ + m_master_intc->sci_irq_trigger(); + m_slave_intc->sci_irq_trigger(); +} + +void namco_de_pcbstack_device::sci_de_hack(uint8_t data) +{ + m_sci->sci_de_hack(data); +} + void namco_de_pcbstack_device::configure_c148_standard(machine_config &config) { NAMCO_C148(config, m_master_intc, 0, m_maincpu, true); @@ -485,6 +488,7 @@ class namcos21_de_state : public driver_device { } void driveyes(machine_config &config); + void init_driveyes(); private: required_device_array m_pcb; @@ -828,8 +832,15 @@ ROM_START( driveyes ) ROM_END +void namcos21_de_state::init_driveyes() +{ + m_pcb[0]->sci_de_hack(0); + m_pcb[1]->sci_de_hack(1); + m_pcb[2]->sci_de_hack(2); +} + + /* YEAR NAME PARENT MACHINE INPUT CLASS INIT MONITOR COMPANY FULLNAME FLAGS */ // 3 PCB stacks in a single cage (3x 4 PCBs) linked for 3 screen panorama, boards look similar to original Namco System 21 (not 21B) including TMS320C25 DSP, but use C68 I/O MCU and sprite chip instead of "68000 'GPU'" ? -GAME( 1992, driveyes, 0, driveyes, driveyes, namcos21_de_state, empty_init, ROT0, "Namco", "Driver's Eyes (Japan) (1992/01/10, Main Ver 2.1, Sub Ver 1.1)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NODEVICE_LAN) - +GAME( 1992, driveyes, 0, driveyes, driveyes, namcos21_de_state, init_driveyes, ROT0, "Namco", "Driver's Eyes (Japan) (1992/01/10, Main Ver 2.1, Sub Ver 1.1)", MACHINE_IMPERFECT_GRAPHICS) diff --git a/src/mame/namco/namcos22.cpp b/src/mame/namco/namcos22.cpp index bc4144e79f5bb..48763bb1ed2ed 100644 --- a/src/mame/namco/namcos22.cpp +++ b/src/mame/namco/namcos22.cpp @@ -1120,49 +1120,18 @@ SYSTEM SUPER22 AMP(4) PCB 8647960100 (8647970100) (sticker 'AMP(2) PCB 864796110 // Main CPU -/* SCI, preliminary! - -20020000 2 R/W RX Status - 0x01 : Frame Error - 0x02 : Frame Received - 0x04 : ? - -20020002 2 R/W Status/Control Flags - 0x01 : - 0x02 : RX flag? (cleared every vsync) - 0x04 : RX flag? (cleared every vsync) - 0x08 : - -20020004 2 W FIFO Control Register - 0x01 : sync bit enable? - 0x02 : TX FIFO sync bit (bit-8) - -20020006 2 W TX Control Register - 0x01 : TX start/stop - 0x02 : ? - 0x10 : ? - -20020008 2 W - -2002000a 2 W TX Frame Size -2002000c 2 R/W RX FIFO Pointer (0x0000 - 0x0fff) -2002000e 2 W TX FIFO Pointer (0x0000 - 0x1fff) -*/ -u16 namcos22_state::namcos22_sci_r(offs_t offset) +// SCI + +void namcos22_state::sci_int_w(int state) { - switch (offset) + int line = 0x04; + if (m_irq_enabled & line) { - case 0x0: - return 0x0004; - - default: - return 0; + m_irq_state |= line; + m_maincpu->set_input_line(m_syscontrol[2] & 7, state ? ASSERT_LINE : CLEAR_LINE); // SCI IRQ } } -void namcos22_state::namcos22_sci_w(offs_t offset, u16 data) -{ -} - // System Controller @@ -1691,7 +1660,7 @@ void namcos22_state::namcos22_am(address_map &map) * 20010000 - 20011fff TX Buffer * 20012000 - 20013fff RX FIFO Buffer (also used for TX Buffer) */ - map(0x20010000, 0x20013fff).ram(); + map(0x20010000, 0x20013fff).m(m_sci, FUNC(namco_c139_device::data_map)); /** * C139 SCI Register @@ -1722,7 +1691,7 @@ void namcos22_state::namcos22_am(address_map &map) * 2002000c 2 R/W RX FIFO Pointer (0x0000 - 0x0fff) * 2002000e 2 W TX FIFO Pointer (0x0000 - 0x1fff) */ - map(0x20020000, 0x2002000f).rw(FUNC(namcos22_state::namcos22_sci_r), FUNC(namcos22_state::namcos22_sci_w)); + map(0x20020000, 0x2002000f).m(m_sci, FUNC(namco_c139_device::regs_map)); /** * System Controller: Interrupt Control, Peripheral Control @@ -1863,8 +1832,8 @@ void namcos22s_state::namcos22s_am(address_map &map) { map(0x000000, 0x3fffff).rom(); map(0x400000, 0x40001f).rw(FUNC(namcos22s_state::namcos22_keycus_r), FUNC(namcos22s_state::namcos22_keycus_w)); - map(0x410000, 0x413fff).ram(); // C139 SCI buffer - map(0x420000, 0x42000f).rw(FUNC(namcos22s_state::namcos22_sci_r), FUNC(namcos22s_state::namcos22_sci_w)); // C139 SCI registers + map(0x410000, 0x413fff).m(m_sci, FUNC(namco_c139_device::data_map)); + map(0x420000, 0x42000f).m(m_sci, FUNC(namco_c139_device::regs_map)); map(0x430000, 0x430003).w(FUNC(namcos22s_state::namcos22_cpuleds_w)); map(0x440000, 0x440003).portr("DSW"); map(0x450008, 0x45000b).rw(FUNC(namcos22s_state::namcos22_portbit_r), FUNC(namcos22s_state::namcos22_portbit_w)); @@ -3766,6 +3735,9 @@ void namcos22_state::namcos22(machine_config &config) slave.xf_out_cb().set(FUNC(namcos22_state::dsp_xf_output_w)); slave.dx_out_cb().set(FUNC(namcos22_state::slave_serial_io_w)); + NAMCO_C139(config, m_sci, 0U); + m_sci->irq_cb().set(FUNC(namcos22_state::sci_int_w)); + NAMCO_C74(config, m_mcu, 49.152_MHz_XTAL/3); // C74 on the CPU board has no periodic interrupts, it runs entirely off Timer A0 m_mcu->set_addrmap(AS_PROGRAM, &namcos22_state::mcu_s22_program); m_mcu->p4_in_cb().set(FUNC(namcos22_state::mcu_port4_s22_r)); diff --git a/src/mame/namco/namcos22.h b/src/mame/namco/namcos22.h index 2e2be7c4a1a01..da57c2f16b6d4 100644 --- a/src/mame/namco/namcos22.h +++ b/src/mame/namco/namcos22.h @@ -11,6 +11,8 @@ #pragma once +#include "namco_c139.h" + #include "machine/eeprompar.h" #include "machine/mb87078.h" #include "namcomcu.h" @@ -208,6 +210,7 @@ class namcos22_state : public driver_device m_eeprom(*this, "eeprom"), m_mb87078(*this, "mb87078"), m_c352(*this, "c352"), + m_sci(*this, "sci"), m_shareram(*this, "shareram"), m_slave_extram(*this, "slaveextram"), m_master_extram(*this, "masterextram"), @@ -317,8 +320,6 @@ class namcos22_state : public driver_device u16 dsp_slave_port8_r(); u16 dsp_slave_portb_r(); void dsp_slave_portb_w(u16 data); - u16 namcos22_sci_r(offs_t offset); - void namcos22_sci_w(offs_t offset, u16 data); u16 namcos22_shared_r(offs_t offset); void namcos22_shared_w(offs_t offset, u16 data, u16 mem_mask = ~0); u16 namcos22_keycus_r(offs_t offset); @@ -420,6 +421,7 @@ class namcos22_state : public driver_device required_device m_eeprom; optional_device m_mb87078; required_device m_c352; + required_device m_sci; required_shared_ptr m_shareram; required_shared_ptr m_slave_extram; required_shared_ptr m_master_extram; @@ -507,6 +509,7 @@ class namcos22_state : public driver_device bool m_skipped_this_frame = false; void render_frame_active(); void screen_vblank(int state); + void sci_int_w(int state); bool m_pdp_render_done = false; bool m_render_refresh = false; u64 m_pdp_frame = 0;