summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/src/bus.lua27
-rw-r--r--src/devices/bus/jvs/cyberlead.cpp486
-rw-r--r--src/devices/bus/jvs/cyberlead.h95
-rw-r--r--src/devices/bus/jvs/jvs.cpp450
-rw-r--r--src/devices/bus/jvs/jvs.h157
-rw-r--r--src/devices/bus/jvs/jvshle.cpp868
-rw-r--r--src/devices/bus/jvs/jvshle.h175
-rw-r--r--src/devices/bus/jvs/namcoio.cpp1564
-rw-r--r--src/devices/bus/jvs/namcoio.h73
-rw-r--r--src/devices/bus/rs232/rs232.h2
-rw-r--r--src/emu/layout/cyberlead.lay14
11 files changed, 3910 insertions, 1 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index b037f0381fd..dabbbd82731 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -5561,6 +5561,33 @@ end
---------------------------------------------------
--
+--@src/devices/bus/jvs/jvs.h,BUSES["JVS"] = true
+---------------------------------------------------
+
+if (BUSES["JVS"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/bus/jvs/cyberlead.cpp",
+ MAME_DIR .. "src/devices/bus/jvs/cyberlead.h",
+ MAME_DIR .. "src/devices/bus/jvs/jvs.cpp",
+ MAME_DIR .. "src/devices/bus/jvs/jvs.h",
+ MAME_DIR .. "src/devices/bus/jvs/jvshle.cpp",
+ MAME_DIR .. "src/devices/bus/jvs/jvshle.h",
+ MAME_DIR .. "src/devices/bus/jvs/namcoio.cpp",
+ MAME_DIR .. "src/devices/bus/jvs/namcoio.h",
+ }
+
+ dependency {
+ { MAME_DIR .. "src/devices/bus/jvs/cyberlead.cpp", GEN_DIR .. "emu/layout/cyberlead.lh" },
+ }
+
+ custombuildtask {
+ layoutbuildtask("emu/layout", "cyberlead"),
+ }
+end
+
+
+---------------------------------------------------
+--
--@src/devices/bus/mtu130/board.h,BUSES["MTU130"] = true
---------------------------------------------------
diff --git a/src/devices/bus/jvs/cyberlead.cpp b/src/devices/bus/jvs/cyberlead.cpp
new file mode 100644
index 00000000000..5598da24cd9
--- /dev/null
+++ b/src/devices/bus/jvs/cyberlead.cpp
@@ -0,0 +1,486 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+/*
+ Namco Cyber Lead cabinet PCB
+
+ CL1 I/OB I/O LED (I/O) PCB 8699014200
+ CL1 LEDA I/O LED (LED) PCB 8699014500
+ Both boards are required and use the same PCB, populated differently
+ |------------------------------------------------------------------------------------------------|
+ | J3 J4 J6 |
+ | J2 J5 |
+ | PROG I/OLEDM1 |
+ | AT29C020 SED1351F B A |
+ | C77 14.764Mhz PST592F J7 |
+ | HC257 HC257 HC257 N341256 |
+ | J1 |
+ | SW1 SW2 SW3 SW4 SW5 9264LF10L 12MHZ |
+ | 1P 2P |
+ | J13 J12 J11 J10 |
+ |------------------------------------------------------------------------------------------------|
+
+Notes:
+ J1 - 6 pin connector (I/O Only)
+ J2 - 2 pin power connector
+ J3 - 10 pin connector (I/O Only)
+ J4 - 12 pin connector (I/O Only)
+ J5 PROG - ?
+ J6 - 6 pin Mini DIN connector
+ J7 - 20 pin connector (LED Only)
+ J10 - 2 pin connector (I/O Only)
+ J11 - 12 pin connector (I/O Only)
+ J12 - 12 pin connector (I/O Only)
+ J13 - 2 pin connector (I/O Only)
+ A - USB A connector (I/O Only)
+ B - USB B connector (I/O Only)
+ HC257 - Quad 2-input multiplexer; 3-state (I/O Only) @IC1,IC2,IC3
+ N341256 - NEC N341256 32k x8 SRAM @IC4
+ AT29C020 - Atmel AT29C020 256k x8 FlashROM @IC5
+ C77 - Namco C77 H8 MCU @IC6
+ 14.76 - X1 14.7SC6M
+ SW1 - A push button
+ SW2 - B push button
+ SW3 - DSW4
+ SW4 - Service push button
+ SW5 - Test push button
+ SED1351F - EPSON SED1351F LCD controller (LED Only) @IC7
+ 9264LF10L - 8192x8 Bit Static RAM (LED Only) @IC8
+ I/OLEDM1 - Altera EPM7064 @IC9
+ PST592F - System reset @IC10
+ 12MHz - OSC1 (LED Only)
+ ADM485JR - Analog Devices ADM485 low power EIA RS-485 transceiver (I/O Only) @IC11
+
+Known games with LED data
+
+ Aqua Rush
+ Attack Pla Rail (*)
+ Ehrgeiz (*)
+ Fighting Layer (Japan) (*)
+ Libero Grande
+ Mr. Driller
+ Soul Calibur
+ Super World Stadium '98
+ Super World Stadium '99
+ Tekken 3 (* up to rev D)
+ Tekken Tag Tournament
+
+ (*) The only games that work with the unpatched CL1 I/OB Ver 1.03 rom
+*/
+
+#include "emu.h"
+#include "cyberlead.h"
+#include "cyberlead.lh"
+
+namco_cyberlead_device::namco_cyberlead_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ namco_cyberlead_device(mconfig, NAMCO_CYBERLEAD, tag, owner, clock)
+{
+}
+
+namco_cyberlead_device::namco_cyberlead_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, type, tag, owner, clock),
+ device_jvs_interface(mconfig, *this),
+ m_iocpu(*this, "iocpu"),
+ m_dsw(*this, "DSW"),
+ m_port7(*this, "PORT7"),
+ m_rs232(*this, "rs232")
+{
+}
+
+void namco_cyberlead_device::device_add_mconfig(machine_config &config)
+{
+ NAMCO_C77(config, m_iocpu, 14.7456_MHz_XTAL);
+ m_iocpu->set_addrmap(AS_PROGRAM, &namco_cyberlead_device::iocpu_program_map);
+ m_iocpu->read_port4().set(FUNC(namco_cyberlead_device::iocpu_port_4_r));
+ m_iocpu->write_port4().set(FUNC(namco_cyberlead_device::iocpu_port_4_w));
+ m_iocpu->read_port5().set(FUNC(namco_cyberlead_device::iocpu_port_5_r));
+ m_iocpu->write_port5().set(FUNC(namco_cyberlead_device::iocpu_port_5_w));
+ m_iocpu->read_port6().set(FUNC(namco_cyberlead_device::iocpu_port_6_r));
+ m_iocpu->write_port6().set(FUNC(namco_cyberlead_device::iocpu_port_6_w));
+ m_iocpu->read_port7().set(FUNC(namco_cyberlead_device::iocpu_port_7_r));
+
+ m_iocpu->write_sci_tx<0>().set(FUNC(namco_cyberlead_device::txd));
+
+ config.set_maximum_quantum(attotime::from_hz(2 * 115200));
+
+ RS232_PORT(config, m_rs232);
+ m_rs232->option_add("namco_cyberlead_led", NAMCO_CYBERLEAD_LED);
+ m_rs232->set_option_machine_config("namco_cyberlead_led", [&config](device_t *device)
+ {
+ if (!(config.gamedrv().flags & machine_flags::SWAP_XY))
+ config.set_default_layout(layout_cyberlead);
+ });
+
+ m_rs232->set_default_option("namco_cyberlead_led");
+ m_rs232->rxd_handler().set(m_iocpu, FUNC(h8_device::sci_rx_w<1>));
+ m_iocpu->write_sci_tx<1>().set(m_rs232, FUNC(rs232_port_device::write_txd));
+
+ add_jvs_port(config);
+}
+
+namespace {
+
+static INPUT_PORTS_START(cyberleadjvs)
+ PORT_START("DSW")
+ PORT_DIPUNKNOWN_DIPLOC(0x08, IP_ACTIVE_LOW, "SW3:1")
+ PORT_DIPUNKNOWN_DIPLOC(0x04, IP_ACTIVE_LOW, "SW3:2")
+ PORT_DIPUNKNOWN_DIPLOC(0x02, IP_ACTIVE_LOW, "SW3:3")
+ PORT_DIPUNUSED_DIPLOC(0x01, IP_ACTIVE_LOW, "SW3:4")
+
+ PORT_START("PORT7")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("LED Mode B") PORT_CODE(KEYCODE_B)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("LED Mode A") PORT_CODE(KEYCODE_A)
+ PORT_BIT(0x8c, IP_ACTIVE_LOW, IPT_UNKNOWN) // inputs?
+ PORT_BIT(0x70, IP_ACTIVE_LOW, IPT_UNKNOWN) // outputs?
+INPUT_PORTS_END
+
+static INPUT_PORTS_START(cyberleadjvs_default)
+ PORT_INCLUDE(cyberleadjvs)
+
+ PORT_START("SYSTEM")
+ PORT_SERVICE_NO_TOGGLE(0x80, IP_ACTIVE_HIGH)
+
+ PORT_START("PLAYER1")
+ PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_START)
+ PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_SERVICE1)
+ PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)
+ PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)
+ PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)
+ PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT)
+ PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_BUTTON1)
+ PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_BUTTON2)
+ PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_BUTTON3)
+ PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_BUTTON4)
+ PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_BUTTON5)
+ PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_BUTTON6)
+
+ PORT_START("PLAYER2")
+ PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_START) PORT_PLAYER(2)
+ PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_PLAYER(2)
+ PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_PLAYER(2)
+ PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_PLAYER(2)
+ PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2)
+ PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(2)
+ PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(2)
+ PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_PLAYER(2)
+ PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_PLAYER(2)
+ PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_PLAYER(2)
+ PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_PLAYER(2)
+
+ PORT_START("COIN1")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_COIN1)
+
+ PORT_START("COIN2")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_COIN2)
+INPUT_PORTS_END
+
+} // anonymous namespace
+
+ioport_constructor namco_cyberlead_device::device_input_ports() const
+{
+ return m_default_inputs ? INPUT_PORTS_NAME(cyberleadjvs_default) : INPUT_PORTS_NAME(cyberleadjvs);
+}
+
+namespace {
+
+ROM_START( cyberleadjvs )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "cl1-iob.ic5", 0x000000, 0x040000, CRC(abb90360) SHA1(d938b1e1ae596d0ab1007352f61b0b800363c762) )
+ ROM_FILL( 0xa6b, 0x1, 0x12 ) // HACK to improve compatibility, until newer revision is dumped
+ROM_END
+
+} // anonymous namespace
+
+const tiny_rom_entry *namco_cyberlead_device::device_rom_region() const
+{
+ return ROM_NAME(cyberleadjvs);
+}
+
+void namco_cyberlead_device::device_start()
+{
+}
+
+void namco_cyberlead_device::rxd(int state)
+{
+ m_iocpu->sci_rx_w<0>(state);
+}
+
+void namco_cyberlead_device::iocpu_program_map(address_map &map)
+{
+ map(0x0000, 0x3fff).rom().region("iocpu", 0);
+ map(0xc000, 0xfb7f).ram();
+ map(0xff80, 0xff83).r(FUNC(namco_cyberlead_device::iocpu_in_r));
+}
+
+uint8_t namco_cyberlead_device::iocpu_in_r(offs_t offset)
+{
+ if (offset == 0)
+ {
+ const auto p1 = player_r(0, 0x80bf);
+ return ~bitswap<8>(p1, 15, 0, 1, 7, 2, 3, 4, 5); // BUTTON3, BUTTON2, BUTTON1, START, RIGHT, LEFT, DOWN, UP
+ }
+ else if (offset == 1)
+ {
+ const auto p2 = player_r(1, 0x80bf);
+ return ~bitswap<8>(p2, 15, 0, 1, 7, 2, 3, 4, 5); // BUTTON3, BUTTON2, BUTTON1, START, RIGHT, LEFT, DOWN, UP
+ }
+ else if (offset == 2)
+ {
+ const auto p1 = player_r(0, 0x7000);
+ const auto c1 = coin_r(0, 0x01);
+ return ~((BIT(c1, 0) << 3) | bitswap<3>(p1, 12, 13, 14)); // COIN, BUTTON6, BUTTON5, BUTTON4
+ }
+ else if (offset == 3)
+ {
+ const auto p2 = player_r(1, 0x7000);
+ const auto c2 = coin_r(1, 0x01);
+ return ~((BIT(c2, 0) << 3) | bitswap<3>(p2, 12, 13, 14)); // COIN, BUTTON6, BUTTON5, BUTTON4
+ }
+
+ return 0xff;
+}
+
+uint8_t namco_cyberlead_device::iocpu_port_4_r()
+{
+ return (1 << 7) | // unknown input
+ 0x63 | // unknown outputs
+ ((m_jvs_sense != jvs_port_device::sense::None) << 4) | ((m_jvs_sense != jvs_port_device::sense::Initialized) << 3) |
+ (1 << 2);// sense output
+}
+
+void namco_cyberlead_device::iocpu_port_4_w(uint8_t data)
+{
+ sense(BIT(data, 2) ? jvs_port_device::sense::Initialized : jvs_port_device::sense::Uninitialized);
+}
+
+uint8_t namco_cyberlead_device::iocpu_port_5_r()
+{
+ return 0xe9 | // unknown outputs
+ (1 << 4) | // unknown input
+ (1 << 2) | // rts
+ (1 << 1); // unknown input
+}
+
+void namco_cyberlead_device::iocpu_port_5_w(uint8_t data)
+{
+ rts(BIT(data, 2));
+}
+
+uint8_t namco_cyberlead_device::iocpu_port_6_r()
+{
+ const auto system = system_r(0x80);
+ const auto p1 = player_r(0, 0x40);
+
+ return (1 << 7) | // unknown
+ (3 << 5) | // coin counters
+ (BIT(~p1, 6) << 4) | // SERVICE1
+ (BIT(m_dsw->read(), 1, 3) << 1) | // DSW1-3 TODO: confirm
+ (BIT(~system, 7) << 0); // TEST
+}
+
+void namco_cyberlead_device::iocpu_port_6_w(uint8_t data)
+{
+ machine().bookkeeping().coin_counter_w(0, BIT(data, 6));
+ machine().bookkeeping().coin_counter_w(1, BIT(data, 5));
+}
+
+uint8_t namco_cyberlead_device::iocpu_port_7_r()
+{
+ return m_port7->read();
+}
+
+
+namco_cyberlead_led_device::namco_cyberlead_led_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, NAMCO_CYBERLEAD_LED, tag, owner, clock),
+ device_rs232_port_interface(mconfig, *this),
+ m_ledcpu(*this, "ledcpu"),
+ m_flash(*this, "flash"),
+ m_screen(*this, "led"),
+ m_scroll(*this, "scroll"),
+ m_sed1351f_ram(*this, "sed1351f_ram"),
+ m_sed1351f_control(*this, "sed1351f_control"),
+ m_dsw(*this, "DSW")
+{
+}
+
+namespace {
+
+} // anonymous namespace
+
+void namco_cyberlead_led_device::device_add_mconfig(machine_config &config)
+{
+ NAMCO_C77(config, m_ledcpu, 14.7456_MHz_XTAL);
+ m_ledcpu->set_addrmap(AS_PROGRAM, &namco_cyberlead_led_device::ledcpu_program_map);
+ m_ledcpu->read_port6().set(FUNC(namco_cyberlead_led_device::ledcpu_port_6_r));
+
+ m_ledcpu->write_sci_tx<1>().set(FUNC(namco_cyberlead_led_device::output_rxd));
+
+ ATMEL_29C020(config, m_flash);
+
+ SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
+ m_screen->set_native_aspect();
+ m_screen->set_size(96 * 8, 16 * 8);
+ m_screen->set_visarea(0, (96 * 8) - 1, 0, (16 * 8) - 1);
+ m_screen->set_screen_update(FUNC(namco_cyberlead_led_device::screen_update));
+ m_screen->set_refresh_hz(60);
+ m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500));
+ m_screen->set_orientation(ROT180);
+ m_screen->screen_vblank().set([this](int state)
+ {
+ m_ledcpu->set_input_line(6, state ? ASSERT_LINE : CLEAR_LINE);
+ });
+}
+
+namespace {
+
+static INPUT_PORTS_START(cyberleadled)
+ PORT_START("DSW")
+ PORT_DIPNAME(0x08, 0x08, "Display Test") PORT_DIPLOCATION("SW3:1") // c9fb
+ PORT_DIPSETTING(0x08, "1 Time")
+ PORT_DIPSETTING(0x00, "4 Times")
+ PORT_DIPNAME(0x04, 0x04, "Disable Epilepsy Filter") PORT_DIPLOCATION("SW3:2") // c9fc
+ PORT_DIPSETTING(0x04, DEF_STR(Off))
+ PORT_DIPSETTING(0x00, DEF_STR(On))
+ PORT_DIPUNKNOWN_DIPLOC(0x02, IP_ACTIVE_LOW, "SW3:3")
+ PORT_DIPUNUSED_DIPLOC(0x01, IP_ACTIVE_LOW, "SW3:4")
+INPUT_PORTS_END
+
+} // anonymous namespace
+
+ioport_constructor namco_cyberlead_led_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(cyberleadled);
+}
+
+namespace {
+
+static const uint16_t led_gfx[] =
+{
+ 0b0001101010010000,
+ 0b0110111111100100,
+ 0b1011111111111001,
+ 0b1011111111111001,
+ 0b1011111111111001,
+ 0b0110111111100100,
+ 0b0001101010010000,
+ 0b0000010101000000,
+};
+
+ROM_START( cyberleadled )
+ ROM_REGION( 0x40000, "flash", 0 )
+ ROM_LOAD( "cl1-leda.ic5", 0x000000, 0x040000, CRC(5717e05f) SHA1(61d61338e1bb414af32dbdc1e24d54c02fb9196e) )
+ROM_END
+
+} // anonymous namespace
+
+const tiny_rom_entry *namco_cyberlead_led_device::device_rom_region() const
+{
+ return ROM_NAME(cyberleadled);
+}
+
+void namco_cyberlead_led_device::device_start()
+{
+ for (int c = 0; c < 4; c++)
+ for (int i = 0; i < 4; i++)
+ {
+ int r = ((((0xff * c) / 3) + 0x2f) * i) / 3;
+ m_palette[c][i] = rgb_t(std::min(r, 0xff), r / 4, r / 3);
+ }
+
+ m_flash_bank = 0;
+
+ save_item(NAME(m_flash_bank));
+}
+
+void namco_cyberlead_led_device::input_txd(int state)
+{
+ m_ledcpu->sci_rx_w<1>(state);
+}
+
+void namco_cyberlead_led_device::ledcpu_program_map(address_map &map)
+{
+ map(0x0000, 0x3fff).rw(m_flash, FUNC(intelfsh8_device::read), FUNC(intelfsh8_device::write));
+ map(0x4000, 0x5fff).ram().share(m_sed1351f_ram);
+ map(0x6000, 0x600f).ram().share(m_sed1351f_control);
+ map(0x7000, 0x7001).ram().share(m_scroll);
+ map(0x8000, 0xbfff).rw(FUNC(namco_cyberlead_led_device::ledcpu_banked_flash_r), FUNC(namco_cyberlead_led_device::ledcpu_banked_flash_w));
+ map(0xc000, 0xfb7f).ram();
+ map(0xff84, 0xff87).w(FUNC(namco_cyberlead_led_device::ledcpu_flash_bank_w));
+}
+
+uint8_t namco_cyberlead_led_device::ledcpu_banked_flash_r(offs_t offset)
+{
+ return m_flash->read(offset + m_flash_bank);
+}
+
+void namco_cyberlead_led_device::ledcpu_banked_flash_w(offs_t offset, uint8_t data)
+{
+ m_flash->write(offset + m_flash_bank, data);
+}
+
+void namco_cyberlead_led_device::ledcpu_flash_bank_w(offs_t offset, uint8_t data)
+{
+ m_flash_bank = 0x4000 * (offset | (data << 2));
+}
+
+uint8_t namco_cyberlead_led_device::ledcpu_port_6_r()
+{
+ const auto system = 0;
+ const auto p1 = 0;
+
+ return (1 << 7) | // unknown
+ (3 << 5) | // coin counters
+ (BIT(~p1, 6) << 4) | // SERVICE1
+ (BIT(m_dsw->read(), 1, 3) << 1) | // DSW1-3 TODO: confirm
+ (BIT(~system, 7) << 0); // TEST
+}
+
+uint32_t namco_cyberlead_led_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ int start = swapendian_int16(m_sed1351f_control[6 / 2]) & 0x1fff;
+ int scroll = ~m_ledcpu->space(AS_PROGRAM).read_byte(0xd0bf) & 3;
+ int scrollx = ((start & 0x1f) * 4) + scroll;
+ int scrolly = start >> 5;
+
+ for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
+ {
+ uint32_t *dst = &bitmap.pix(y, cliprect.min_x);
+ uint16_t *src = &m_sed1351f_ram[((scrolly + (y >> 3)) & 511) * 16];
+ uint16_t gfx = led_gfx[y & 7];
+ for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
+ {
+ int p = (scrollx + (x >> 3)) & 127;
+ *dst++ = m_palette[BIT(src[p >> 3], (~p & 7) * 2, 2)][BIT(gfx, (x & 7) << 1, 2)];
+ }
+ }
+ return 0;
+}
+
+namespace {
+
+ROM_START( cyberleadajvs )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "cl1-iob.ic5", 0x000000, 0x040000, CRC(abb90360) SHA1(d938b1e1ae596d0ab1007352f61b0b800363c762) )
+ROM_END
+
+class namco_cyberleada_device :
+ public namco_cyberlead_device
+{
+public:
+ namco_cyberleada_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0)
+ : namco_cyberlead_device(mconfig, NAMCO_CYBERLEADA, tag, owner, clock)
+ {
+ }
+
+protected:
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(cyberleadajvs);
+ }
+};
+
+} // anonymous namespace
+
+
+DEFINE_DEVICE_TYPE(NAMCO_CYBERLEAD, namco_cyberlead_device, "namco_cyberlead", "Namco Cyber Lead I/O LED(I/O) PCB 8699014200 (compatibility patch)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_CYBERLEADA, device_jvs_interface, namco_cyberleada_device, "namco_cyberleada", "Namco Cyber Lead I/O LED(I/O) PCB 8699014200")
+DEFINE_DEVICE_TYPE(NAMCO_CYBERLEAD_LED, namco_cyberlead_led_device, "namco_cyberlead_led", "Namco Cyber Lead I/O LED(LED) PCB 8699014500")
diff --git a/src/devices/bus/jvs/cyberlead.h b/src/devices/bus/jvs/cyberlead.h
new file mode 100644
index 00000000000..08c1873a4ad
--- /dev/null
+++ b/src/devices/bus/jvs/cyberlead.h
@@ -0,0 +1,95 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+
+#ifndef MAME_BUS_JVS_CYBERLEAD_H
+#define MAME_BUS_JVS_CYBERLEAD_H
+
+#pragma once
+
+#include "jvs.h"
+#include "bus/rs232/rs232.h"
+#include "cpu/h8/c77.h"
+#include "machine/intelfsh.h"
+#include "emupal.h"
+#include "screen.h"
+#include "tilemap.h"
+
+class namco_cyberlead_device :
+ public device_t,
+ public device_jvs_interface
+{
+public:
+ namco_cyberlead_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ auto &rs232() const { if (m_rs232) return *m_rs232; return *subdevice<rs232_port_device>(m_rs232.finder_tag()); }
+
+protected:
+ namco_cyberlead_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ // device_t
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD;
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
+ virtual void device_start() override ATTR_COLD;
+
+ // device_jvs_interface
+ virtual void rxd(int state) override;
+
+private:
+ void iocpu_program_map(address_map &map);
+
+ uint8_t iocpu_in_r(offs_t offset);
+ uint8_t iocpu_port_4_r();
+ void iocpu_port_4_w(uint8_t data);
+ uint8_t iocpu_port_5_r();
+ void iocpu_port_5_w(uint8_t data);
+ uint8_t iocpu_port_6_r();
+ void iocpu_port_6_w(uint8_t data);
+ uint8_t iocpu_port_7_r();
+
+ required_device<namco_c77_device> m_iocpu;
+ required_ioport m_dsw;
+ required_ioport m_port7;
+ required_device<rs232_port_device> m_rs232;
+};
+
+class namco_cyberlead_led_device :
+ public device_t,
+ public device_rs232_port_interface
+{
+public:
+ namco_cyberlead_led_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+protected:
+ // device_t
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD;
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
+ virtual void device_start() override ATTR_COLD;
+
+ // device_rs232_port_interface
+ virtual void input_txd(int state) override;
+
+ uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ void ledcpu_program_map(address_map &map);
+ uint8_t ledcpu_banked_flash_r(offs_t offset);
+ void ledcpu_banked_flash_w(offs_t offset, uint8_t data);
+ void ledcpu_flash_bank_w(offs_t offset, uint8_t data);
+ uint8_t ledcpu_port_6_r();
+
+ required_device<namco_c77_device> m_ledcpu;
+ required_device<intelfsh8_device> m_flash;
+ required_device<screen_device> m_screen;
+ required_shared_ptr<uint16_t> m_scroll;
+ required_shared_ptr<uint16_t> m_sed1351f_ram;
+ required_shared_ptr<uint16_t> m_sed1351f_control;
+ required_ioport m_dsw;
+ rgb_t m_palette[4][4];
+ int m_flash_bank;
+};
+
+DECLARE_DEVICE_TYPE(NAMCO_CYBERLEAD, namco_cyberlead_device)
+DECLARE_DEVICE_TYPE(NAMCO_CYBERLEADA, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_CYBERLEAD_LED, namco_cyberlead_led_device)
+
+#endif
diff --git a/src/devices/bus/jvs/jvs.cpp b/src/devices/bus/jvs/jvs.cpp
new file mode 100644
index 00000000000..5bc224e085b
--- /dev/null
+++ b/src/devices/bus/jvs/jvs.cpp
@@ -0,0 +1,450 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+
+#include "emu.h"
+#include "jvs.h"
+
+#define LOG_PACKETS (1U << 1)
+#define LOG_SIGNALS (1U << 2)
+#define LOG_IGNORED (1U << 3)
+#define LOG_CONFLICT (1U << 4)
+
+//#define VERBOSE (LOG_PACKETS /*| LOG_SIGNALS | LOG_IGNORED | LOG_CONFLICT*/)
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
+
+#define LOGPACKETS(...) LOGMASKED(LOG_PACKETS, __VA_ARGS__)
+#define LOGSIGNALS(...) LOGMASKED(LOG_SIGNALS, __VA_ARGS__)
+#define LOGIGNORED(...) LOGMASKED(LOG_IGNORED, __VA_ARGS__)
+#define LOGCONFLICT(...) LOGMASKED(LOG_CONFLICT, __VA_ARGS__)
+
+DEFINE_DEVICE_TYPE(JVS_PORT, jvs_port_device, "jvs_port", "JVS IO Port")
+
+#define FIX_CONFLICT (1)
+
+jvs_port_device::jvs_port_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, JVS_PORT, tag, owner, clock),
+ device_single_card_slot_interface<device_jvs_interface>(mconfig, *this),
+ m_device(nullptr),
+ m_debug_timer(nullptr),
+ m_rxd_cb(*this),
+ m_sense_cb(*this),
+ m_rts(0),
+ m_rxd(0),
+ m_txd(1)
+{
+}
+
+jvs_port_device::~jvs_port_device()
+{
+}
+
+void jvs_port_device::device_config_complete()
+{
+ m_device = get_card_device();
+}
+
+void jvs_port_device::device_start()
+{
+ device_jvs_interface *intf;
+ if (!owner()->interface(intf))
+ {
+ if (VERBOSE & LOG_PACKETS)
+ m_debug_timer = timer_alloc(FUNC(jvs_port_device::debug_timer_callback), this);
+
+ m_debug_bit = -1;
+ m_debug_index = 0;
+ m_debug_rxd = 1;
+ std::fill_n(m_debug_buffer, std::size(m_debug_buffer), 0);
+
+ update_rxd();
+
+ save_item(NAME(m_debug_bit));
+ save_item(NAME(m_debug_index));
+ save_item(NAME(m_debug_rxd));
+ save_item(NAME(m_debug_buffer));
+ }
+
+ save_item(NAME(m_rts));
+ save_item(NAME(m_txd));
+ save_item(NAME(m_rxd));
+}
+
+void jvs_port_device::rts(int state)
+{
+ if (m_rts != state)
+ {
+ LOGSIGNALS("%s rts %d\n", machine().describe_context(), state);
+
+ if (machine().scheduler().currently_executing())
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(jvs_port_device::update_rts), this), state);
+ else
+ update_rts(state);
+ }
+}
+
+void jvs_port_device::update_rts(s32 state)
+{
+ debug_flush();
+ debug_stop();
+
+ m_rts = state;
+
+ if (m_device)
+ {
+ if (m_rts && !m_rxd && !(FIX_CONFLICT && m_txd))
+ m_rxd_cb(1);
+
+ update_rxd();
+ }
+}
+
+void jvs_port_device::txd(int state)
+{
+ if (m_txd != state)
+ {
+ LOGSIGNALS("%s txd %d\n", machine().describe_context(), state);
+ m_txd = state;
+
+ update_rxd();
+ }
+}
+
+void jvs_port_device::update_rxd()
+{
+ int high = 0;
+ int low = 0;
+
+ if (m_rts)
+ {
+ high += (m_txd != 0);
+ low += (m_txd == 0);
+ }
+ else if (!m_txd)
+ LOGIGNORED("ignored txd\n");
+
+ for (device_jvs_interface *intf = m_device; intf; intf = intf->m_jvs ? intf->m_jvs->m_device : nullptr)
+ {
+ if (intf->m_rts)
+ {
+ high += (intf->m_txd != 0);
+ low += (intf->m_txd == 0);
+ }
+ else if (!intf->m_txd)
+ LOGIGNORED("ignored txd %s\n", intf->device().tag());
+ }
+
+ if (high + low > 1 && ((VERBOSE & LOG_CONFLICT) || FIX_CONFLICT))
+ {
+ debug_flush('@');
+
+ std::string conflict;
+ if (VERBOSE & LOG_CONFLICT)
+ conflict = util::string_format("conflict detected between %d devices (%d high, %d low)", high + low, high, low);
+
+ if (m_rts)
+ {
+ if (VERBOSE & LOG_CONFLICT)
+ conflict += util::string_format(",%s (%d)", tag(), m_txd);
+
+ if (FIX_CONFLICT && low && m_txd)
+ m_rts = 0;
+ }
+
+ for (device_jvs_interface *intf = m_device; intf; intf = intf->m_jvs ? intf->m_jvs->m_device : nullptr)
+ if (intf->m_rts)
+ {
+ if (VERBOSE & LOG_CONFLICT)
+ conflict += util::string_format(",%s (%d)", intf->device().tag(), intf->m_txd);
+
+ if (FIX_CONFLICT && low && intf->m_txd)
+ intf->m_rts = 0;
+ }
+
+ LOGCONFLICT("%s\n", conflict);
+
+ if (FIX_CONFLICT && low)
+ high = 0;
+ }
+
+ int rxd = low && !high ? 0 : 1;
+ if (m_rxd != rxd)
+ {
+ m_rxd = rxd;
+
+ for (device_jvs_interface *intf = m_device; intf; intf = intf->m_jvs ? intf->m_jvs->m_device : nullptr)
+ if (!intf->m_rts)
+ intf->rxd(m_rxd);
+
+ if (!m_rts)
+ m_rxd_cb(m_rxd);
+
+ debug_rxd(m_rxd);
+ }
+
+ if (high + low == 0)
+ {
+ debug_flush();
+ debug_stop();
+ }
+}
+
+void jvs_port_device::debug_rxd(int state)
+{
+ if (!state && m_debug_rxd && m_debug_timer && m_debug_timer->expire().is_never())
+ m_debug_timer->adjust(attotime::from_hz(115200 * 2), 0, attotime::from_hz(115200));
+
+ m_debug_rxd = state;
+}
+
+void jvs_port_device::debug_flush(wchar_t ch)
+{
+ if (m_debug_index)
+ {
+ if (VERBOSE & LOG_PACKETS)
+ {
+ std::string debug;
+ debug += m_rts ? ">" : "<";
+ for (int i = 0; i < m_debug_index; i++)
+ debug += util::string_format(" %02x", m_debug_buffer[i]);
+ if (ch)
+ debug += ch;
+ debug += "\n";
+
+ LOGPACKETS("%s", debug);
+ }
+
+ std::fill_n(m_debug_buffer, m_debug_index, 0);
+ m_debug_index = 0;
+
+ debug_stop();
+ }
+}
+
+void jvs_port_device::debug_stop()
+{
+ if (m_debug_timer)
+ m_debug_timer->adjust(attotime::never);
+ m_debug_bit = -1;
+}
+
+TIMER_CALLBACK_MEMBER(jvs_port_device::debug_timer_callback)
+{
+ if (m_debug_bit < 0)
+ {
+ if (m_debug_rxd)
+ debug_stop();
+ else
+ m_debug_bit = 0;
+ }
+ else if (m_debug_bit == 8)
+ {
+ m_debug_index++;
+
+ if (!m_debug_rxd)
+ debug_flush('*');
+ else if (m_debug_index >= std::size(m_debug_buffer))
+ debug_flush('!');
+
+ debug_stop();
+ }
+ else
+ {
+ if (m_debug_rxd)
+ m_debug_buffer[m_debug_index] |= 1 << m_debug_bit;
+
+ m_debug_bit++;
+ }
+}
+
+device_jvs_interface::device_jvs_interface(const machine_config &mconfig, device_t &device) :
+ device_interface(device, "jvs"),
+ m_jvs(*this, "jvs"),
+ m_jvs_sense(jvs_port_device::sense::None),
+ m_default_inputs(true),
+ m_output_cb(*this),
+ m_analog_output_cb(*this),
+ m_system(*this, "SYSTEM"),
+ m_system_cb(*this, 0),
+ m_player(*this, "PLAYER%u", 1U),
+ m_player_cb(*this, 0),
+ m_coin(*this, "COIN%u", 1U),
+ m_coin_cb(*this, 0),
+ m_analog_input(*this, "ANALOG_INPUT%u", 1U),
+ m_analog_input_cb(*this, 0),
+ m_rotary_input(*this, "ROTARY_INPUT%u", 1U),
+ m_rotary_input_cb(*this, 0),
+ m_screen_position_enable(*this, "SCREEN_POSITION_INPUT_ENABLE%u", 1U),
+ m_screen_position_enable_cb(*this, 0xffff),
+ m_screen_position_x(*this, "SCREEN_POSITION_INPUT_X%u", 1U),
+ m_screen_position_x_cb(*this, 0),
+ m_screen_position_y(*this, "SCREEN_POSITION_INPUT_Y%u", 1U),
+ m_screen_position_y_cb(*this, 0),
+ m_rts(0),
+ m_sense(jvs_port_device::sense::None),
+ m_txd(1)
+{
+ m_port = dynamic_cast<jvs_port_device *>(device.owner());
+
+ m_root = m_port;
+ if (m_root)
+ {
+ device_jvs_interface *intf;
+ while (m_root->owner() && m_root->owner()->interface(intf) && m_root->owner()->owner())
+ m_root = dynamic_cast<jvs_port_device *>(m_root->owner()->owner());
+ }
+}
+
+void device_jvs_interface::interface_post_start()
+{
+ sense(jvs_port_device::sense::Uninitialized);
+
+ device().save_item(NAME(m_jvs_sense));
+ device().save_item(NAME(m_rts));
+ device().save_item(NAME(m_sense));
+ device().save_item(NAME(m_txd));
+}
+
+device_jvs_interface::~device_jvs_interface()
+{
+}
+
+void device_jvs_interface::rts(int state)
+{
+ if (m_rts != state)
+ {
+ if (device().machine().scheduler().currently_executing())
+ device().machine().scheduler().synchronize(timer_expired_delegate(FUNC(device_jvs_interface::update_rts), this), state);
+ else
+ update_rts(state);
+ }
+}
+
+void device_jvs_interface::update_rts(int32_t state)
+{
+ m_rts = state;
+
+ if (m_root)
+ {
+ if (m_rts && !m_root->m_rxd && !(FIX_CONFLICT && m_txd))
+ rxd(1);
+
+ m_root->update_rxd();
+ }
+}
+
+void device_jvs_interface::txd(int state)
+{
+ if (m_txd != state)
+ {
+ m_txd = state;
+
+ if (m_root)
+ m_root->update_rxd();
+ }
+}
+
+void device_jvs_interface::sense(uint8_t state)
+{
+ if (m_sense != state)
+ {
+ if (device().machine().scheduler().currently_executing())
+ device().machine().scheduler().synchronize(timer_expired_delegate(FUNC(device_jvs_interface::update_sense), this), state);
+ else
+ update_sense(state);
+ }
+}
+
+void device_jvs_interface::update_sense(int32_t state)
+{
+ m_sense = state;
+
+ if (m_port)
+ {
+#if VERBOSE & LOG_SIGNALS
+ device().
+ LOG_OUTPUT_FUNC("%s sense %s %d\n", device().tag(),
+ m_sense == jvs_port_device::sense::Initialized ? "Initialized" :
+ m_sense == jvs_port_device::sense::Uninitialized ? "Uninitialized" :
+ m_sense == jvs_port_device::sense::None ? "None" : "", state);
+#endif
+
+ device_jvs_interface *intf;
+ if (m_port->owner()->interface(intf))
+ intf->jvs_sense(m_sense);
+ else
+ m_port->m_sense_cb(m_sense);
+ }
+}
+
+uint8_t device_jvs_interface::system_r(uint8_t mem_mask)
+{
+ return (m_default_inputs ? m_system.read_safe(0) : m_system_cb(mem_mask)) & mem_mask;
+}
+
+uint32_t device_jvs_interface::player_r(offs_t offset, uint32_t mem_mask)
+{
+ return (m_default_inputs ? m_player[offset].read_safe(0) : m_player_cb[offset](0, mem_mask)) & mem_mask;
+}
+
+uint8_t device_jvs_interface::coin_r(offs_t offset, uint8_t mem_mask)
+{
+ return (m_default_inputs ? m_coin[offset].read_safe(0) : m_coin_cb[offset](0, mem_mask)) & mem_mask;
+}
+
+uint16_t device_jvs_interface::analog_input_r(offs_t offset, uint16_t mem_mask)
+{
+ return (m_default_inputs ? m_analog_input[offset].read_safe(0) : m_analog_input_cb[offset](0, mem_mask)) & mem_mask;
+}
+
+uint16_t device_jvs_interface::rotary_input_r(offs_t offset)
+{
+ return m_default_inputs ? m_rotary_input[offset].read_safe(0) : m_rotary_input_cb[offset]();
+}
+
+uint16_t device_jvs_interface::screen_position_enable_r(offs_t offset)
+{
+ return m_default_inputs ? m_screen_position_enable[offset].read_safe(0xffff) : m_screen_position_enable_cb[offset]();
+}
+
+uint16_t device_jvs_interface::screen_position_x_r(offs_t offset)
+{
+ return m_default_inputs ? m_screen_position_x[offset].read_safe(0) : m_screen_position_x_cb[offset]();
+}
+
+uint16_t device_jvs_interface::screen_position_y_r(offs_t offset)
+{
+ return m_default_inputs ? m_screen_position_y[offset].read_safe(0) : m_screen_position_y_cb[offset]();
+}
+
+void device_jvs_interface::add_jvs_port(machine_config &config)
+{
+ JVS_PORT(config, m_jvs, jvs_port_devices, nullptr);
+}
+
+void device_jvs_interface::jvs_sense(uint8_t state)
+{
+ m_jvs_sense = state;
+}
+
+#include "cyberlead.h"
+#include "namcoio.h"
+
+void jvs_port_devices(device_slot_interface &device)
+{
+ device.option_add("namco_amc", NAMCO_AMC);
+ device.option_add("namco_asca1", NAMCO_ASCA1);
+ device.option_add("namco_asca3", NAMCO_ASCA3);
+ device.option_add("namco_asca3a", NAMCO_ASCA3A);
+ device.option_add("namco_asca5", NAMCO_ASCA5);
+ device.option_add("namco_csz1", NAMCO_CSZ1);
+ device.option_add("namco_cyberlead", NAMCO_CYBERLEAD);
+ device.option_add("namco_cyberleada", NAMCO_CYBERLEADA);
+ device.option_add("namco_emio102", NAMCO_EMIO102);
+ device.option_add("namco_empri101", NAMCO_EMPRI101);
+ device.option_add("namco_fca10", NAMCO_FCA10);
+ device.option_add("namco_fca11", NAMCO_FCA11);
+ device.option_add("namco_fcb", NAMCO_FCB);
+ device.option_add("namco_tssio", NAMCO_TSSIO);
+ device.option_add("namco_xmiu1", NAMCO_XMIU1);
+}
diff --git a/src/devices/bus/jvs/jvs.h b/src/devices/bus/jvs/jvs.h
new file mode 100644
index 00000000000..66b62c26ba9
--- /dev/null
+++ b/src/devices/bus/jvs/jvs.h
@@ -0,0 +1,157 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+
+#ifndef MAME_BUS_JVS_JVS_H
+#define MAME_BUS_JVS_JVS_H
+
+#pragma once
+
+class device_jvs_interface;
+
+class jvs_port_device :
+ public device_t,
+ public device_single_card_slot_interface<device_jvs_interface>
+{
+ friend class device_jvs_interface;
+
+public:
+ template <typename T>
+ jvs_port_device(machine_config const &mconfig, char const *tag, device_t *owner, T &&opts, char const *dflt)
+ : jvs_port_device(mconfig, tag, owner, 0)
+ {
+ option_reset();
+ opts(*this);
+ set_default_option(dflt);
+ set_fixed(false);
+ }
+
+ jvs_port_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+ virtual ~jvs_port_device();
+
+ struct sense { enum : uint8_t
+ {
+ Initialized = 0, // 0V
+ Uninitialized = 1, // 2.5V
+ None = 2 // 5v
+ }; };
+
+ auto rxd() { return m_rxd_cb.bind(); }
+ auto sense() { return m_sense_cb.bind(); }
+
+ void txd(int state);
+ void rts(int state);
+
+protected:
+ // device_t
+ virtual void device_config_complete() override;
+ virtual void device_start() override ATTR_COLD;
+
+ device_jvs_interface *m_device;
+
+private:
+ void debug_rxd(int state);
+ void debug_flush(wchar_t ch = 0);
+ void debug_stop();
+ TIMER_CALLBACK_MEMBER(debug_timer_callback);
+
+ emu_timer *m_debug_timer;
+ int8_t m_debug_bit;
+ uint16_t m_debug_index;
+ int8_t m_debug_rxd;
+ uint8_t m_debug_buffer[0x102];
+
+ void update_rts(int32_t state);
+ void update_rxd();
+
+ devcb_write_line m_rxd_cb;
+ devcb_write8 m_sense_cb;
+ int8_t m_rts;
+ int8_t m_rxd;
+ int8_t m_txd;
+};
+
+class device_jvs_interface :
+ public device_interface
+{
+ friend class jvs_port_device;
+
+public:
+ virtual ~device_jvs_interface();
+
+ auto system() { m_default_inputs = false; return m_system_cb.bind(); }
+ template<unsigned N> auto player() { m_default_inputs = false; return m_player_cb[N].bind(); }
+ template<unsigned N> auto coin() { m_default_inputs = false; return m_coin_cb[N].bind(); }
+ template<unsigned N> auto analog_input() { m_default_inputs = false; return m_analog_input_cb[N].bind(); }
+ template<unsigned N> auto rotary_input() { m_default_inputs = false; return m_rotary_input_cb[N].bind(); }
+ template<unsigned N> auto screen_position_enable() { m_default_inputs = false; return m_screen_position_enable_cb[N].bind(); }
+ template<unsigned N> auto screen_position_x() { m_default_inputs = false; return m_screen_position_x_cb[N].bind(); }
+ template<unsigned N> auto screen_position_y() { m_default_inputs = false; return m_screen_position_y_cb[N].bind(); }
+ auto output() { return m_output_cb.bind(); }
+ template<unsigned N> auto analog_output() { return m_analog_output_cb[N].bind(); }
+
+ auto jvs() const { if (m_jvs.target()) return m_jvs.target(); return device().subdevice<jvs_port_device>(m_jvs.finder_tag()); }
+
+protected:
+ device_jvs_interface(const machine_config &mconfig, device_t &device);
+
+ // device_interface
+ virtual void interface_post_start() override;
+
+ virtual void rxd(int state) = 0;
+
+ void rts(int state);
+ void txd(int state);
+ void sense(uint8_t state);
+ virtual void jvs_sense(uint8_t state);
+
+ void add_jvs_port(machine_config &config);
+
+ uint8_t system_r(uint8_t mem_mask);
+ uint32_t player_r(offs_t offset, uint32_t mem_mask);
+ uint8_t coin_r(offs_t offset, uint8_t mem_mask);
+ uint16_t analog_input_r(offs_t offset, uint16_t mem_mask);
+ uint16_t rotary_input_r(offs_t offset);
+ uint16_t screen_position_enable_r(offs_t offset);
+ uint16_t screen_position_x_r(offs_t offset);
+ uint16_t screen_position_y_r(offs_t offset);
+
+ optional_device<jvs_port_device> m_jvs;
+ uint8_t m_jvs_sense;
+
+ bool m_default_inputs;
+ devcb_write64 m_output_cb;
+ devcb_write16::array<8> m_analog_output_cb;
+
+private:
+ optional_ioport m_system;
+ devcb_read8 m_system_cb;
+ optional_ioport_array<8> m_player;
+ devcb_read32::array<8> m_player_cb;
+ optional_ioport_array<8> m_coin;
+ devcb_read8::array<8> m_coin_cb;
+ optional_ioport_array<8> m_analog_input;
+ devcb_read16::array<8> m_analog_input_cb;
+ optional_ioport_array<8> m_rotary_input;
+ devcb_read16::array<8> m_rotary_input_cb;
+ optional_ioport_array<8> m_screen_position_enable;
+ devcb_read16::array<8> m_screen_position_enable_cb;
+ optional_ioport_array<8> m_screen_position_x;
+ devcb_read16::array<8> m_screen_position_x_cb;
+ optional_ioport_array<8> m_screen_position_y;
+ devcb_read16::array<8> m_screen_position_y_cb;
+
+ jvs_port_device *m_port;
+ jvs_port_device *m_root;
+ void update_rts(int32_t state);
+ void update_sense(int32_t state);
+
+ int8_t m_rts;
+ uint8_t m_sense;
+ int8_t m_txd;
+};
+
+DECLARE_DEVICE_TYPE(JVS_PORT, jvs_port_device)
+
+void jvs_port_devices(device_slot_interface &device);
+
+#endif // MAME_BUS_JVS_JVS_H
diff --git a/src/devices/bus/jvs/jvshle.cpp b/src/devices/bus/jvs/jvshle.cpp
new file mode 100644
index 00000000000..50ef59402c1
--- /dev/null
+++ b/src/devices/bus/jvs/jvshle.cpp
@@ -0,0 +1,868 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+
+#include "emu.h"
+#include "jvshle.h"
+#include <algorithm>
+
+#define LOG_COMMAND (1U << 1)
+#define LOG_ERROR (1U << 2)
+#define LOG_OUTPUT (1U << 3)
+
+//#define VERBOSE (/*LOG_COMMAND | */LOG_ERROR | LOG_OUTPUT)
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
+
+#define LOGCOMMAND(...) LOGMASKED(LOG_COMMAND, __VA_ARGS__)
+#define LOGERROR(...) LOGMASKED(LOG_ERROR, __VA_ARGS__)
+#define LOGOUTPUT(...) LOGMASKED(LOG_OUTPUT, __VA_ARGS__)
+
+namespace {
+
+struct State { enum : uint8_t
+{
+ Sync,
+ Escape,
+ EscapedData,
+ Data
+}; };
+
+static constexpr uint8_t Sync = 0xe0;
+static constexpr uint8_t Escape = 0xd0;
+
+} // anonymous namespace
+
+jvs_hle_device::jvs_hle_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, type, tag, owner, clock),
+ device_jvs_interface(mconfig, *this),
+ m_rxd(1),
+ m_output(0),
+ m_analog_output{},
+ m_analog_output_channels(0)
+{
+}
+
+void jvs_hle_device::device_start()
+{
+ m_rx_timer = timer_alloc(FUNC(jvs_hle_device::rx_timer_callback), this);
+ m_tx_timer = timer_alloc(FUNC(jvs_hle_device::tx_timer_callback), this);
+
+ m_coin_timer = timer_alloc(FUNC(jvs_hle_device::coin_timer_callback), this);
+
+ m_mainid.resize(100);
+ m_mainid_length = 0;
+ m_retransmit_buffer.resize(0x102);
+ m_retransmit_length = 3;
+ m_rx_bit = -1;
+ m_rx_buffer.resize(0x102);
+ m_rx_length = 0;
+ m_rx_state = State::Sync;
+ m_tx_bit = -1;
+ m_tx_buffer.resize(0x102);
+ m_tx_index = 0;
+ m_tx_length = 0;
+ m_tx_state = State::Sync;
+
+ save_item(NAME(m_address));
+ save_item(NAME(m_mainid));
+ save_item(NAME(m_mainid_length));
+ save_item(NAME(m_retransmit_buffer));
+ save_item(NAME(m_retransmit_length));
+ save_item(NAME(m_rx_bit));
+ save_item(NAME(m_rx_buffer));
+ save_item(NAME(m_rx_index));
+ save_item(NAME(m_rx_length));
+ save_item(NAME(m_rx_state));
+ save_item(NAME(m_rxd));
+ save_item(NAME(m_tx_bit));
+ save_item(NAME(m_tx_buffer));
+ save_item(NAME(m_tx_index));
+ save_item(NAME(m_tx_length));
+ save_item(NAME(m_tx_safe_length));
+ save_item(NAME(m_tx_state));
+
+ save_item(NAME(m_player_count));
+ save_item(NAME(m_switch_count));
+ save_item(NAME(m_switch_mask));
+ save_item(NAME(m_coin_counter));
+ save_item(NAME(m_coin_offset));
+ save_item(NAME(m_coin_prev));
+ save_item(NAME(m_analog_input_channels));
+ save_item(NAME(m_analog_input_bits));
+ save_item(NAME(m_rotary_input_channels));
+ save_item(NAME(m_screen_position_input_channels));
+ save_item(NAME(m_screen_position_input_xbits));
+ save_item(NAME(m_screen_position_input_ybits));
+ save_item(NAME(m_output));
+ save_item(NAME(m_output_mask));
+ save_item(NAME(m_output_slots));
+ save_item(NAME(m_analog_output));
+ save_item(NAME(m_analog_output_channels));
+}
+
+void jvs_hle_device::device_reset()
+{
+ rts(0);
+ sense(jvs_port_device::sense::Uninitialized);
+ rx_stop();
+ rx_clear();
+
+ std::fill_n(m_mainid.begin(), m_mainid_length, 0);
+ m_mainid_length = 0;
+ m_address = 0xff;
+ m_retransmit_buffer[0] = 0x00; // destination
+ m_retransmit_buffer[2] = StatusCode::Normal;
+ std::fill_n(m_retransmit_buffer.begin() + 3, m_retransmit_length - 3, 0);
+ m_retransmit_length = 3;
+ m_rx_index = 0;
+
+ m_player_count = player_count();
+ m_switch_count = switch_count();
+ m_switch_mask = switch_mask();
+ m_coin_slots = coin_slots();
+ m_coin_offset = coin_offset();
+
+ m_coin_counter.fill(0);
+ attotime const coin_timer_rate = m_coin_slots ? attotime::from_hz(120) : attotime::never;
+ m_coin_timer->adjust(coin_timer_rate, 0, coin_timer_rate);
+ m_coin_prev.fill(0xff);
+
+ m_analog_input_channels = analog_input_channels();
+ m_analog_input_bits = analog_input_bits();
+ m_rotary_input_channels = rotary_input_channels();
+ m_screen_position_input_channels = screen_position_input_channels();
+ m_screen_position_input_xbits = screen_position_input_xbits();
+ m_screen_position_input_xbits = screen_position_input_ybits();
+
+ m_output_slots = output_slots();
+ m_output_mask = jvs_bitmask<uint64_t>(m_output_slots);
+ update_output(0);
+
+ for (int i = 0; i < m_analog_output_channels; i++)
+ update_analog_output(i, 0);
+
+ m_analog_output_channels = analog_output_channels();
+}
+
+const char *jvs_hle_device::device_id()
+{
+ return "";
+}
+
+uint8_t jvs_hle_device::command_revision()
+{
+ return 0x13;
+}
+
+uint8_t jvs_hle_device::jvs_revision()
+{
+ return 0x30;
+}
+
+uint8_t jvs_hle_device::communication_revision()
+{
+ return 0x10;
+}
+
+void jvs_hle_device::feature_check()
+{
+ uint8_t *response;
+ if (m_player_count && m_switch_count && produce(4, response))
+ {
+ response[0] = FunctionCode::SwitchInput;
+ response[1] = m_player_count;
+ response[2] = m_switch_count;
+ response[3] = 0;
+ }
+
+ if (m_coin_slots && produce(4, response))
+ {
+ response[0] = FunctionCode::CoinInput;
+ response[1] = m_coin_slots;
+ response[2] = 0;
+ response[3] = 0;
+ }
+
+ if (m_analog_input_channels && produce(4, response))
+ {
+ response[0] = FunctionCode::AnalogInput;
+ response[1] = m_analog_input_channels;
+ response[2] = m_analog_input_bits;
+ response[3] = 0;
+ }
+
+ if (m_rotary_input_channels && produce(4, response))
+ {
+ response[0] = FunctionCode::RotaryInput;
+ response[1] = m_rotary_input_channels;
+ response[2] = 0;
+ response[3] = 0;
+ }
+
+ if (m_screen_position_input_channels && produce(4, response))
+ {
+ response[0] = FunctionCode::ScreenPositionInput;
+ response[1] = m_screen_position_input_xbits;
+ response[2] = m_screen_position_input_ybits;
+ response[3] = m_screen_position_input_channels;
+ }
+
+ if (m_output_slots && produce(4, response))
+ {
+ response[0] = FunctionCode::GeneralPurposeOutput;
+ response[1] = m_output_slots;
+ response[2] = 0;
+ response[3] = 0;
+ }
+
+ if (m_analog_output_channels && produce(4, response))
+ {
+ response[0] = FunctionCode::AnalogOutput;
+ response[1] = m_analog_output_channels;
+ response[2] = 0;
+ response[3] = 0;
+ }
+}
+
+void jvs_hle_device::execute(uint8_t command)
+{
+ uint8_t const *parameters;
+ uint8_t *response;
+
+ switch (command)
+ {
+ case CommandCode::Reset:
+ if (consume(1, parameters) && parameters[0] == 0xd9)
+ device_reset();
+ break;
+
+ case CommandCode::SetAddress:
+ if (consume(1, parameters) && m_jvs_sense != jvs_port_device::sense::Uninitialized && m_address == 0xff &&
+ produce(1, response))
+ {
+ m_address = parameters[0];
+ response[0] = ReportCode::Normal;
+ }
+ break;
+
+ case CommandCode::IOIDENT:
+ {
+ const char *id = device_id();
+ int len = strlen(id) + 1;
+ if (produce(1 + len, response))
+ {
+ response[0] = ReportCode::Normal;
+ memcpy(&response[1], id, len);
+ }
+ break;
+ }
+
+ case CommandCode::CMDREV:
+ if (produce(2, response))
+ {
+ response[0] = ReportCode::Normal;
+ response[1] = command_revision();
+ }
+ break;
+
+ case CommandCode::JVSREV:
+ if (produce(2, response))
+ {
+ response[0] = ReportCode::Normal;
+ response[1] = jvs_revision();
+ }
+ break;
+
+ case CommandCode::COMMVER:
+ if (produce(2, response))
+ {
+ response[0] = ReportCode::Normal;
+ response[1] = communication_revision();
+ }
+ break;
+
+ case CommandCode::FEATCHK:
+ if (produce(1, response))
+ {
+ response[0] = ReportCode::Normal;
+ feature_check();
+ if (produce(1, response))
+ response[0] = 0x00;
+ }
+ break;
+
+ case CommandCode::MAINID:
+ if (consume(1, parameters))
+ {
+ uint8_t const *mainid = parameters;
+ size_t const size = m_rx_length - m_rx_index;
+ size_t length = 0;
+ while (mainid[length] && length < size)
+ length++;
+
+ if (consume(length, parameters))
+ {
+ if (length < size && length < m_mainid.size())
+ {
+ if (produce(1, response))
+ {
+ std::copy_n(mainid, length, m_mainid.begin());
+ if (m_mainid_length > length)
+ std::fill_n(&m_mainid[length], m_mainid_length - length, 0);
+ m_mainid_length = length;
+
+ //std::string s(m_mainid.begin(), m_mainid.begin() + length);
+ //osd_printf_info("mainid = %s\n", s);
+
+ response[0] = ReportCode::Normal;
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ }
+ break;
+
+ case CommandCode::SWINP:
+ if (consume(2, parameters))
+ {
+ uint8_t const players = parameters[0];
+ uint8_t const bytes = parameters[1];
+
+ if (players <= m_player_count && bytes <= (m_switch_count + 7) / 8)
+ {
+ if (produce((players * bytes) + 2, response))
+ {
+ response[0] = ReportCode::Normal;
+ response[1] = system_r(0xff);
+
+ uint32_t mask = m_switch_mask & jvs_bitmask<uint32_t>(bytes * 8);
+ for (int i = 0; i < players; i++)
+ {
+ uint32_t val = player_r(i, mask);
+ for (int j = 0; j < bytes; j++)
+ response[(i * bytes) + j + 2] = val >> (j * 8);
+ }
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ break;
+
+ case CommandCode::COININP:
+ if (consume(1, parameters))
+ {
+ uint8_t const count = parameters[0];
+
+ if (count <= m_coin_slots)
+ {
+ if (produce((count * 2) + 1, response))
+ {
+ response[0] = ReportCode::Normal;
+
+ for (int i = 0; i < count; i++)
+ {
+ response[(i * 2) + 1] = (m_coin_counter[i] >> 8) & 0x3f;
+ response[(i * 2) + 2] = m_coin_counter[i];
+ }
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ break;
+
+ case CommandCode::ANLINP:
+ if (consume(1, parameters))
+ {
+ uint8_t const count = parameters[0];
+
+ if (count <= m_analog_input_channels)
+ {
+ if (produce((count * 2) + 1, response))
+ {
+ uint16_t const analog_input_mask = m_analog_input_bits > 0 && m_analog_input_bits < 16 ? make_bitmask<uint16_t>(m_analog_input_bits) << (16 - m_analog_input_bits) : 0xffff;
+
+ response[0] = ReportCode::Normal;
+
+ for (int i = 0; i < count; i++)
+ {
+ uint16_t const analog_input = analog_input_r(i, analog_input_mask);
+ response[(i * 2) + 1] = analog_input >> 8;
+ response[(i * 2) + 2] = analog_input;
+ }
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ break;
+
+ case CommandCode::ROTINP:
+ if (consume(1, parameters))
+ {
+ uint8_t const count = parameters[0];
+
+ if (count <= m_rotary_input_channels)
+ {
+ if (produce((count * 2) + 1, response))
+ {
+ response[0] = ReportCode::Normal;
+
+ for (int i = 0; i < count; i++)
+ {
+ uint16_t const rotary_input = rotary_input_r(i);
+ response[(i * 2) + 1] = rotary_input >> 8;
+ response[(i * 2) + 2] = rotary_input;
+ }
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ break;
+
+ case CommandCode::SCRPOSINP:
+ if (consume(1, parameters))
+ {
+ uint8_t const index = parameters[0] - 1;
+ if (index < m_screen_position_input_channels)
+ {
+ if (produce(5, response))
+ {
+ uint16_t const enable = screen_position_enable_r(index);
+ uint16_t const x = enable ? screen_position_x_r(index) : 0xffff;
+ uint16_t const y = enable ? screen_position_y_r(index) : 0xffff;
+ response[0] = ReportCode::Normal;
+ response[1] = x >> 8;
+ response[2] = x;
+ response[3] = y >> 8;
+ response[4] = y;
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ break;
+
+ case CommandCode::RETRANSMIT:
+ std::copy_n(m_retransmit_buffer.begin(), m_retransmit_length, m_tx_buffer.begin());
+ m_tx_length = m_retransmit_length;
+ break;
+
+ case CommandCode::COINDEC:
+ if (consume(3, parameters))
+ {
+ uint8_t const index = parameters[0] - m_coin_offset;
+ uint16_t const amount = (parameters[1] << 8) | parameters[2];
+
+ if (index < m_coin_slots)
+ {
+ if (produce(1, response))
+ {
+ m_coin_counter[index] = (m_coin_counter[index] < amount) ? 0 : m_coin_counter[index] - amount;
+ response[0] = ReportCode::Normal;
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ break;
+
+ case CommandCode::OUTPUT1:
+ if (consume(1, parameters))
+ {
+ uint8_t const count = parameters[0];
+
+ if (consume(count, parameters))
+ {
+ if (count <= (m_output_slots + 7) / 8)
+ {
+ if (produce(1, response))
+ {
+ uint64_t output = m_output & ~jvs_bitmask<uint64_t>(count * 8);
+ for (int i = 0; i < count; i++)
+ output |= (uint64_t)parameters[i] << (i * 8);
+
+ update_output(output);
+ response[0] = ReportCode::Normal;
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ }
+ break;
+
+ case CommandCode::ANLOUT:
+ if (consume(1, parameters))
+ {
+ uint8_t const count = parameters[0];
+
+ if (consume(count * 2, parameters))
+ {
+ if (count <= m_analog_output_channels)
+ {
+ if (produce(1, response))
+ {
+ for (int i = 0; i < count; i++)
+ update_analog_output(i, (parameters[i * 2] << 8) | parameters[(i * 2) + 1]);
+
+ response[0] = ReportCode::Normal;
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ }
+ break;
+
+ case CommandCode::COININC:
+ if (consume(3, parameters))
+ {
+ uint8_t const index = parameters[0] - m_coin_offset;
+ uint16_t const amount = (parameters[1] << 8) | parameters[2];
+
+ if (index < m_coin_slots)
+ {
+ if (produce(1, response))
+ {
+ m_coin_counter[index] = (m_coin_counter[index] + amount) & 0x3fff;
+ response[0] = ReportCode::Normal;
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ break;
+
+ case CommandCode::OUTPUT2:
+ if (consume(2, parameters))
+ {
+ uint8_t const index = parameters[0] - 1;
+ uint8_t const output = parameters[1];
+
+ if (index < (m_output_slots + 7) / 8)
+ {
+ if (produce(1, response))
+ {
+ update_output((m_output & ~(0xffULL << (index * 8))) | ((uint64_t)output << (index * 8)));
+ response[0] = ReportCode::Normal;
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ break;
+
+ case CommandCode::OUTPUT3:
+ if (consume(2, parameters))
+ {
+ uint8_t const index = parameters[0];
+ uint8_t const output = parameters[1];
+
+ if (index < m_output_slots && output <= 2)
+ {
+ if (produce(1, response))
+ {
+ if (output == 2 || BIT(m_output, index ^ 7) != output)
+ update_output(m_output ^ (1ULL << (index ^ 7)));
+
+ response[0] = ReportCode::Normal;
+ }
+ }
+ else if (produce(1, response))
+ response[0] = ReportCode::InvalidParameter;
+ }
+ break;
+
+ default:
+ LOGERROR("unknown JVS command % 02x\n", command);
+ status_code(StatusCode::UnknownCommand);
+ break;
+ }
+}
+
+void jvs_hle_device::status_code(uint8_t status_code)
+{
+ m_tx_buffer[2] = status_code;
+}
+
+bool jvs_hle_device::consume(int size, uint8_t const *&parameters)
+{
+ if (m_rx_index + size < m_rx_length)
+ {
+ parameters = &m_rx_buffer[m_rx_index];
+ m_rx_index += size;
+ return true;
+ }
+
+ uint8_t *response;
+ if (produce(1, response))
+ response[0] = ReportCode::IncorrectNumberOfParameters;
+
+ m_rx_index = m_rx_length - 1;
+ parameters = nullptr;
+ return false;
+}
+
+bool jvs_hle_device::produce(int size, uint8_t *&response)
+{
+ if (m_tx_length + size >= std::size(m_tx_buffer))
+ {
+ status_code(StatusCode::AcknowledgeOverflow);
+ m_tx_length = m_tx_safe_length;
+ }
+
+ if (m_tx_buffer[2] == StatusCode::Normal)
+ {
+ response = &m_tx_buffer[m_tx_length];
+ m_tx_length += size;
+ return true;
+ }
+
+ response = nullptr;
+ return false;
+}
+
+void jvs_hle_device::packet_received()
+{
+ m_tx_bit = -1;
+ std::fill_n(m_tx_buffer.begin(), m_tx_length, 0);
+ m_tx_index = 0;
+ m_tx_length = 0;
+
+ if (m_rx_length >= 4 && (m_rx_buffer[0] == 0xff || m_rx_buffer[0] == m_address))
+ {
+ m_tx_buffer[0] = 0x00; // destination
+
+ uint8_t checksum = std::accumulate(m_rx_buffer.begin(), m_rx_buffer.begin() + m_rx_length - 1, 0);
+ if (m_rx_buffer[m_rx_length - 1] != checksum)
+ {
+ LOGERROR("jvs checksum %02x expected %02x\n", checksum, m_rx_buffer[m_rx_length - 1]);
+
+ if (m_rx_buffer[0] != 0xff)
+ {
+ status_code(StatusCode::ChecksumError);
+ m_tx_length = 3;
+ }
+ }
+ else
+ {
+ if (VERBOSE & LOG_COMMAND)
+ {
+ std::string jvsin;
+ for (int i = 0; i < m_rx_length; i++)
+ jvsin += util::string_format(" %02x", m_rx_buffer[i]);
+
+ LOGCOMMAND("jvs in %s\n", jvsin);
+ }
+
+ status_code(StatusCode::Normal);
+
+ m_rx_index = 2;
+ m_tx_length = 3;
+
+ while (m_tx_buffer[2] == StatusCode::Normal && m_rx_index < m_rx_length - 1)
+ {
+ m_tx_safe_length = m_tx_length;
+ execute(m_rx_buffer[m_rx_index++]);
+ }
+
+ if (m_tx_length == 3 && m_tx_buffer[2] == StatusCode::Normal)
+ m_tx_length = 0;
+ else
+ {
+ std::copy_n(m_tx_buffer.begin(), m_tx_length, m_retransmit_buffer.begin());
+ m_retransmit_length = m_tx_length;
+ }
+ }
+
+ if (m_tx_length)
+ {
+ m_tx_buffer[1] = m_tx_length - 1;
+ uint8_t checksum = std::accumulate(m_tx_buffer.begin(), m_tx_buffer.begin() + m_tx_length, 0);
+ m_tx_buffer[m_tx_length++] = checksum;
+
+ if (VERBOSE & LOG_COMMAND)
+ {
+ std::string jvsout;
+ for (int i = 0; i < m_tx_length; i++)
+ jvsout += util::string_format(" %02x", m_tx_buffer[i]);
+
+ LOGCOMMAND("jvs out %s\n", jvsout);
+ }
+
+ m_tx_timer->adjust(attotime::from_hz(115200) * 10, 0, attotime::from_hz(115200));
+ }
+ }
+
+ rx_clear();
+}
+
+void jvs_hle_device::rxd(int state)
+{
+ if (m_rxd && !state && m_rx_timer && m_rx_timer->expire().is_never())
+ m_rx_timer->adjust(attotime::from_hz(115200) / 2, 0, attotime::from_hz(115200));
+
+ m_rxd = state;
+}
+
+TIMER_CALLBACK_MEMBER(jvs_hle_device::rx_timer_callback)
+{
+ if (m_rx_bit < 0)
+ {
+ if (m_rxd)
+ rx_stop();
+ else
+ m_rx_bit = 0;
+ }
+ else if (m_rx_bit < 8)
+ {
+ if (m_rxd)
+ m_rx_buffer[m_rx_length] |= 1 << m_rx_bit;
+
+ m_rx_bit++;
+ }
+ else
+ {
+ m_rx_length++;
+
+ if (!m_rxd)
+ rx_clear(); // frame error
+ else if (m_rx_buffer[m_rx_length - 1] == Sync)
+ {
+ rx_clear();
+ m_rx_state = State::Data;
+ }
+ else if (m_rx_state == State::Sync)
+ rx_discard();
+ else if (m_rx_buffer[m_rx_length - 1] == Escape)
+ {
+ rx_discard();
+ m_rx_state = State::EscapedData;
+ }
+ else
+ {
+ if (m_rx_state == State::EscapedData)
+ {
+ m_rx_buffer[m_rx_length - 1]++;
+ m_rx_state = State::Data;
+ }
+
+ if (m_rx_length >= 2 && m_rx_length == m_rx_buffer[1] + 2)
+ packet_received();
+ else if (m_rx_length == m_rx_buffer.size())
+ rx_clear();
+ }
+
+ rx_stop();
+ }
+}
+
+TIMER_CALLBACK_MEMBER(jvs_hle_device::tx_timer_callback)
+{
+ if (m_tx_bit < 0)
+ {
+ if (m_tx_index >= m_tx_length)
+ {
+ m_tx_timer->adjust(attotime::never);
+ m_tx_state = State::Sync;
+ rts(0);
+ sense(jvs_port_device::sense::Initialized);
+ }
+ else
+ {
+ if (m_tx_index == 0)
+ rts(1);
+ txd(0);
+ m_tx_bit = 0;
+ }
+ }
+ else if (m_tx_bit < 8)
+ {
+ uint8_t data = (m_tx_state == State::Sync) ? Sync :
+ (m_tx_state == State::Escape) ? Escape :
+ (m_tx_state == State::EscapedData) ? m_tx_buffer[m_tx_index] - 1 :
+ m_tx_buffer[m_tx_index];
+
+ txd(BIT(data, m_tx_bit));
+ m_tx_bit++;
+ }
+ else
+ {
+ txd(1);
+ m_tx_bit = -1;
+
+ if (m_tx_state == State::Escape)
+ m_tx_state = State::EscapedData;
+ else
+ {
+ if (m_tx_state != State::Sync)
+ m_tx_index++;
+
+ if (m_tx_index < m_tx_length && (m_tx_buffer[m_tx_index] == Sync || m_tx_buffer[m_tx_index] == Escape))
+ m_tx_state = State::Escape;
+ else
+ m_tx_state = State::Data;
+ }
+ }
+}
+
+TIMER_CALLBACK_MEMBER(jvs_hle_device::coin_timer_callback)
+{
+ for (int i = 0; i < m_coin_slots; i++)
+ {
+ uint8_t coin = coin_r(i, 0x01);
+ if (BIT(coin & ~m_coin_prev[i], 0))
+ m_coin_counter[i] = (m_coin_counter[i] + 1) & 0x3fff;
+
+ m_coin_prev[i] = coin;
+ }
+}
+
+void jvs_hle_device::rx_clear()
+{
+ std::fill_n(m_rx_buffer.begin(), m_rx_length, 0);
+ m_rx_length = 0;
+ m_rx_state = State::Sync;
+}
+
+void jvs_hle_device::rx_discard()
+{
+ m_rx_buffer[--m_rx_length] = 0;
+}
+
+void jvs_hle_device::rx_stop()
+{
+ m_rx_timer->adjust(attotime::never);
+ m_rx_bit = -1;
+}
+
+uint32_t jvs_hle_device::switch_mask()
+{
+ return jvs_bitmask<uint32_t>(switch_count());
+}
+
+void jvs_hle_device::update_output(uint64_t output)
+{
+ output &= m_output_mask;
+
+ if (m_output != output)
+ {
+ LOGOUTPUT("update_output %0*llx\n", (m_output_slots + 3) / 4, output);
+ m_output_cb(0, output, output ^ m_output);
+ m_output = output;
+ }
+}
+
+void jvs_hle_device::update_analog_output(int offset, uint16_t output)
+{
+ if (m_analog_output[offset] != output)
+ {
+ LOGOUTPUT("update_fanalog_output %d %04x\n", offset, output);
+ m_analog_output_cb[offset](0, output, 0xffff);
+ m_analog_output[offset] = output;
+ }
+}
diff --git a/src/devices/bus/jvs/jvshle.h b/src/devices/bus/jvs/jvshle.h
new file mode 100644
index 00000000000..67f7b9fc859
--- /dev/null
+++ b/src/devices/bus/jvs/jvshle.h
@@ -0,0 +1,175 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+#ifndef MAME_BUS_JVS_JVSDEV_H
+#define MAME_BUS_JVS_JVSDEV_H
+
+#pragma once
+
+#include "jvs.h"
+
+class jvs_hle_device :
+ public device_t,
+ public device_jvs_interface
+{
+protected:
+ jvs_hle_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ // device_t
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+ // device_jvs_interface
+ virtual void rxd(int state) override;
+
+ virtual const char *device_id();
+ virtual uint8_t command_revision();
+ virtual uint8_t jvs_revision();
+ virtual uint8_t communication_revision();
+ virtual uint8_t player_count() { return 0; }
+ virtual uint8_t switch_count() { return 0; }
+ virtual uint32_t switch_mask();
+ virtual uint8_t coin_slots() { return 0; }
+ virtual uint8_t coin_offset() { return 1; }
+ virtual uint8_t analog_input_channels() { return 0; }
+ virtual uint8_t analog_input_bits() { return 0; }
+ virtual uint8_t rotary_input_channels() { return 0; }
+ virtual uint8_t screen_position_input_channels() { return 0; }
+ virtual uint8_t screen_position_input_xbits() { return 0; }
+ virtual uint8_t screen_position_input_ybits() { return 0; }
+ virtual uint8_t output_slots() { return 0; }
+ virtual uint8_t analog_output_channels() { return 0; }
+ virtual void feature_check();
+ virtual void execute(uint8_t command);
+ void status_code(uint8_t status_code);
+ bool consume(int size, const uint8_t *&parameters);
+ bool produce(int size, uint8_t *&response);
+
+ template<typename T, typename U> constexpr T jvs_bitmask(U n)
+ {
+ return make_bitmask<T>((n + 7) & ~7) ^ (make_bitmask<T>((-n) & 7) << (n & ~7));
+ }
+
+ struct CommandCode { enum : uint8_t
+ {
+ Reset = 0xf0,
+ SetAddress = 0xf1,
+ CommMethodChange = 0xf2,
+
+ IOIDENT = 0x10,
+ CMDREV = 0x11,
+ JVSREV = 0x12,
+ COMMVER = 0x13,
+ FEATCHK = 0x14,
+ MAINID = 0x15,
+
+ SWINP = 0x20,
+ COININP = 0x21,
+ ANLINP = 0x22,
+ ROTINP = 0x23,
+ KEYINP = 0x24,
+ SCRPOSINP = 0x25,
+ MISCSWINP = 0x26,
+
+ PAYCNT = 0x2e,
+ RETRANSMIT = 0x2f,
+ COINDEC = 0x30,
+ PAYINC = 0x31,
+ OUTPUT1 = 0x32,
+ ANLOUT = 0x33,
+ CHAROUT = 0x34,
+ COININC = 0x35,
+ PAYDEC = 0x36,
+ OUTPUT2 = 0x37,
+ OUTPUT3 = 0x38,
+ }; };
+
+ struct StatusCode { enum : uint8_t
+ {
+ Normal = 0x01,
+ UnknownCommand = 0x02,
+ ChecksumError = 0x03,
+ AcknowledgeOverflow = 0x04,
+ }; };
+
+ struct ReportCode { enum : uint8_t
+ {
+ Normal = 0x01,
+ IncorrectNumberOfParameters = 0x02,
+ InvalidParameter = 0x03,
+ Busy = 0x04,
+ }; };
+
+ struct FunctionCode { enum : uint8_t
+ {
+ End = 0x00,
+
+ SwitchInput = 0x01,
+ CoinInput = 0x02,
+ AnalogInput = 0x03,
+ RotaryInput = 0x04,
+ KeycodeInput = 0x05,
+ ScreenPositionInput = 0x06,
+ MiscSwitchInput = 0x07,
+
+ CardSystem = 0x10,
+ MedalHopper = 0x11,
+ GeneralPurposeOutput = 0x12,
+ AnalogOutput = 0x13,
+ CharacterOutput = 0x14,
+ Backup = 0x15,
+ }; };
+
+private:
+ void packet_received();
+ TIMER_CALLBACK_MEMBER(rx_timer_callback);
+ TIMER_CALLBACK_MEMBER(tx_timer_callback);
+ TIMER_CALLBACK_MEMBER(coin_timer_callback);
+ void rx_clear();
+ void rx_discard();
+ void rx_stop();
+ void update_output(uint64_t output);
+ void update_analog_output(int offset, uint16_t output);
+
+ emu_timer *m_tx_timer;
+ emu_timer *m_rx_timer;
+ emu_timer *m_coin_timer;
+
+ uint8_t m_address;
+ std::vector<uint8_t> m_mainid;
+ uint8_t m_mainid_length;
+ std::vector<uint8_t> m_retransmit_buffer;
+ uint16_t m_retransmit_length;
+ uint16_t m_rx_index;
+ int32_t m_rx_bit;
+ std::vector<uint8_t> m_rx_buffer;
+ int32_t m_rx_length;
+ uint8_t m_rx_state;
+ int32_t m_rxd;
+ int32_t m_tx_bit;
+ std::vector<uint8_t> m_tx_buffer;
+ int32_t m_tx_index;
+ uint32_t m_tx_length;
+ uint32_t m_tx_safe_length;
+ uint8_t m_tx_state;
+
+ uint8_t m_player_count;
+ uint8_t m_switch_count;
+ uint32_t m_switch_mask;
+ uint8_t m_coin_slots;
+ std::array<uint16_t, 8> m_coin_counter;
+ uint8_t m_coin_offset;
+ std::array<uint8_t, 8> m_coin_prev;
+ uint8_t m_analog_input_channels;
+ uint8_t m_analog_input_bits;
+ uint8_t m_rotary_input_channels;
+ uint8_t m_screen_position_input_channels;
+ uint8_t m_screen_position_input_xbits;
+ uint8_t m_screen_position_input_ybits;
+ uint64_t m_output;
+ uint64_t m_output_mask;
+ uint8_t m_output_slots;
+ std::array<uint16_t, 8> m_analog_output;
+ uint8_t m_analog_output_channels;
+};
+
+#endif // MAME_BUS_JVS_JVSDEV_H
diff --git a/src/devices/bus/jvs/namcoio.cpp b/src/devices/bus/jvs/namcoio.cpp
new file mode 100644
index 00000000000..fc9580423ff
--- /dev/null
+++ b/src/devices/bus/jvs/namcoio.cpp
@@ -0,0 +1,1564 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+/*
+Namco I/O PCBs
+--------------
+
+ASCA-1A PCB
+8662968202 (8662978202)
+|--------------------------------------------------------|
+|J105 J104 J103 J102 J101 |
+| NJM2904 |-------| |
+| ADM485 |ALTERA | |
+| NJM2904 |EPM7064| |
+| | | |
+| |-------| |-------| |
+| |---| | | |
+| IC13| | C78 | SW2 LB1235 LB1233 |
+| |---| | | 14.7460MHz LB1235 |
+| JP1|-------| |
+| 62256 PST592D |
+| MB87078 J106 |
+|--------------------------------------------------------|
+Notes:
+ IC13 - Atmel AT29C020 2MBit EEPROM labelled 'ASC1IO-A' (PLCC32)
+ C78 - Namco Custom MCU, positively identified as a Hitachi H8/3334 (PLCC84)
+ Clock input 14.7460MHz
+ EPM7064 - Altera EPM7064LC68-15 CPLD, labelled 'ASCA DR0' (PLCC68)
+ PST592D - System Reset IC (SOIC4)
+ 62256 - Hitachi HM62256 32kB x8-bit SRAM
+ ADM485 - Analog Devices +ADM485 5V Low Power EIA RS-485 Transceiver (SOIC8)
+ MB87078 - Fujitsu MB87078 Electronic Volume Control IC (SOIC24)
+ NJM2904 - New Japan Radio Co. NJM2904 Dual Operational Amplifier
+ LB1235 - Sanyo LB1235 65V 1.5A 4-channel Darlington Driver
+ LB1233 - Sanyo LB1233 Darlington Transistor Array with Built-in Current Limiting Resistors
+ J101 - 64 pin connector joining to ?
+ J102 - USB-A connector \ both tied together with common connections
+ J103 - USB-B connector /
+ J104 - Dual Red/White RCA Jacks (Twin Stereo Audio)
+ J105 - 9 pin connector
+ J106 - 12 pin connector
+ JP1 - 3 pin jumper labelled 'WE' and 'NC'. Default position is NC. This is used to write enable
+ the EEPROM for factory programming.
+ SW2 - DIP switch with 4 switches (default all off)
+
+This board is used with Final Furlong. This board connects to GORgON AV PCB
+
+
+V187 ASCA-2A PCB
+2477960102 (2477970102)
+|--------------------------------------------------------|
+| J105 |
+| |-------| 14.7456MHz |
+| J104 |ALTERA | ADM485 PST592D|
+| |EPM7064| |-------| |
+| | | | | |
+| |-------| | C78 | |
+| LC78815 | | |
+| |-------| |
+| MB87078 |---| |
+| LA4705 LB1233 |IC1| 62256 |
+| LB1235 |---| |
+| J101 J102 |
+|--------------------------------------------------------|
+Notes:
+ IC1 - Atmel AT29C020 2MBit EEPROM labelled 'ASCA1 I/O-A' (PLCC32)
+ C78 - Namco Custom MCU, positively identified as a Hitachi H8/3334 (PLCC84)
+ Clock input 14.7456MHz
+ EPM7064 - Altera EPM7064LC68-15 CPLD, labelled 'ASCA DR0' (PLCC68)
+ PST592D - System Reset IC (SOIC4)
+ ADM485 - Analog Devices +ADM485 5V Low Power EIA RS-485 Transceiver (SOIC8)
+ 62256 - Hitachi HM62256 32kB x8-bit SRAM
+ LB1235 - Sanyo LB1235 65V 1.5A 4-channel Darlington Driver
+ LB1233 - Sanyo LB1233 Darlington Transistor Array with Built-in Current Limiting Resistors
+ MB87078 - Fujitsu MB87078 Electronic Volume Control IC (SOIC24)
+ LC78815 - Sanyo LC78815 2-Channel 16-Bit D/A Converter (SOIC20)
+ LA4705 - Sanyo LA4705 15W 2-channel Power Amplifier
+ J101 - 34 pin flat cable connector for filter board
+ J102 - 50 pin flat cable connector for filter board
+ J104 - 8 pin JVS power connector (+5V, +12V, +3.3V)
+ J105 - 64 pin connector for connection of Main PCB
+
+This board is used with Rapid River but also works with Final Furlong.
+
+
+ASCA-3A PCB 8662968301 (8662978301)
+This is used with Motocross Go!
+It's identical to the one shown below (ASCA-4A) but the ROM is a surface mounted
+PLCC32 instead of a socketed DIP32 chip. Everything else is identical.
+
+ASCA-4A PCB 8662968401 (8662978401)
+(Used with most games and [for testing purposes] is able to boot all S23/SS23/Evolution2 games)
+Sticker for Gunmen Wars: ASCA5 PCB 86629615
+|---------------------------------------------------|
+| PST592 62256 |
+| 14.7460MHz |-------| |
+| | C78 | |
+| LB1235 DSW(4) | | 27C1001 |
+| LB1233 LB1235 |-------| |
+| |
+| |------| ADM485 |
+| |ALTERA| |
+| |EPM7096 |
+| J101 |------| J102 J103 |
+|---------------------------------------------------|
+Notes:
+ J101 - 64 pin connector for power + inputs. This joins to another PCB at 90 degrees containing
+ a bunch of connectors named 'ASCA 4B PCB 8662968702 (8662978702)'. This is where ALL of the
+ inputs/outputs for game control and inter-PCB communication are connected
+ PST592 - System Reset IC (SOIC4)
+ C78 - Hitachi HD643334 H8/3334 Microcontroller rebadged as 'C78'. Clock input 14.746MHz (PLCC84)
+ 27C1001 - 128k x8 EPROM (DIP32)
+ - For Downhill Bikers labelled 'ASC3 IO-C'
+ - For Panic Park labelled 'ASC3 IO-C'
+ - For Race On! labelled 'ASC5 IO-A'
+ - For Gunmen Wars labelled 'ASC5 IO-A'
+ 62256 - 32k x8 SRAM (SOP28)
+ EPM7096 - Altera EPM7096 CPLD with sticker 'ASCA,DR1' (PLCC68)
+ ADM485 - Analog Devices ADM485 +5V Low Power EIA RS-485 Transceiver (SOIC8)
+ J102/J103- Standard USB A and B connectors. These are not populated on most games, but are populated for
+ use with Motocross Go! on the ASCA-3A PCB.
+
+
+FCA PCB 8662969102 (8662979102)
+(Used with 500GP and Angler King. Another identical board is used with Ridge Racer V on System 246)
+|---------------------------------------------------|
+| J101 J106 |
+| 4.9152MHz |
+| DSW(6) |
+| LED2 |-----| |
+| | MCU | |
+| LEDS3-10 | | |
+| PIC16F84 |-----| |
+| JP1 LED1 ADM485 |
+| |
+| J102 J104 |
+|---------------------------------------------------|
+Notes:
+ J101 - 6 pin connector for power input
+ J102 - 60 pin flat cable connector
+ J104 - 5 pin connector
+ J106 - 30 pin flat cable connector
+ JP1 - 3 pin jumper, set to 'NORM'. Alt setting 'WR'
+ 3771 - Fujitsu MB3771 System Reset IC (SOIC8)
+ PIC16F84 - Microchip PIC16F84 PIC (SOIC20)
+ - For 500GP and Angler King stamped 'CAP10'
+ - For Ridge Racer V (on System 246) stamped 'CAP11'
+ MCU - Fujitsu MB90F574 F2MC-16LX Family Microcontroller (QFP120)
+ - For 500 GP and Angler King stamped 'FCAF10'
+ - For Ridge Racer V (on System 246) stamped 'FCAF11'
+ ADM485 - Analog Devices ADM485 +5V Low Power EIA RS-485 Transceiver (SOIC8)
+
+
+Drive/Feedback PCB
+------------------
+
+V183 AMC PCB 2473966102 (2473970102)
+|---------------------------------------------------|
+| |
+| |
+| 4.9152MHz J2|
+| |-----| |
+| | MCU | |
+| | | |
+| SS22 FFB |-----| |
+| |------| |
+| |ALTERA| DSW(4) |
+| |EPM7096 |
+| |------| |
+| J4|
+| ADM485 |
+| 27C1024 62256x2 J5|
+| |
+|---------------------------------------------------|
+Notes:
+ This board is used only with Motocross Go! to control the steering feedback motor. It communicates as a slave JVS
+ I/O board to both the game board and the ASCA I/O board. Another signal labelled as 'FREEZE/RELAY' connects between
+ this board to its ASCA I/O board.
+
+ MCU - Fujitsu MB90611A F2MC-16F Family Microcontroller. Clock input 4.9152MHz (QFP100)
+ 62256 - 32k x2 SRAM (SOP28)
+ EPM7096 - Altera EPM7064 CPLD labelled 'MG1,P LD0A' (PLCC44)
+ 27C1001 - 128k x8 EPROM labelled 'MG1-PRG0' (DIP40)
+ ADM485 - Analog Devices ADM485 +5V Low Power EIA RS-485 Transceiver (SOIC8)
+ J4/J5 - Standard USB A and B connectors.
+ J2 - Ribbon cable connector.
+ SS22 FFB - The recycled System 22 v147 motor drive board portion.
+
+ From testing on an actual Motocross Go! cabinet, the game doesn't like it if this board is disconnected in any way.
+ It needs both an ASCA and AMC I/O board chained in order to fully boot with properly working I/O. The following happens
+ if any portion of an AMC pcb is disconnected (only applies to Motocross Go!).
+
+ AMC board powered off: Board doesn't properly initialize its subcpu giving a 'subcpu timeout'.
+ AMC board powered on, JVS comms disconnected, freeze/relay connected: Board initializes the subcpu properly but
+ intentionally disables inputs.
+ AMC board powered on, JVS comms connected, freeze/relay disconnected: Board initializes the subcpu properly.
+ Main inputs work. Motor doesn't respond (if freeze/relay is reconnected, the motor will respond again).
+
+ In RS232 mode, the data should be sent at 9600 8n1 in the following format:
+ byte 0: 0xff
+ byte 1: 0x00-0x7f Analog output 0
+ byte 2: 0x00-0x0f/0x80-0x8f digital output
+ byte 3: 0x00-0xff Analog output 1
+
+
+I/O Boards for gun games
+------------------------
+
+Type 1:
+
+V185 I/O PCB
+2479961102 (2479971102)
+|-----------------------------------------|
+| J601 LED1 LED2 |-------| |
+| DSW(4)JP1 |-------| |TSSIO | |
+| |-----| | C78 |14.746MHz|PLD | |
+| |TSSIO| | |PST592 |-------| |
+| |PROG | |-------| |
+| |-----| |
+| |
+| 62256 |
+|ADM485 |
+|J1 SLA4060 J5|
+| J3 J4 |
+|-----------------------------------------|
+Notes:
+ TSSIOPROG - Atmel AT29C020 EEPROM stamped 'TSSIOP8' (PLCC32)
+ C78 - Namco Custom C78, actually a rebadged Hitachi HD643334 MCU, clock input 14.746MHz (PLCC84)
+ TSSIOPLD - Altera MAX EPM7128ELC84 CPLD with label 'TSSIOPLD' (PLCC84)
+ SLA4060 - Sanken Electric SLA4060 NPN general purpose darlington transistor (used to drive the kick-back solenoid in the gun)
+ PST592 - System Reset IC (SOIC4)
+ J1 - 12 position connector for power and I/O communication
+ J3 - 12 position connector for gun connection (trigger/buttons/optical signal/power)
+ J4 - not used?
+ J5 - 6 position connector for network
+ J601 - not used?
+ JP1 - jumper set to 1-2 (lower position), labelled 'WR'
+ DSW - 4 position dipswitch block, all off
+
+This board is used only on Time Crisis II.
+Note the gun is a standard light gun.
+
+
+Type 2:
+
+V221 MIU PCB
+2512960101 (2512970101)
+additional sticker for Time Crisis 3 says '2591961001 V291 XMIU PCB'
+|---------------------------------------------|
+|J10 J9 29C020 LC35256 DSW(4) |
+| M0105 PRG.8F LED |
+|2267 6393 LED |
+| T082 T082 |------| |
+| |--------| | C78 | J8 |
+| |ALTERA | | | |
+|J11 |MAX | |------| J7|
+| LM1881 |EPM7128 | 3771 |
+|R305526 |--------| |
+| ZUW1R51212 14.746MHz |
+| ADM485 J6|
+| |
+| J1 J2 J3 J4 J5 |
+|---------------------------------------------|
+Notes:
+ 2267 - JRC2267 Current limiting diode array? (SOIC8)
+ R305526 - Some kind of mini transformer or regulator?
+ LC35256 - Sanyo LC35256 32k x8 SRAM (SOP28)
+ LM1881 - National Semiconductor LM1881 Video Sync Separator (SOIC8)
+ M0105 - Matsushita Panasonic 0105 = ? (SOIC16)
+ T082 - Texas Instruments T082 (=TL082) JFET-Input operational amplifier (SOIC8)
+ 6393 - Sanyo 6393 (LA6393) High Performance Dual Comparator (SOIC8)
+ ADM485 - Analog Devices ADM485 5V Low Power EIA RS-485 Transceiver (SOIC8)
+ 3771 - Fujitsu MB3771 Power Supply Monitor and Master Reset IC (SOIC8)
+ EPM7128 - Altera MAX EPM7128SLC84-15 PLD labelled 'TMIU1/PLD0' (Time Crisis 3)
+ 29C020 - location for 29C020 PLCC32 Flash/EP ROM (not populated)
+ ZUW1R51212 - Cosel ZUW1R51212 DC to DC Power Supply Module (input 9-18VDC, output +-12VDC or +24VDC)
+ DSW - 4 position dipswitch block, all off
+ J1 - 6-pin power input connector
+ J2 - 12-pin connector (cabinet buttons UP/DOWN/ENTER plus TEST/SERVICE/COIN etc)
+ J3 - 4 pin connector (not used)
+ J4 - 9 pin Namco female plug connector for gun (solenoid +24V/trigger/pedal/sensor)
+ J5 - 5 pin connector used for I/O --> S246 communications (connects to USB link connection on main unit)
+ J6 - 7-pin connector (not used)
+ J9 - 6-pin connector (not used)
+ J10 - 2-pin Namco female plug connector (not used)
+ J11 - 6-pin Namco female plug connector (video input from CCD camera)
+ PRG.8F - 27C1001 EPROM with label
+ 'XMIU1 PRG0' (I/O program for Time Crisis 3)
+ 'CSZ1 PRG0A' (I/O program for Crisis Zone)
+
+This board is used on Crisis Zone (System 23 Evolution2) and Time Crisis 3 (System 246)
+Note both games use a CCD camera for the gun sensor.
+
+*/
+
+#include "emu.h"
+#include "namcoio.h"
+#include "jvshle.h"
+#include "cpu/f2mc16/mb90570.h"
+
+#define LOG_OUTPUT (1U << 1)
+
+//#define VERBOSE (LOG_OUTPUT)
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
+
+#define LOGOUTPUT(...) LOGMASKED(LOG_OUTPUT, __VA_ARGS__)
+
+namco_amc_device::namco_amc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, NAMCO_AMC, tag, owner, clock),
+ device_jvs_interface(mconfig, *this),
+ device_rs232_port_interface(mconfig, *this),
+ m_iocpu(*this, "iocpu"),
+ m_analog_output{},
+ m_clear_output(1),
+ m_output(0),
+ m_ppg_duty(0),
+ m_ppg_flags(0),
+ m_sense_latch(0),
+ m_unknown_output(0)
+{
+}
+
+void namco_amc_device::device_add_mconfig(machine_config &config)
+{
+ MB90611A(config, m_iocpu, 4.9152_MHz_XTAL);
+ m_iocpu->set_addrmap(AS_PROGRAM, &namco_amc_device::iocpu_program_map);
+ m_iocpu->ppg().output<1>().set([this](offs_t offset, uint32_t data, uint32_t mem_mask)
+ {
+ m_ppg_duty = mem_mask >> 26;
+ update_analog_output0();
+ });
+ m_iocpu->adc().channel<0>().set(FUNC(namco_amc_device::adc_r<0>));
+ m_iocpu->adc().channel<1>().set(FUNC(namco_amc_device::adc_r<1>));
+ m_iocpu->adc().channel<2>().set(FUNC(namco_amc_device::adc_r<2>));
+ m_iocpu->adc().channel<3>().set(FUNC(namco_amc_device::adc_r<3>));
+ m_iocpu->port<0x2>().read().set([this]()
+ {
+ return (BIT(m_clear_output, 0) << 7) |
+ ((m_jvs_sense != jvs_port_device::sense::Initialized) << 6) |
+ ((m_jvs_sense != jvs_port_device::sense::None) << 5) |
+ (1 << 4) | // sense
+ (15 << 0); // output
+ });
+ m_iocpu->port<0x2>().write().set([this](uint8_t data)
+ {
+ m_sense_latch = BIT(data, 4); // HACK: improve compatibility by delaying sense::Initialized
+ if (!m_sense_latch)
+ sense(jvs_port_device::sense::Uninitialized);
+
+ uint8_t output = bitswap<4>(data, 0, 1, 2, 3) << 4;
+ if (m_output != output)
+ {
+ LOGOUTPUT("output_w %02x\n", output);
+ m_output_cb(0, output, output ^ m_output);
+ m_output = output;
+ }
+ });
+ m_iocpu->port<0x3>().read().set([this](uint8_t data)
+ {
+ return ~bitswap<8>(player_r(0, 0xfc03), 10, 11, 12, 13, 14, 15, 0, 1);
+ });
+ m_iocpu->port<0x7>().read().set_ioport("DSW");
+ m_iocpu->port<0x8>().write().set([this](uint8_t data)
+ {
+ m_ppg_flags = BIT(data, 0, 2);
+ update_analog_output0();
+
+ m_unknown_output = BIT(data, 2); // RS232 bit 7 of digital data, JVS unknown?
+ // BIT(data, 5); // set at startup
+ });
+ m_iocpu->port<0x9>().write().set([this](uint8_t data)
+ {
+ if (m_sense_latch)
+ sense(jvs_port_device::sense::Initialized); // HACK: improve compatibility by delaying sense::Initialized
+
+ // BIT(data, 1); // set at startup
+ rts(BIT(data, 2));
+ // BIT(data, 3); // toggles to indicate activity/error?
+ // BIT(data, 4); // ""
+ });
+
+ m_iocpu->uart<1>().sot().set(FUNC(namco_amc_device::txd));
+
+ config.set_maximum_quantum(attotime::from_hz(2 * 115200));
+
+ add_jvs_port(config);
+}
+
+namespace {
+
+struct DSW { enum : uint8_t
+{
+ Interface = 0x08,
+}; };
+
+static INPUT_PORTS_START(namco_amc)
+ PORT_START("DSW")
+ PORT_DIPNAME(0x08, 0x08, "Interface") PORT_DIPLOCATION("SW1:4")
+ PORT_DIPSETTING(0x08, "JVS")
+ PORT_DIPSETTING(0x00, "RS232")
+ PORT_DIPNAME(0x04, 0x04, "Output Test") PORT_DIPLOCATION("SW1:3")
+ PORT_DIPSETTING(0x04, DEF_STR(Off))
+ PORT_DIPSETTING(0x00, DEF_STR(On)) // interface must be set to JVS
+ PORT_DIPNAME(0x02, 0x00, "JVS Timeout") PORT_DIPLOCATION("SW1:2") // HACK: improve compatibility by setting default to Continue
+ PORT_DIPSETTING(0x02, "Stop") // outputs are cleared and board requires power cycling
+ PORT_DIPSETTING(0x00, "Continue") // outputs are cleared, but board still responds
+ PORT_DIPUNKNOWN_DIPLOC(0x01, 0x01, "SW1:1")
+INPUT_PORTS_END
+
+static INPUT_PORTS_START(namco_amc_default)
+ PORT_INCLUDE(namco_amc)
+
+ PORT_START("PLAYER1")
+ PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_BUTTON1)
+ PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_BUTTON2)
+ PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_BUTTON3)
+ PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_BUTTON4)
+ PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_BUTTON5)
+ PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_BUTTON6)
+ PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_BUTTON7)
+ PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_BUTTON8)
+
+ PORT_START("ANALOG_INPUT1")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 1")
+
+ PORT_START("ANALOG_INPUT2")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 2")
+
+ PORT_START("ANALOG_INPUT3")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 3")
+
+ PORT_START("ANALOG_INPUT4")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 4")
+
+ PORT_START("CLEAR")
+ PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_BUTTON9) PORT_NAME("Clear Output") PORT_WRITE_LINE_MEMBER(FUNC(namco_amc_device::clear_output))
+INPUT_PORTS_END
+
+ROM_START( namco_amc )
+ ROM_REGION( 0x020000, "iocpu", 0 )
+ ROM_LOAD( "mg1prog0a.3a", 0x000000, 0x020000, CRC(b2b5be8f) SHA1(803652b7b8fde2196b7fb742ba8b9843e4fcd2de) )
+ROM_END
+
+} // anonymous namespace
+
+ioport_constructor namco_amc_device::device_input_ports() const
+{
+ return m_default_inputs ? INPUT_PORTS_NAME(namco_amc_default) : INPUT_PORTS_NAME(namco_amc);
+}
+
+const tiny_rom_entry *namco_amc_device::device_rom_region() const
+{
+ return ROM_NAME(namco_amc);
+}
+
+void namco_amc_device::device_start()
+{
+ save_item(NAME(m_analog_output));
+ save_item(NAME(m_clear_output));
+ save_item(NAME(m_output));
+ save_item(NAME(m_ppg_duty));
+ save_item(NAME(m_ppg_flags));
+ save_item(NAME(m_sense_latch));
+ save_item(NAME(m_unknown_output));
+}
+
+void namco_amc_device::rxd(int state)
+{
+ m_iocpu->uart<1>().sin(state);
+}
+
+void namco_amc_device::input_txd(int state)
+{
+ m_iocpu->uart<0>().sin(state);
+}
+
+void namco_amc_device::iocpu_program_map(address_map &map)
+{
+ map(0x0000c8, 0x0000c8).w(FUNC(namco_amc_device::analog_output_w<1>));
+ map(0xfc0000, 0xfcffff).ram();
+ map(0xfe0000, 0xffffff).rom().region("iocpu", 0);
+}
+
+void namco_amc_device::update_analog_output0()
+{
+ analog_output_w<0>((BIT(m_ppg_flags, 0) ? 0 : m_ppg_duty) | (!BIT(m_ppg_flags, 1) << 6));
+}
+
+template<unsigned N>
+void namco_amc_device::analog_output_w(uint8_t data)
+{
+ if (m_analog_output[N] != data)
+ {
+ m_analog_output[N] = data;
+ LOGOUTPUT("analog_output_w %04x %04x\n", m_analog_output[0] * 0x101, m_analog_output[1] * 0x101);
+ m_analog_output_cb[N](data * 0x101);
+ }
+}
+
+template<int N>
+uint16_t namco_amc_device::adc_r()
+{
+ return analog_input_r(N, 0xffc0) >> 6;
+}
+
+void namco_amc_device::clear_output(int state)
+{
+ m_clear_output = state;
+}
+
+int namco_amc_device::unknown_output()
+{
+ return m_unknown_output;
+}
+
+
+namespace {
+
+static INPUT_PORTS_START(namco_c78_io)
+ PORT_START("DSW")
+ PORT_DIPNAME(0x08, 0x08, "DIP SW1") PORT_DIPLOCATION("SW2:1")
+ PORT_DIPSETTING(0x08, DEF_STR(Off))
+ PORT_DIPSETTING(0x00, DEF_STR(On))
+ PORT_DIPNAME(0x04, 0x04, "DIP SW2") PORT_DIPLOCATION("SW2:2")
+ PORT_DIPSETTING(0x04, DEF_STR(Off))
+ PORT_DIPSETTING(0x00, DEF_STR(On))
+ PORT_DIPNAME(0x02, 0x02, "Output Test") PORT_DIPLOCATION("SW2:3")
+ PORT_DIPSETTING(0x02, DEF_STR(Off))
+ PORT_DIPSETTING(0x00, DEF_STR(On))
+ PORT_DIPUNUSED_DIPLOC(0x01, 0x01, "SW2:4")
+INPUT_PORTS_END
+
+class namco_c78_jvs_io_device :
+ public device_t,
+ public device_jvs_interface
+{
+protected:
+ namco_c78_jvs_io_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, type, tag, owner, clock),
+ device_jvs_interface(mconfig, *this),
+ m_iocpu(*this, "iocpu"),
+ m_dsw(*this, "DSW")
+ {
+ }
+
+ // device_t
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD
+ {
+ H83334(config, m_iocpu, 14.7456_MHz_XTAL);
+ m_iocpu->set_addrmap(AS_PROGRAM, &namco_c78_jvs_io_device::iocpu_program_map);
+ m_iocpu->write_port1().set_nop(); // unused
+ m_iocpu->write_port2().set_nop(); // unused
+ m_iocpu->write_port3().set_nop(); // unused
+ m_iocpu->read_port4().set(FUNC(namco_c78_jvs_io_device::iocpu_port_4_r));
+ m_iocpu->write_port4().set(FUNC(namco_c78_jvs_io_device::iocpu_port_4_w));
+ m_iocpu->read_port5().set(FUNC(namco_c78_jvs_io_device::iocpu_port_5_r));
+ m_iocpu->write_port5().set(FUNC(namco_c78_jvs_io_device::iocpu_port_5_w));
+ m_iocpu->read_port6().set(FUNC(namco_c78_jvs_io_device::iocpu_port_6_r));
+ m_iocpu->write_port6().set(FUNC(namco_c78_jvs_io_device::iocpu_port_6_w));
+
+ m_iocpu->write_sci_tx<0>().set(FUNC(namco_c78_jvs_io_device::txd));
+
+ config.set_maximum_quantum(attotime::from_hz(2 * 115200));
+
+ add_jvs_port(config);
+ }
+
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD
+ {
+ return INPUT_PORTS_NAME(namco_c78_io);
+ }
+
+ virtual void device_start() override ATTR_COLD
+ {
+ }
+
+ // device_jvs_interface
+ virtual void rxd(int state) override
+ {
+ m_iocpu->sci_rx_w<0>(state);
+ }
+
+ virtual void iocpu_program_map(address_map &map) ATTR_COLD
+ {
+ map(0x0000, 0x3fff).rom().region("iocpu", 0);
+ map(0xc000, 0xfb7f).ram();
+ }
+
+ uint8_t iocpu_port_4_r()
+ {
+ return (1 << 7) | // unknown input
+ (BIT(m_dsw->read(), 2, 2) << 5) |
+ ((m_jvs_sense != jvs_port_device::sense::None) << 4) |
+ ((m_jvs_sense != jvs_port_device::sense::Initialized) << 3) |
+ (1 << 2) | // sense
+ (3 << 0); // unknown output
+ }
+
+ void iocpu_port_4_w(uint8_t data)
+ {
+ sense(BIT(data, 2) ? jvs_port_device::sense::Initialized : jvs_port_device::sense::Uninitialized);
+ // BIT(data, 1) // set at startup
+ }
+
+ uint8_t iocpu_port_5_r()
+ {
+ return (31 << 3) | // unknown input
+ (1 << 2) | // rts
+ (1 << 1) | // unknown input
+ (1 << 0); // unknown output
+ }
+
+ void iocpu_port_5_w(uint8_t data)
+ {
+ rts(BIT(data, 2));
+ }
+
+ uint8_t iocpu_port_6_r()
+ {
+ return (1 << 7) | // unknown input
+ (3 << 5) | // coin counters
+ (BIT(~player_r(0, 0x40), 6) << 4) |
+ (3 << 2) | // unknown output
+ (BIT(m_dsw->read(), 1) << 1) | // TODO: confirm DSW hookup
+ (1 << 0); // unused input
+ }
+
+ void iocpu_port_6_w(uint8_t data)
+ {
+ machine().bookkeeping().coin_counter_w(0, BIT(data, 6));
+ machine().bookkeeping().coin_counter_w(1, BIT(data, 5)); // except tssio/csz1/xmiu1
+ // BIT(data, 3); // toggles (except asca1)
+ // BIT(data, 2); // toggles (except asca1)
+ }
+
+ required_device<h83334_device> m_iocpu;
+ required_ioport m_dsw;
+};
+
+
+static INPUT_PORTS_START(namco_asca1)
+ PORT_INCLUDE(namco_c78_io)
+
+ PORT_START("SYSTEM")
+ PORT_SERVICE(0x80, IP_ACTIVE_HIGH)
+
+ PORT_START("PLAYER1")
+ PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_SERVICE1)
+ PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)
+ PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)
+ PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_BUTTON1)
+ PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_BUTTON2)
+ PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_BUTTON3)
+ PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_BUTTON4)
+ PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_BUTTON5)
+ PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_BUTTON6)
+ PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_BUTTON7)
+ PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_BUTTON8)
+ PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_BUTTON9)
+ PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_BUTTON10)
+ PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_BUTTON11)
+ PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_BUTTON12)
+ PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_BUTTON13)
+
+ PORT_START("COIN1")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_COIN1)
+
+ PORT_START("COIN2")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_COIN2)
+
+ PORT_START("ANALOG_INPUT1")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 1")
+
+ PORT_START("ANALOG_INPUT2")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 2")
+
+ PORT_START("ANALOG_INPUT3")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 3")
+
+ PORT_START("ANALOG_INPUT4")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 4")
+
+ PORT_START("ANALOG_INPUT5")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 5")
+
+ PORT_START("ANALOG_INPUT6")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 6")
+
+ PORT_START("ANALOG_INPUT7")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 7")
+
+ PORT_START("ANALOG_INPUT8")
+ PORT_BIT(0xffff, 0x8000, IPT_PADDLE) PORT_MINMAX(0x0000, 0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(2560) PORT_NAME("Analog Input 8")
+INPUT_PORTS_END
+
+ROM_START( namco_asca1 )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "asc1_io-a.ic13", 0x000000, 0x040000, CRC(77cdf69a) SHA1(497af1059f85c07bea2dd0d303481623f6019dcf) )
+ROM_END
+
+class namco_asca_1_device :
+ public namco_c78_jvs_io_device
+{
+public:
+ namco_asca_1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_asca_1_device(mconfig, NAMCO_ASCA1, tag, owner, clock)
+ {
+ }
+
+protected:
+ namco_asca_1_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+ namco_c78_jvs_io_device(mconfig, type, tag, owner, clock),
+ m_output(0)
+ {
+ }
+
+ // device_t
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD
+ {
+ return m_default_inputs ? INPUT_PORTS_NAME(namco_asca1) : namco_c78_jvs_io_device::device_input_ports();
+ }
+
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD
+ {
+ namco_c78_jvs_io_device::device_add_mconfig(config);
+
+ m_iocpu->read_port8().set(FUNC(namco_asca_1_device::iocpu_port_8_r));
+ m_iocpu->write_port8().set(FUNC(namco_asca_1_device::iocpu_port_8_w));
+ m_iocpu->read_port9().set(FUNC(namco_asca_1_device::iocpu_port_9_r));
+ m_iocpu->write_port9().set(FUNC(namco_asca_1_device::iocpu_port_9_w));
+
+ m_iocpu->read_adc<0>().set(FUNC(namco_asca_1_device::adc_r<0>));
+ m_iocpu->read_adc<1>().set(FUNC(namco_asca_1_device::adc_r<1>));
+ m_iocpu->read_adc<2>().set(FUNC(namco_asca_1_device::adc_r<2>));
+ m_iocpu->read_adc<3>().set(FUNC(namco_asca_1_device::adc_r<3>));
+ m_iocpu->read_adc<4>().set(FUNC(namco_asca_1_device::adc_r<4>));
+ m_iocpu->read_adc<5>().set(FUNC(namco_asca_1_device::adc_r<5>));
+ m_iocpu->read_adc<6>().set(FUNC(namco_asca_1_device::adc_r<6>));
+ m_iocpu->read_adc<7>().set(FUNC(namco_asca_1_device::adc_r<7>));
+ }
+
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_asca1);
+ }
+
+ virtual void device_start() override ATTR_COLD
+ {
+ namco_c78_jvs_io_device::device_start();
+
+ save_item(NAME(m_output));
+ }
+
+ // namco_c78_jvs_io_device
+ virtual void iocpu_program_map(address_map &map) override
+ {
+ namco_c78_jvs_io_device::iocpu_program_map(map);
+
+ map(0x6000, 0x6003).r(FUNC(namco_asca_1_device::in_r));
+ map(0x6004, 0x6005).w(FUNC(namco_asca_1_device::output_w));
+ }
+
+ uint8_t iocpu_port_8_r()
+ {
+ return (1 << 7) | // unknown output
+ (3 << 5) | // unknown input
+ (31 << 0); // unknown output
+ }
+
+ void iocpu_port_8_w(uint8_t data)
+ {
+ // BIT(data, 6) // set at startup, is set to input?
+ }
+
+ uint8_t iocpu_port_9_r()
+ {
+ return (3 << 6) | // unknown input
+ (31 << 1) | // unknown output
+ (1 << 0); // unknown input
+ }
+
+ void iocpu_port_9_w(uint8_t data)
+ {
+ }
+
+ uint8_t in_r(offs_t offset)
+ {
+ if (offset == 0)
+ {
+ const auto system = system_r(0x80);
+ const auto p1 = player_r(0, 0xe033);
+
+ return ~((BIT(p1, 13) << 7) |
+ (BIT(p1, 14) << 6) |
+ (BIT(p1, 15) << 5) |
+ (BIT(p1, 0) << 4) |
+ (BIT(p1, 1) << 3) |
+ (BIT(p1, 4) << 2) |
+ (BIT(p1, 5) << 1) |
+ (BIT(system, 7) << 0));
+ }
+ else if (offset == 1)
+ {
+ const auto p1 = player_r(0, 0xe00300);
+
+ return ~((BIT(p1, 21) << 4) |
+ (BIT(p1, 22) << 3) |
+ (BIT(p1, 23) << 2) |
+ (BIT(p1, 8) << 1) |
+ (BIT(p1, 9) << 0));
+ }
+ else if (offset == 2)
+ {
+ const auto p1 = player_r(0, 0x1c00);
+ const auto c1 = coin_r(0, 0x01);
+
+ return ~((BIT(c1, 0) << 3) |
+ (BIT(p1, 10) << 2) |
+ (BIT(p1, 11) << 1) |
+ (BIT(p1, 12) << 0));
+ }
+ else if (offset == 3)
+ {
+ const auto c1 = coin_r(0, 0x80);
+ const auto c2 = coin_r(1, 0x81);
+
+ return ~((BIT(c2, 0) << 3) |
+ (BIT(~c2, 7) << 1) | // not asca1
+ (BIT(~c1, 7) << 0)); // not asca1
+ }
+ return 0xff;
+ }
+
+ template<int N> uint16_t adc_r()
+ {
+ return analog_input_r(N, 0xffc0) >> 6;
+ }
+
+ virtual void output_w(offs_t offset, uint8_t data)
+ {
+ uint16_t output = ((m_output & ~(0xff << (offset * 8))) | bitswap<8>(data, 0, 1, 2, 3, 4, 5, 6, 7) << (offset * 8)) & 0xf8ff;
+
+ if (m_output != output)
+ {
+ LOGOUTPUT("output_w %04x\n", output);
+ m_output_cb(0, output, output ^ m_output);
+ m_output = output;
+ }
+ }
+
+ uint64_t m_output;
+};
+
+
+static INPUT_PORTS_START(namco_asca3)
+ PORT_INCLUDE(namco_asca1)
+
+ PORT_MODIFY("COIN1")
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_TILT2) PORT_NAME("Counter 1 disconnected")
+
+ PORT_MODIFY("COIN2")
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_TILT3) PORT_NAME("Counter 2 disconnected")
+
+ PORT_START("ROTARY_INPUT1")
+ PORT_BIT(0xff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(15) PORT_KEYDELTA(8) PORT_NAME("Rotary Input 1")
+
+ PORT_START("ROTARY_INPUT2")
+ PORT_BIT(0xff, 0, IPT_MOUSE_Y) PORT_SENSITIVITY(15) PORT_KEYDELTA(8) PORT_REVERSE PORT_NAME("Rotary Input 2")
+INPUT_PORTS_END
+
+ROM_START( namco_asca3 )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "asc3_io-c.ic14", 0x000000, 0x020000, CRC(2f272a7b) SHA1(9d7ebe274c0d26f5f38747224d42d0375e2ed14c) )
+ROM_END
+
+class namco_asca_3_device :
+ public namco_asca_1_device
+{
+public:
+ namco_asca_3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_asca_3_device(mconfig, NAMCO_ASCA3, tag, owner, clock)
+ {
+ }
+
+protected:
+ namco_asca_3_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+ namco_asca_1_device(mconfig, type, tag, owner, clock),
+ m_rotary_prev{}
+ {
+ }
+
+ // device_t
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD
+ {
+ return m_default_inputs ? INPUT_PORTS_NAME(namco_asca3) : namco_c78_jvs_io_device::device_input_ports();
+ }
+
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_asca3);
+ }
+
+ virtual void device_start() override ATTR_COLD
+ {
+ m_analog_output_timer = timer_alloc(FUNC(namco_asca_3_device::analog_output_timer_callback), this);
+
+ namco_asca_1_device::device_start();
+
+ save_item(NAME(m_analog_output));
+ save_item(NAME(m_analog_output_cycles));
+ save_item(NAME(m_analog_output_state));
+ save_item(NAME(m_rotary_prev));
+ }
+
+ // namco_c78_jvs_io_device
+ virtual void iocpu_program_map(address_map &map) override
+ {
+ namco_asca_1_device::iocpu_program_map(map);
+
+ map(0x6006, 0x6007).r(FUNC(namco_asca_3_device::rotary_diff_r));
+ }
+
+ virtual void output_w(offs_t offset, uint8_t data) override
+ {
+ if (offset == 1)
+ {
+ namco_asca_1_device::output_w(offset, data & ~(1 << 4));
+
+ int analog_output_state = BIT(data, 4);
+ if (analog_output_state != m_analog_output_state)
+ {
+ if (analog_output_state)
+ {
+ uint64_t high = m_analog_output_cycles[0] - m_analog_output_cycles[1];
+ uint64_t low = m_iocpu->total_cycles() - m_analog_output_cycles[0];
+ update_analog_output((high ? (0xff * high) / (high + low) : 0x0000) * 0x101);
+ }
+
+ m_analog_output_cycles[analog_output_state] = m_iocpu->total_cycles();
+ m_analog_output_timer->adjust(attotime::from_hz(18));
+ m_analog_output_state = analog_output_state;
+ }
+ }
+ else
+ namco_asca_1_device::output_w(offset, data);
+ }
+
+ TIMER_CALLBACK_MEMBER(analog_output_timer_callback)
+ {
+ update_analog_output(m_analog_output_state ? 0xffff : 0x0000);
+ }
+
+ void update_analog_output(uint16_t data)
+ {
+ if (m_analog_output != data)
+ {
+ m_analog_output = data;
+ LOGOUTPUT("analog_output_w %04x\n", data);
+ m_analog_output_cb[0](data);
+ }
+ }
+
+ virtual uint8_t rotary_diff_r(offs_t offset)
+ {
+ uint8_t data = rotary_input_r(offset) - m_rotary_prev[offset];
+ m_rotary_prev[offset] += data;
+ return data;
+ }
+
+ emu_timer *m_analog_output_timer;
+ uint16_t m_analog_output;
+ uint64_t m_analog_output_cycles[2];
+ uint8_t m_analog_output_state;
+ uint8_t m_rotary_prev[2];
+};
+
+
+ROM_START( namco_asca3a )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "asca-3a.ic14", 0x000000, 0x040000, CRC(8e9266e5) SHA1(ffa8782ca641d71d57df23ed1c5911db05d3df97) )
+ROM_END
+
+class namco_asca_3a_device :
+ public namco_asca_3_device
+{
+public:
+ namco_asca_3a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_asca_3_device(mconfig, NAMCO_ASCA3A, tag, owner, clock)
+ {
+ }
+
+protected:
+ // device_t
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_asca3a);
+ }
+};
+
+
+ROM_START( namco_asca5 )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "asc5_io-a.ic14", 0x000000, 0x020000, CRC(5964767f) SHA1(320db5e78ae23c5f94e368432d51573b409995db) )
+ROM_END
+
+class namco_asca_5_device :
+ public namco_asca_3_device
+{
+public:
+ namco_asca_5_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_asca_3_device(mconfig, NAMCO_ASCA5, tag, owner, clock)
+ {
+ }
+
+protected:
+ // device_t
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_asca5);
+ }
+};
+
+
+ROM_START( namco_emio102 )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "th1io-a.4f", 0x000000, 0x040000, CRC(1cbbce27) SHA1(71d61d9218543e1b0b2a6c550a8ff2b7c6267257) )
+ROM_END
+
+class namco_em_io1_02_device :
+ public namco_asca_1_device
+{
+public:
+ namco_em_io1_02_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_asca_1_device(mconfig, NAMCO_EMIO102, tag, owner, clock)
+ {
+ }
+
+protected:
+ // device_t
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_emio102);
+ }
+
+ virtual void device_start() override ATTR_COLD
+ {
+ namco_c78_jvs_io_device::device_start();
+
+ save_item(NAME(m_multiplexed_output));
+ }
+
+ virtual void output_w(offs_t offset, uint8_t data) override
+ {
+ if (m_multiplexed_output[offset] != data)
+ {
+ m_multiplexed_output[offset] = data;
+
+ if (m_multiplexed_output[1] < 3)
+ {
+ int shift = m_multiplexed_output[1] == 2 ? 16 :
+ m_multiplexed_output[1] == 0 ? 8 : 0;
+
+ uint64_t output = (m_output & ~(0xff << shift)) | (m_multiplexed_output[0] << shift);
+
+ if (m_output != output)
+ {
+ LOGOUTPUT("output_w %06x\n", output);
+ m_output_cb(0, output, output ^ m_output);
+ m_output = output;
+ }
+ }
+ }
+ }
+
+ uint8_t m_multiplexed_output[2];
+};
+
+
+static INPUT_PORTS_START(namco_tssio)
+ PORT_INCLUDE(namco_c78_io)
+
+ PORT_START("SYSTEM")
+ PORT_SERVICE(0x80, IP_ACTIVE_HIGH)
+
+ PORT_START("PLAYER1")
+ PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_SERVICE1)
+ PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)
+ PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)
+ PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_BUTTON1)
+ PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_BUTTON2)
+ PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_BUTTON3)
+ PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_BUTTON4)
+ PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_BUTTON5)
+ PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_BUTTON6)
+
+ PORT_START("COIN1")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_COIN1)
+ PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_TILT2) PORT_NAME("Counter disconnected")
+
+ PORT_START("SCREEN_POSITION_INPUT_X1") // tuned for CRT
+ PORT_BIT(0xfff, 91 + 733 / 2, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(91, 91 + 733) PORT_SENSITIVITY(100) PORT_KEYDELTA(12)
+
+ PORT_START("SCREEN_POSITION_INPUT_Y1") // tuned for CRT - can't shoot below the statusbar?
+ PORT_BIT(0xfff, 38 + 247 / 2, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_MINMAX(38, 38 + 247) PORT_SENSITIVITY(100) PORT_KEYDELTA(4)
+INPUT_PORTS_END
+
+ROM_START( namco_tssio )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "tssioprog.ic3", 0x000000, 0x040000, CRC(edad4538) SHA1(1330189184a636328d956c0e435f8d9ad2e96a80) )
+ROM_END
+
+class namco_tss_io_device :
+ public namco_c78_jvs_io_device
+{
+public:
+ namco_tss_io_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_tss_io_device(mconfig, NAMCO_TSSIO, tag, owner, clock)
+ {
+ }
+
+protected:
+ namco_tss_io_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_c78_jvs_io_device(mconfig, type, tag, owner, clock),
+ m_output(0)
+ {
+ }
+
+ // device_t
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD
+ {
+ namco_c78_jvs_io_device::device_add_mconfig(config);
+
+ m_iocpu->read_port8().set(FUNC(namco_tss_io_device::iocpu_port_8_r));
+ m_iocpu->write_port8().set(FUNC(namco_tss_io_device::iocpu_port_8_w));
+ m_iocpu->read_port9().set(FUNC(namco_tss_io_device::iocpu_port_9_r));
+ m_iocpu->write_port9().set(FUNC(namco_tss_io_device::iocpu_port_9_w));
+ }
+
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD
+ {
+ return m_default_inputs ? INPUT_PORTS_NAME(namco_tssio) : namco_c78_jvs_io_device::device_input_ports();
+ }
+
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_tssio);
+ }
+
+ virtual void device_start() override ATTR_COLD
+ {
+ namco_c78_jvs_io_device::device_start();
+
+ save_item(NAME(m_output));
+ }
+
+ // namco_c78_jvs_io_device
+ virtual void iocpu_program_map(address_map &map) override
+ {
+ namco_c78_jvs_io_device::iocpu_program_map(map);
+
+ map(0x6000, 0x6001).r(FUNC(namco_tss_io_device::in_r));
+ map(0x6002, 0x6002).w(FUNC(namco_tss_io_device::output_w));
+ map(0x7000, 0x7005).r(FUNC(namco_tss_io_device::gun_r));
+ map(0x7007, 0x7007).r(FUNC(namco_tss_io_device::gun_ack_r)); // guessed, result ignored
+ }
+
+ uint8_t iocpu_port_8_r()
+ {
+ return (1 << 7) | // unknown output
+ (3 << 5) | // unknown input
+ (1 << 4) | // unknown output
+ (15 << 0); // unknown input
+ }
+
+ void iocpu_port_8_w(uint8_t data)
+ {
+ }
+
+ uint8_t iocpu_port_9_r()
+ {
+ return (1 << 7) | // unknown input
+ (15 << 3) | // unknown output
+ (7 << 0); // unknown input
+ }
+
+ void iocpu_port_9_w(uint8_t data)
+ {
+ }
+
+ uint8_t gun_r(offs_t offset)
+ {
+ uint16_t data = (offset % 3) == 0 ? screen_position_x_r(0) :
+ (offset % 3) == 1 ? screen_position_y_r(0) : // ystart
+ screen_position_y_r(0) + 1;// yend
+ return (offset < 3) ? data & 0xff : data >> 8;
+ }
+
+ uint8_t gun_ack_r()
+ {
+ return 0x00;
+ }
+
+ uint8_t in_r(offs_t offset)
+ {
+ if (offset == 0)
+ {
+ const auto system = system_r(0x80);
+ const auto p1 = player_r(0, 0x32);
+ const auto c1 = coin_r(0, 0x81);
+
+ return ~((BIT(p1, 1) << 7) |
+ (BIT(p1, 4) << 6) |
+ (BIT(p1, 5) << 5) |
+ (BIT(system, 7) << 4) |
+ (0 << 3) | // unknown input
+ (BIT(~c1, 7) << 2) |
+ (0 << 1) | // unknown input
+ (BIT(c1, 0) << 0));
+ }
+ else if (offset == 1)
+ {
+ const auto p1 = player_r(0, 0xf001);
+
+ return ~((BIT(p1, 12) << 4) |
+ (BIT(p1, 13) << 3) |
+ (BIT(p1, 14) << 2) |
+ (BIT(p1, 15) << 1) |
+ (BIT(p1, 0) << 0));
+ }
+
+ return 0xff;
+ }
+
+ void output_w(uint8_t data)
+ {
+ data = bitswap<3>(data, 0, 1, 2) << 5;
+ if (m_output != data)
+ {
+ LOGOUTPUT("output_w %02x\n", data);
+ m_output_cb(0, data, data ^ m_output);
+ m_output = data;
+ }
+ }
+
+ uint8_t m_output;
+};
+
+
+ROM_START( namco_csz1 )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "csz1prg0a.8f", 0x000000, 0x020000, CRC(8edc36b3) SHA1(b5df211988d856572fcc313480e693c8561784e4) )
+ROM_END
+
+class namco_csz1_device :
+ public namco_tss_io_device
+{
+public:
+ namco_csz1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_tss_io_device(mconfig, NAMCO_CSZ1, tag, owner, clock)
+ {
+ }
+
+protected:
+ // device_t
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_csz1);
+ }
+
+ virtual void device_start() override ATTR_COLD
+ {
+ namco_tss_io_device::device_start();
+
+ m_analog_output = 0;
+
+ save_item(NAME(m_analog_output));
+ }
+
+ // namco_c78_jvs_io_device
+ virtual void iocpu_program_map(address_map &map) override
+ {
+ namco_tss_io_device::iocpu_program_map(map);
+
+ map(0x6003, 0x6003).w(FUNC(namco_csz1_device::analog_output_w));
+ }
+
+ void analog_output_w(uint8_t data)
+ {
+ if (m_analog_output != data)
+ {
+ m_analog_output = data;
+ LOGOUTPUT("analog_output_w %04x\n", data * 0x101);
+ m_analog_output_cb[0](data * 0x101);
+ }
+ }
+
+ uint8_t m_analog_output;
+};
+
+
+ROM_START( namco_xmiu1 )
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "csz1prg0a.8f", 0x000000, 0x040000, CRC(bb112fe0) SHA1(6fa5dc81d258137c1b1ad427d49d136d0bbf53fa) )
+ROM_END
+
+class namco_xmiu1_device :
+ public namco_tss_io_device
+{
+public:
+ namco_xmiu1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_tss_io_device(mconfig, NAMCO_XMIU1, tag, owner, clock)
+ {
+ }
+
+protected:
+ // device_t
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_xmiu1);
+ }
+};
+
+
+ROM_START( namco_fca11 )
+ ROM_REGION( 0x040000, "iocpu2", 0 ) // 256KB internal flash ROM
+ ROM_LOAD( "fcaf11.ic4", 0x030000, 0x010000, CRC(13d936df) SHA1(fbb2191263b2b326f1f49729767ee6fae2db21f7) ) // almost good dump, all JVS related code and data is in place
+ ROM_FILL( 0x000000, 0x034000, 0x67 ) // dump was made from $0000 to $ffff, not $fc0000. The ROM only appears in memory from $4000-$ffff
+
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "asc3_io-c.ic14", 0x000000, 0x020000, CRC(2f272a7b) SHA1(9d7ebe274c0d26f5f38747224d42d0375e2ed14c) )
+ ROM_COPY( "iocpu2", 0x039040, 0x001082, 0x00003b ) // patch ASCA3 ROM to report FCA11 description
+
+ ROM_REGION( 0x10000, "pic", 0 ) // I/O board PIC16F84 code
+ ROM_LOAD( "fcap11.ic2", 0x000000, 0x004010, CRC(1b2592ce) SHA1(a1a487361053af564f6ec67e545413e370a3b38c) )
+ROM_END
+
+class namco_fca_11_device :
+ public namco_asca_3_device
+{
+public:
+ namco_fca_11_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_fca_11_device(mconfig, NAMCO_FCA11, tag, owner, clock)
+ {
+ }
+
+protected:
+ namco_fca_11_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+ namco_asca_3_device(mconfig, type, tag, owner, clock),
+ m_iocpu2(*this, "iocpu2")
+ {
+ }
+
+ // device_t
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD
+ {
+ namco_asca_3_device::device_add_mconfig(config);
+
+ MB90F574(config, m_iocpu2, 4.9152_MHz_XTAL);
+ m_iocpu2->set_addrmap(AS_PROGRAM, &namco_fca_11_device::iocpu2_program_map);
+ }
+
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_fca11);
+ }
+
+ virtual void device_reset() override ATTR_COLD
+ {
+ namco_asca_3_device::device_reset();
+
+ m_iocpu2->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
+ }
+
+ // device_jvs_interface
+ virtual void rxd(int state) override
+ {
+ namco_asca_3_device::rxd(state);
+
+ m_iocpu2->uart<1>().sin(state);
+ }
+
+ void iocpu2_program_map(address_map &map)
+ {
+ map(0x004000, 0x00ffff).rom().region("iocpu2", 0x034000);
+ map(0xfc0000, 0xffffff).rom().region("iocpu2", 0);
+ }
+
+ required_device<mb90f574_device> m_iocpu2;
+};
+
+
+ROM_START( namco_fca10 )
+ ROM_REGION( 0x040000, "iocpu2", ROMREGION_ERASE00 ) // 256KB internal flash ROM
+ ROM_LOAD( "fcaf10.bin", 0x000000, 0x040000, NO_DUMP )
+
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "asc3_io-c.ic14", 0x000000, 0x020000, CRC(2f272a7b) SHA1(9d7ebe274c0d26f5f38747224d42d0375e2ed14c) )
+
+ ROM_REGION( 0x10000, "pic", 0 ) // I/O board PIC16F84 code
+ ROM_LOAD( "fcap10.ic2", 0x000000, 0x004010, NO_DUMP )
+ROM_END
+
+class namco_fca_10_device :
+ public namco_fca_11_device
+{
+public:
+ namco_fca_10_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_fca_11_device(mconfig, NAMCO_FCA10, tag, owner, clock)
+ {
+ }
+
+protected:
+ // device_t
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_fca10);
+ }
+
+ virtual void device_start() override ATTR_COLD
+ {
+ namco_fca_11_device::device_start();
+
+ // patch ASCA3 ROM to report FCA10 description
+ auto iocpu = memregion("iocpu")->base();
+ for (int i = 0; i < 0x30; i++)
+ iocpu[BYTE_XOR_BE(i + 0x108d)] = "FCA-1;Ver1.00;JPN,Multipurpose + Rotary Encoder"[i];
+ }
+};
+
+
+ROM_START( namco_fcb )
+ ROM_REGION( 0x040000, "iocpu2", 0 ) // 256KB internal flash ROM
+ ROM_LOAD( "fcb1_io-0b.ic4", 0x034000, 0x00c000, BAD_DUMP CRC(5e25b73f) SHA1(fa805a422ff8793989b0ce901cc868ec1a87c7ac) ) // most JVS handling code is in undumped area
+ ROM_FILL( 0x000000, 0x034000, 0x67 ) // dump was made from $0000 to $ffff, not $fc0000. The ROM only appears in memory from $4000-$ffff
+
+ ROM_REGION( 0x040000, "iocpu", 0 )
+ ROM_LOAD( "asc3_io-c.ic14", 0x000000, 0x020000, CRC(2f272a7b) SHA1(9d7ebe274c0d26f5f38747224d42d0375e2ed14c) )
+ ROM_COPY( "iocpu2", 0x03436a, 0x001082, 0x00003b ) // patch ASCA3 ROM to report FCB description
+
+ ROM_REGION( 0x10000, "pic", 0 ) // I/O board PIC16F84 code
+ ROM_LOAD( "fcb_pic", 0x000000, 0x004010, NO_DUMP )
+ROM_END
+
+class namco_fcb_device :
+ public namco_fca_11_device
+{
+public:
+ namco_fcb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ namco_fca_11_device(mconfig, NAMCO_FCB, tag, owner, clock)
+ {
+ }
+
+protected:
+ // device_t
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD
+ {
+ return ROM_NAME(namco_fcb);
+ }
+};
+
+
+class namco_em_pri1_01_device :
+ public jvs_hle_device
+{
+public:
+ namco_em_pri1_01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0) :
+ jvs_hle_device(mconfig, NAMCO_EMPRI101, tag, owner, clock)
+ {
+ }
+
+ // device_t
+ static constexpr feature_type imperfect_features() { return feature::PRINTER; }
+
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD
+ {
+ add_jvs_port(config);
+ }
+
+ // jvs_hle_device
+ virtual const char *device_id() override
+ {
+ return "namco ltd.;EM Pri1-01;Ver2.00;JPN&EXP,Techno-Drive PRN";
+ }
+
+ virtual void execute(uint8_t command) override
+ {
+ const uint8_t *parameters;
+ uint8_t *response;
+
+ switch (command)
+ {
+ case 0x72:
+ if (consume(1, parameters))
+ {
+ switch (parameters[0])
+ {
+ case 0x01: // initialise?
+ if (produce(1, response))
+ response[0] = ReportCode::Normal;
+ break;
+
+ case 0x02: // end transmit?
+ if (produce(1, response))
+ response[0] = ReportCode::Normal;
+ break;
+
+ case 0x03: // get status
+ if (produce(2, response))
+ {
+ response[0] = ReportCode::Normal;
+ response[1] = 0x01; // 1=ok/2=busy/4=paper empty
+ }
+ break;
+
+ case 0x04: // begin transmit?
+ if (produce(1, response))
+ response[0] = ReportCode::Normal;
+ break;
+
+ case 0x05: // end of print job?
+ if (consume(1, parameters) && produce(1, response))
+ response[0] = (parameters[0] == 1) ? ReportCode::Normal : ReportCode::InvalidParameter;
+ break;
+
+ case 0x07: // transmit data
+ if (consume(1, parameters))
+ {
+ uint8_t length = parameters[0];
+
+ if (consume(length, parameters) && produce(1, response))
+ response[0] = ReportCode::Normal;
+ }
+ break;
+
+ default:
+ status_code(StatusCode::UnknownCommand);
+ break;
+ }
+ }
+ break;
+
+ default:
+ jvs_hle_device::execute(command);
+ break;
+ }
+ }
+};
+
+} // anonymous namespace
+
+
+DEFINE_DEVICE_TYPE(NAMCO_AMC, namco_amc_device, "namco_amc", "Namco AMC PCB (Extra I/O,JPN,Ver1.10)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_ASCA1, device_jvs_interface, namco_asca_1_device, "namco_asca1", "Namco ASCA-1 (Multipurpose I/O,JPN,Ver2.00)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_ASCA3, device_jvs_interface, namco_asca_3_device, "namco_asca3", "Namco ASCA-3 (Multipurpose + Rotary Encoder,JPN,Ver2.04)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_ASCA3A, device_jvs_interface, namco_asca_3a_device, "namco_asca3a", "Namco ASCA-3 (Multipurpose + Rotary Encoder,JPN,Ver2.02)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_ASCA5, device_jvs_interface, namco_asca_5_device, "namco_asca5", "Namco ASCA-5 (Multipurpose,JPN,Ver2.09)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_CSZ1, device_jvs_interface, namco_csz1_device, "namco_csz1", "Namco CSZ1 MIU-I/O (GUN-EXTENTION,JPN,Ver2.05)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_EMIO102, device_jvs_interface, namco_em_io1_02_device, "namco_emio102", "Namco EM I/O1-02 (Techno-Drive I/O,JPN&EXP,Ver2.00)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_EMPRI101, device_jvs_interface, namco_em_pri1_01_device, "namco_empri101", "Namco EM Pri1-01")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_FCA10, device_jvs_interface, namco_fca_10_device, "namco_fca10", "Namco FCA-1 (Multipurpose + Rotary Encoder,JPN,Ver1.00)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_FCA11, device_jvs_interface, namco_fca_11_device, "namco_fca11", "Namco FCA-1 (Multipurpose + Rotary Encoder,JPN,Ver1.01)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_FCB, device_jvs_interface, namco_fcb_device, "namco_fcb", "Namco FCB (TouchPanel&Multipurpose,JPN,Ver1.02)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_TSSIO, device_jvs_interface, namco_tss_io_device, "namco_tssio", "Namco TSS-I/O (GUN-EXTENTION,JPN,Ver2.02)")
+DEFINE_DEVICE_TYPE_PRIVATE(NAMCO_XMIU1, device_jvs_interface, namco_xmiu1_device, "namco_xmiu1", "Namco XMIU1 TSS-I/O (GUN-EXTENTION,JPN,Ver2.11,Ver2.12)")
diff --git a/src/devices/bus/jvs/namcoio.h b/src/devices/bus/jvs/namcoio.h
new file mode 100644
index 00000000000..c3d38ff8bb1
--- /dev/null
+++ b/src/devices/bus/jvs/namcoio.h
@@ -0,0 +1,73 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+
+#ifndef MAME_BUS_JVS_NAMCOIO_H
+#define MAME_BUS_JVS_NAMCOIO_H
+
+#pragma once
+
+#include "jvs.h"
+#include "bus/rs232/rs232.h"
+#include "cpu/f2mc16/mb90610a.h"
+#include "cpu/h8/h83337.h"
+
+class namco_amc_device :
+ public device_t,
+ public device_jvs_interface,
+ public device_rs232_port_interface
+{
+public:
+ namco_amc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ void clear_output(int state);
+ int unknown_output();
+
+ static constexpr DEVICE_INPUT_DEFAULTS_START(dsw_rs232)
+ DEVICE_INPUT_DEFAULTS("DSW", 0x08, 0x00)
+ DEVICE_INPUT_DEFAULTS_END
+
+protected:
+ // device_t
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD;
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
+ virtual void device_start() override ATTR_COLD;
+
+ // device_jvs_interface
+ virtual void rxd(int state) override;
+
+ // device_rs232_port_interface
+ virtual void input_txd(int state) override;
+
+ virtual void iocpu_program_map(address_map &map);
+
+ template<int N> uint16_t adc_r();
+ void update_analog_output0();
+ template<unsigned N> void analog_output_w(uint8_t data);
+
+ required_device<mb90611a_device> m_iocpu;
+ std::array<uint8_t, 2> m_analog_output;
+ uint8_t m_clear_output;
+ uint8_t m_output;
+ uint8_t m_ppg_duty;
+ uint8_t m_ppg_flags;
+ uint8_t m_sense_latch;
+ uint8_t m_unknown_output;
+};
+
+
+DECLARE_DEVICE_TYPE(NAMCO_AMC, namco_amc_device)
+DECLARE_DEVICE_TYPE(NAMCO_ASCA1, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_ASCA3, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_ASCA3A, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_ASCA5, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_CSZ1, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_EMIO102, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_EMPRI101, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_FCA10, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_FCA11, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_FCB, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_TSSIO, device_jvs_interface)
+DECLARE_DEVICE_TYPE(NAMCO_XMIU1, device_jvs_interface)
+
+#endif
diff --git a/src/devices/bus/rs232/rs232.h b/src/devices/bus/rs232/rs232.h
index 2de3aedd9d7..2ac20051b8e 100644
--- a/src/devices/bus/rs232/rs232.h
+++ b/src/devices/bus/rs232/rs232.h
@@ -117,7 +117,7 @@ public:
set_default_option(dflt);
set_fixed(false);
}
- rs232_port_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ rs232_port_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
virtual ~rs232_port_device();
// static configuration helpers
diff --git a/src/emu/layout/cyberlead.lay b/src/emu/layout/cyberlead.lay
new file mode 100644
index 00000000000..9eb4e87e472
--- /dev/null
+++ b/src/emu/layout/cyberlead.lay
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!--
+license:CC0-1.0
+-->
+<mamelayout version="2">
+ <view name="Default Layout">
+ <screen index="1">
+ <bounds x="0" y="0" width="768" height="128" />
+ </screen>
+ <screen index="0">
+ <bounds x="0" y="128" width="768" height="576" />
+ </screen>
+ </view>
+</mamelayout>