diff --git a/src/devices/bus/rs232/xvd701.cpp b/src/devices/bus/rs232/xvd701.cpp index c94a676b545..e51ff0449ce 100644 --- a/src/devices/bus/rs232/xvd701.cpp +++ b/src/devices/bus/rs232/xvd701.cpp @@ -1,12 +1,23 @@ // license:BSD-3-Clause -// copyright-holders:smf +// copyright-holders:smf, DragonMinded, windyfairy + #include "emu.h" #include "xvd701.h" +#define LOG_COMMAND (1 << 1) +// #define VERBOSE (LOG_COMMAND) +// #define LOG_OUTPUT_STREAM std::cout + +#include "logmacro.h" + +#define LOGCMD(...) LOGMASKED(LOG_COMMAND, __VA_ARGS__) + + jvc_xvd701_device::jvc_xvd701_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, JVC_XVD701, tag, owner, clock), device_serial_interface(mconfig, *this), device_rs232_port_interface(mconfig, *this), + m_media_type(JVC_MEDIA_VCD), // TODO: This should be changed based on the type of disc inserted or else seeking won't work properly m_response_index(0), m_timer_response(nullptr) { @@ -55,6 +66,11 @@ void jvc_xvd701_device::device_reset() memset(m_command, 0, sizeof(m_command)); m_response_index = sizeof(m_response); + + m_jlip_id = 33; // Twinkle default + m_is_powered = false; + m_chapter = 0; + m_playback_status = STATUS_STOP; } void jvc_xvd701_device::device_timer(emu_timer &timer, device_timer_id id, int param) @@ -82,14 +98,27 @@ void jvc_xvd701_device::tra_complete() unsigned char jvc_xvd701_device::sum(unsigned char *buffer, int length) { - int sum = 0; + int sum = 0x80; for (int i = 0; i < length; i++) - sum += buffer[i]; + sum -= buffer[i] & 0x7f; return sum & 0x7f; } +void jvc_xvd701_device::create_packet(unsigned char status, const unsigned char response[6]) +{ + m_response[0] = 0xfc; + m_response[1] = 0xff; + m_response[2] = m_jlip_id; + m_response[3] = status; + memcpy(&m_response[4], response, 6); + m_response[10] = sum(m_response, sizeof(m_response) - 1); + + m_response_index = 0; + m_timer_response->adjust(attotime::from_msec(100)); +} + void jvc_xvd701_device::send_response() { if (m_response_index < sizeof(m_response) && is_transmit_register_empty()) @@ -99,6 +128,22 @@ void jvc_xvd701_device::send_response() } } +bool jvc_xvd701_device::seek_chapter(int chapter) +{ + if (chapter <= 0) + { + // Chapters are from 1 and up + return false; + } + + m_chapter = chapter; + + if (m_playback_status != STATUS_PAUSE) + m_playback_status = STATUS_PLAYING; + + return true; +} + void jvc_xvd701_device::rcv_complete() { receive_register_extract(); @@ -110,39 +155,162 @@ void jvc_xvd701_device::rcv_complete() if (m_command[0] == 0xff && m_command[1] == 0xff && - m_command[2] == 0x21 && - sum(m_command, sizeof(m_command)) == 0) + m_command[10] == sum(m_command, sizeof(m_command) - 1)) { - // printf("xvd701"); + if (m_command[3] == 0x0c && m_command[4] == 0x43 && m_command[5] == 0x6d) + { + // FF FF 21 0C 43 6D 00 00 00 00 25 PAUSE + LOGCMD("xvd701: Playback PAUSE\n"); + m_playback_status = STATUS_PAUSE; + create_packet(STATUS_OK, NO_RESPONSE); + } + else if (m_command[3] == 0x0c && m_command[4] == 0x43 && m_command[5] == 0x75) + { + // FF FF 21 0C 43 75 00 00 00 00 1D PLAY + LOGCMD("xvd701: Playback PLAY\n"); - //for (int i = 0; i < sizeof(m_command); i++) - // printf(" %02x", m_command[i]); + auto status = STATUS_OK; + if (m_playback_status == STATUS_STOP) + { + // Force video to load again if the video was stopped then started again + if (!seek_chapter(m_chapter)) + status = STATUS_ERROR; + } - //printf("\n"); + if (status == STATUS_OK) + m_playback_status = STATUS_PLAYING; - // FF FF 21 3E 40 70 00 00 00 00 73 DEVICE ON - // FF FF 21 3E 40 60 00 00 00 00 03 DEVICE OFF - // FF FF 21 0C 44 60 00 00 00 00 31 STOP - // FF FF 21 0C 43 75 00 00 00 00 1D PLAY - // FF FF 21 0C 43 6D 00 00 00 00 25 PAUSE - // FF FF 21 0C 50 20 00 00 00 00 63 SEEK TO SPECIFIC CHAPTER - // FF FF 21 0C 50 73 00 00 00 00 12 FF (SEEK TO NEXT CHAPTER) - // FF FF 21 0C 50 61 00 00 00 00 24 PREV (SEEK TO PREVIOUS CHAPTER) + create_packet(status, NO_RESPONSE); + } + else if (m_command[3] == 0x0c && m_command[4] == 0x44 && m_command[5] == 0x60) + { + // FF FF 21 0C 44 60 00 00 00 00 31 STOP + LOGCMD("xvd701: Playback STOP\n"); - m_response[0] = 0xff; - m_response[1] = 0xfe; - m_response[2] = 0x7f; - m_response[3] = 0x7e; - m_response[4] = 0x7d; - m_response[5] = 0x7c; - m_response[6] = 0x7b; - m_response[7] = 0x7a; - m_response[8] = 0x79; - m_response[9] = 0x78; - m_response[10] = 0x77; - m_response_index = 0; + m_playback_status = STATUS_STOP; + create_packet(STATUS_OK, NO_RESPONSE); + } + else if (m_command[3] == 0x0c && m_command[4] == 0x50 && m_command[5] == 0x20) + { + // FF FF 21 0C 50 20 00 00 00 00 63 SEEK TO SPECIFIC CHAPTER + auto chapter = ((m_command[6] % 10) * 100) + ((m_command[7] % 10) * 10) + (m_command[8] % 10); - m_timer_response->adjust(attotime::from_msec(100)); + if (m_media_type == JVC_MEDIA_VCD) + { + // VCD can only go to 99, so it sticks the data in the first two spots + chapter /= 10; + } + + auto status = seek_chapter(chapter); + LOGCMD("xvd701: Seek chapter %d -> %d\n", chapter, status); + create_packet(status ? STATUS_OK : STATUS_ERROR, NO_RESPONSE); + } + else if (m_command[3] == 0x0c && m_command[4] == 0x50 && m_command[5] == 0x61) + { + // FF FF 21 0C 50 61 00 00 00 00 24 PREV (SEEK TO PREVIOUS CHAPTER) + auto chapter = m_chapter - 1; + if (m_playback_status != STATUS_PLAYING && chapter == 0) + chapter = 1; + + auto status = seek_chapter(chapter); + LOGCMD("xvd701: Seek prev -> %d\n", status); + create_packet(status ? STATUS_OK : STATUS_ERROR, NO_RESPONSE); + } + else if (m_command[3] == 0x0c && m_command[4] == 0x50 && m_command[5] == 0x73) + { + // FF FF 21 0C 50 73 00 00 00 00 12 FF (SEEK TO NEXT CHAPTER) + auto status = seek_chapter(m_chapter + 1); + LOGCMD("xvd701: Seek FF -> %d\n", status); + create_packet(status ? STATUS_OK : STATUS_ERROR, NO_RESPONSE); + } + + else if (m_command[3] == 0x3e && m_command[4] == 0x40 && m_command[5] == 0x60) + { + // FF FF 21 3E 40 60 00 00 00 00 03 DEVICE OFF + LOGCMD("xvd701: Device OFF\n"); + + auto status = m_is_powered ? STATUS_OK : STATUS_ERROR; + if (m_is_powered) + m_is_powered = false; + + create_packet(status, NO_RESPONSE); + + } + else if (m_command[3] == 0x3e && m_command[4] == 0x40 && m_command[5] == 0x70) + { + // FF FF 21 3E 40 70 00 00 00 00 73 DEVICE ON + LOGCMD("xvd701: Device ON\n"); + + auto status = !m_is_powered ? STATUS_OK : STATUS_ERROR; + if (!m_is_powered) + m_is_powered = true; + + create_packet(status, NO_RESPONSE); + } + else if (m_command[3] == 0x3e && m_command[4] == 0x4e && m_command[5] == 0x20) + { + LOGCMD("xvd701: Device power status request\n"); + const unsigned char response[6] = { m_is_powered, 0x20, 0, 0, 0, 0 }; + create_packet(STATUS_OK, response); + } + else if (m_command[3] == 0x7c && m_command[4] == 0x41) + { + auto new_id = m_command[5]; + LOGCMD("xvd701: Change JLIP ID to %02x\n", new_id); + + if (new_id > 0 && new_id < 64) + { + m_jlip_id = new_id; + create_packet(STATUS_OK, NO_RESPONSE); + } + else + { + create_packet(STATUS_ERROR, NO_RESPONSE); + } + } + else if (m_command[3] == 0x7c && m_command[4] == 0x45 && m_command[5] == 0x00) + { + LOGCMD("xvd701: Machine code request\n"); + + const unsigned char response[6] = { 0x00, 0x01, 0x03, 0x00, 0x03, 0x01 }; + create_packet(STATUS_OK, response); + } + else if (m_command[3] == 0x7c && m_command[4] == 0x48 && m_command[5] == 0x20) + { + LOGCMD("xvd701: Baud rate request\n"); + + // Hardcoded to 9600 baud + const unsigned char response[6] = { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 }; + create_packet(STATUS_OK, response); + } + else if (m_command[3] == 0x7c && m_command[4] == 0x49 && m_command[5] == 0x00) + { + LOGCMD("xvd701: Device code request\n"); + + const unsigned char response[6] = { 0x03, 0x0C, 0x7F, 0x7F, 0x7F, 0x7F }; + create_packet(STATUS_OK, response); + } + else if (m_command[3] == 0x7c && m_command[4] == 0x4c && m_command[5] == 0x00) + { + LOGCMD("xvd701: Device name first half request\n"); + + const unsigned char response[6] = { 'D', 'V', 'D', ' ', 'P', 'L' }; + create_packet(STATUS_OK, response); + } + else if (m_command[3] == 0x7c && m_command[4] == 0x4d && m_command[5] == 0x00) + { + LOGCMD("xvd701: Device name last half request\n"); + + const unsigned char response[6] = { 'A', 'Y', 'E', 'R', 0x7F, 0x7F }; + create_packet(STATUS_OK, response); + } + else if (m_command[3] == 0x7c && m_command[4] == 0x4e && m_command[5] == 0x20) + { + LOGCMD("xvd701: NOP request\n"); + + const unsigned char response[6] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; + create_packet(STATUS_OK, response); + } } } diff --git a/src/devices/bus/rs232/xvd701.h b/src/devices/bus/rs232/xvd701.h index 3e4bc782972..da9fbce0502 100644 --- a/src/devices/bus/rs232/xvd701.h +++ b/src/devices/bus/rs232/xvd701.h @@ -28,13 +28,45 @@ protected: private: static constexpr int TIMER_RESPONSE = 1; + enum jvc_xvd701_media_type : uint32_t + { + JVC_MEDIA_VCD = 0, + JVC_MEDIA_DVD = 1, + }; + + enum jvc_xvd701_playback_status : uint32_t + { + STATUS_STOP = 0, + STATUS_PLAYING = 1, + STATUS_PAUSE = 2, + }; + void send_response(); unsigned char sum(unsigned char *buffer, int length); + void create_packet(unsigned char status, const unsigned char response[6]); + + bool seek_chapter(int chapter); + + jvc_xvd701_media_type m_media_type; unsigned char m_command[11]; unsigned char m_response[11]; int m_response_index; emu_timer *m_timer_response; + + jvc_xvd701_playback_status m_playback_status; + + unsigned char m_jlip_id; + bool m_is_powered; + + int m_chapter; + + enum : unsigned char { + STATUS_UNKNOWN_COMMAND = 1, + STATUS_OK = 3, + STATUS_ERROR = 5, + }; + const unsigned char NO_RESPONSE[6] = { 0 }; }; DECLARE_DEVICE_TYPE(JVC_XVD701, jvc_xvd701_device) diff --git a/src/mame/drivers/twinkle.cpp b/src/mame/drivers/twinkle.cpp index 615b0b2ee2f..0c6e31c18b7 100644 --- a/src/mame/drivers/twinkle.cpp +++ b/src/mame/drivers/twinkle.cpp @@ -305,15 +305,18 @@ public: } void twinklex(machine_config &config); + void twinklex2(machine_config &config); void twinklei(machine_config &config); void twinkle(machine_config &config); + void twinkle_dvd_type1(machine_config &config); + void twinkle_dvd_type2(machine_config &config); private: virtual void machine_start() override; void twinkle_io_w(offs_t offset, uint8_t data); uint8_t twinkle_io_r(offs_t offset); - void twinkle_output_w(offs_t offset, uint16_t data); + void twinkle_videomixer_w(offs_t offset, uint16_t data); void led_w(uint16_t data); void key_led_w(uint16_t data); void serial_w(uint16_t data); @@ -691,21 +694,71 @@ uint8_t twinkle_state::twinkle_io_r(offs_t offset) return data; } -void twinkle_state::twinkle_output_w(offs_t offset, uint16_t data) +void twinkle_state::twinkle_videomixer_w(offs_t offset, uint16_t data) { + // Bt812 NTSC/PAL to RGB/YCrCb Decoder chip switch( offset ) { case 0x00: - /* offset */ + /* + Address Register offset + 0x00 Command Register 0, Input Select Register + 0x01 Reserved + 0x02 Command Register 2, Status Register + 0x03 Command Register 3, Output Format Register + 0x04 Command Register 4, Operation Mode Select Register + 0x05 Command Register 5, Input Format Register + 0x06 Command Register 6, Clock Definition Register + 0x07 Command Register 7, Video Timing Definition Register + 0x08 Brightness Adjust Register (range: -64 to +63) + 0x09 Contrast Adjust Register (range: 0 to 198.44%) + 0x0a Saturation Adjust Register (range: 0 to 198.44%) + 0x0b Hue Adjust Register (range: -45 to +44.3) + 0x0c HCLOCK Low Register + 0x0d HCLOCK High Register + 0x0e HDELAY Low Register + 0x0f HDELAY High Register + 0x10 ACTIVE_PIXELS Low Register + 0x11 ACTIVE_PIXELS High Register + 0x12 VDELAY Low Register + 0x13 VDELAY High Register + 0x14 ACTIVE_LINES Low Register + 0x15 ACTIVE_LINES High Register + 0x16 P (subcarrier freq) Register 0 + 0x17 P (subcarrier freq) Register 1 + 0x18 P (subcarrier freq) Register 2 + 0x19 AGC Delay Register + 0x1a Burst Delay Register + 0x1b Sample Rate Conversion Low Register + 0x1c Sample Rate Conversion High Register + 0x1d Command Register 1D, Video Timing Polarity Register + 0x1e-0xfe Reserved + 0xff Software Reset + */ break; case 0x04: - /* data */ + /* + Register data + Uses offset given in the address register offset command. + + Game initialized values: + HCLOCK 853 + HDELAY 126 + ACTIVE_PIXELS 706 + VDELAY 22 + ACTIVE_LINES 16 + */ break; case 0x08: - // overlay enable? + // Status bits? + // Seen values: + // 0x01 - ? + // 0x02 - Perform overlay mixing + // 0x08 - ? break; case 0x10: { + // Always writes 0x214 and 0x128 here? int clock = (data >> 0) & 1; int _do = (data >> 1) & 1; int cs = (data >> 2) & 1; @@ -738,13 +791,13 @@ void twinkle_state::twinkle_output_w(offs_t offset, uint16_t data) } break; case 0x18: - /* ?? */ + // Always 0x69? break; case 0x30: - /* ?? */ + // Always 0x10? break; case 0x48: - /* ?? */ + // 0x20 - Powered on? break; } } @@ -835,7 +888,7 @@ void twinkle_state::main_map(address_map &map) map(0x1f280000, 0x1f280003).portr("INSEC"); map(0x1f290000, 0x1f29007f).rw("rtc", FUNC(rtc65271_device::rtc_r), FUNC(rtc65271_device::rtc_w)).umask32(0x00ff00ff); map(0x1f2a0000, 0x1f2a007f).rw("rtc", FUNC(rtc65271_device::xram_r), FUNC(rtc65271_device::xram_w)).umask32(0x00ff00ff); - map(0x1f2b0000, 0x1f2b00ff).w(FUNC(twinkle_state::twinkle_output_w)); + map(0x1f2b0000, 0x1f2b00ff).w(FUNC(twinkle_state::twinkle_videomixer_w)); } /* SPU board */ @@ -1094,21 +1147,6 @@ void twinkle_state::twinkle(machine_config &config) FDC37C665GT(config, "fdc37c665gt", XTAL(24'000'000)); - rs232_port_device &rs232(RS232_PORT(config, "rs232", 0)); - rs232.option_add("xvd701", JVC_XVD701); -// rs232.option_add("xvs1100", JVC_XVS1100); // 8th mix only - rs232.set_default_option("xvd701"); - rs232.rxd_handler().set("fdc37c665gt:uart2", FUNC(ins8250_uart_device::rx_w)); - rs232.dcd_handler().set("fdc37c665gt:uart2", FUNC(ins8250_uart_device::dcd_w)); - rs232.dsr_handler().set("fdc37c665gt:uart2", FUNC(ins8250_uart_device::dsr_w)); - rs232.ri_handler().set("fdc37c665gt:uart2", FUNC(ins8250_uart_device::ri_w)); - rs232.cts_handler().set("fdc37c665gt:uart2", FUNC(ins8250_uart_device::cts_w)); - - ns16550_device &uart(*subdevice("fdc37c665gt:uart2")); - uart.out_tx_callback().set("rs232", FUNC(rs232_port_device::write_txd)); - uart.out_dtr_callback().set("rs232", FUNC(rs232_port_device::write_dtr)); - uart.out_rts_callback().set("rs232", FUNC(rs232_port_device::write_rts)); - /* video hardware */ CXD8561Q(config, "gpu", XTAL(53'693'175), 0x200000, subdevice("maincpu")).set_screen("screen"); @@ -1130,15 +1168,82 @@ void twinkle_state::twinkle(machine_config &config) rf5c400.add_route(1, "speakerright", 1.0); } +void twinkle_state::twinkle_dvd_type1(machine_config &config) +{ + // All versions before beatmania IIDX 2nd style + // - maincpu:sio1 (PSX SIO1) is used for the DVD player + // - fdc37c665gt:uart2 is used for network/session play + rs232_port_device &rs232(RS232_PORT(config, "rs232_dvd", 0)); + rs232.option_add("xvd701", JVC_XVD701); + rs232.set_default_option("xvd701"); + + // TODO: CTS isn't implemented in SIO but it's required to fix the DVD player error for very early IIDX games + auto sio1 = subdevice("maincpu:sio1"); + rs232.rxd_handler().set(*sio1, FUNC(psxsio1_device::write_rxd)); + rs232.dsr_handler().set(*sio1, FUNC(psxsio1_device::write_dsr)); + //rs232.cts_handler().set(*sio1, FUNC(psxsio1_device::write_cts)); + sio1->txd_handler().set(rs232, FUNC(rs232_port_device::write_txd)); + sio1->dtr_handler().set(rs232, FUNC(rs232_port_device::write_dtr)); + sio1->rts_handler().set(rs232, FUNC(rs232_port_device::write_rts)); + + ns16550_device &uart(*subdevice("fdc37c665gt:uart2")); + rs232_port_device &rs232_network(RS232_PORT(config, "rs232_network", default_rs232_devices, nullptr)); + uart.out_tx_callback().set(rs232_network, FUNC(rs232_port_device::write_txd)); + uart.out_dtr_callback().set(rs232_network, FUNC(rs232_port_device::write_dtr)); + uart.out_rts_callback().set(rs232_network, FUNC(rs232_port_device::write_rts)); + rs232_network.rxd_handler().set(uart, FUNC(ns16550_device::rx_w)); + rs232_network.dcd_handler().set(uart, FUNC(ns16550_device::dcd_w)); + rs232_network.dsr_handler().set(uart, FUNC(ns16550_device::dsr_w)); + rs232_network.ri_handler().set(uart, FUNC(ns16550_device::ri_w)); + rs232_network.cts_handler().set(uart, FUNC(ns16550_device::cts_w)); +} + +void twinkle_state::twinkle_dvd_type2(machine_config &config) +{ + // All versions starting from beatmania IIDX 2nd style + // - fdc37c665gt:uart2 is used for the DVD player + // - maincpu:sio1 (PSX SIO1) is used for network/session play + rs232_port_device &rs232(RS232_PORT(config, "rs232_dvd", 0)); + rs232.option_add("xvd701", JVC_XVD701); + // rs232.option_add("xvs1100", JVC_XVS1100); // 8th mix only + rs232.set_default_option("xvd701"); + + auto sio1 = subdevice("maincpu:sio1"); + rs232_port_device &rs232_network(RS232_PORT(config, "rs232_network", default_rs232_devices, nullptr)); + sio1->txd_handler().set(rs232_network, FUNC(rs232_port_device::write_txd)); + sio1->dtr_handler().set(rs232_network, FUNC(rs232_port_device::write_dtr)); + rs232_network.rxd_handler().set(*sio1, FUNC(psxsio1_device::write_rxd)); + rs232_network.dsr_handler().set(*sio1, FUNC(psxsio1_device::write_dsr)); + + ns16550_device &uart(*subdevice("fdc37c665gt:uart2")); + uart.out_tx_callback().set(rs232, FUNC(rs232_port_device::write_txd)); + uart.out_dtr_callback().set(rs232, FUNC(rs232_port_device::write_dtr)); + uart.out_rts_callback().set(rs232, FUNC(rs232_port_device::write_rts)); + rs232.rxd_handler().set(uart, FUNC(ns16550_device::rx_w)); + rs232.dcd_handler().set(uart, FUNC(ns16550_device::dcd_w)); + rs232.dsr_handler().set(uart, FUNC(ns16550_device::dsr_w)); + rs232.ri_handler().set(uart, FUNC(ns16550_device::ri_w)); + rs232.cts_handler().set(uart, FUNC(ns16550_device::cts_w)); +} + void twinkle_state::twinklex(machine_config &config) { twinkle(config); + twinkle_dvd_type1(config); + X76F041(config, "security"); +} + +void twinkle_state::twinklex2(machine_config &config) +{ + twinkle(config); + twinkle_dvd_type2(config); X76F041(config, "security"); } void twinkle_state::twinklei(machine_config &config) { twinkle(config); + twinkle_dvd_type2(config); I2C_M24C02(config, "security", 0); // M24C02-W } @@ -1530,9 +1635,9 @@ GAMEL( 1999, bmiidx, gq863, twinklex, twinklex, twinkle_state, empty_init, R GAMEL( 1999, bmiidxa, bmiidx, twinklex, twinklex, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX (863 JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING, layout_bmiidx ) GAMEL( 1999, bmiidxc, gq863, twinklex, twinklex, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX with DDR 2nd Club Version (896 JAB)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING, layout_bmiidx ) GAMEL( 1999, bmiidxca, bmiidxc, twinklex, twinklex, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX with DDR 2nd Club Version (896 JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING, layout_bmiidx ) -GAMEL( 1999, bmiidxs, gq863, twinklex, twinklex, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX Substream (983 JAA)", MACHINE_IMPERFECT_SOUND, layout_bmiidx ) -GAMEL( 1999, bmiidxsa, bmiidxs, twinklex, twinklex, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX Substream (983-AA JAA)", MACHINE_IMPERFECT_SOUND, layout_bmiidx ) -GAMEL( 1999, bmiidxc2, gq863, twinklex, twinklex, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX Substream with DDR 2nd Club Version 2 (984 A01 BM)", MACHINE_IMPERFECT_SOUND, layout_bmiidx ) +GAMEL( 1999, bmiidxs, gq863, twinklex2,twinklex, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX Substream (983 JAA)", MACHINE_IMPERFECT_SOUND, layout_bmiidx ) +GAMEL( 1999, bmiidxsa, bmiidxs, twinklex2,twinklex, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX Substream (983-AA JAA)", MACHINE_IMPERFECT_SOUND, layout_bmiidx ) +GAMEL( 1999, bmiidxc2, gq863, twinklex2,twinklex, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX Substream with DDR 2nd Club Version 2 (984 A01 BM)", MACHINE_IMPERFECT_SOUND, layout_bmiidx ) GAMEL( 1999, bmiidx2, gq863, twinklei, twinklei, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX 2nd style (GC985 JAA)", MACHINE_IMPERFECT_SOUND, layout_bmiidx ) GAMEL( 2000, bmiidx3, gq863, twinklei, twinklei, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX 3rd style (GC992 JAC)", MACHINE_IMPERFECT_SOUND, layout_bmiidx ) GAMEL( 2000, bmiidx3b, bmiidx3, twinklei, twinklei, twinkle_state, empty_init, ROT0, "Konami", "beatmania IIDX 3rd style (GC992 JAB)", MACHINE_IMPERFECT_SOUND, layout_bmiidx )