xvd701, twinkle: Implemented commands required for Twinkle DVD player. (#9114)

This commit is contained in:
987123879113 2022-04-10 00:45:06 +09:00 committed by GitHub
parent cf2982a769
commit 1d19337feb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 362 additions and 57 deletions

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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<ns16550_device>("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<psxcpu_device>("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<psxsio1_device>("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<ns16550_device>("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<psxsio1_device>("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<ns16550_device>("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 )