From ee6e82013d9c6d9850d87c4fed778993c859e92a Mon Sep 17 00:00:00 2001 From: 987123879113 <63495610+987123879113@users.noreply.github.com> Date: Tue, 12 Jan 2021 23:26:58 +0900 Subject: ksys573.cpp, machine/k573mcr.cpp: Implemented JVS memory card reader device for System 573. (#7659) --- src/devices/machine/jvsdev.h | 2 +- src/mame/drivers/ksys573.cpp | 390 ++++++++++++++++++++++++++-------- src/mame/machine/k573mcr.cpp | 489 ++++++++++++++++++++++++++++++++++++++++++- src/mame/machine/k573mcr.h | 64 +++++- 4 files changed, 841 insertions(+), 104 deletions(-) diff --git a/src/devices/machine/jvsdev.h b/src/devices/machine/jvsdev.h index 7a93b3ead04..f7dba299c97 100644 --- a/src/devices/machine/jvsdev.h +++ b/src/devices/machine/jvsdev.h @@ -37,6 +37,7 @@ protected: virtual bool analogs(uint8_t *&buf, uint8_t count); virtual bool swoutputs(uint8_t count, const uint8_t *vals); virtual bool swoutputs(uint8_t id, uint8_t val); + virtual int handle_message(const uint8_t *send_buffer, uint32_t send_size, uint8_t *&recv_buffer); required_device host; @@ -45,7 +46,6 @@ private: uint8_t jvs_address; uint32_t jvs_reset_counter; - int handle_message(const uint8_t *send_buffer, uint32_t send_size, uint8_t *&recv_buffer); }; #endif // MAME_MACHINE_JVSDEV_H diff --git a/src/mame/drivers/ksys573.cpp b/src/mame/drivers/ksys573.cpp index 5dda0ae561e..789b8c7da60 100644 --- a/src/mame/drivers/ksys573.cpp +++ b/src/mame/drivers/ksys573.cpp @@ -352,6 +352,7 @@ G: gun mania only, drives air soft gun (this game uses real BB bullet) #include "machine/adc083x.h" #include "machine/bankdev.h" #include "machine/ds2401.h" +#include "machine/jvshost.h" #include "machine/linflash.h" #include "machine/k573cass.h" #include "machine/k573dio.h" @@ -370,11 +371,104 @@ G: gun mania only, drives air soft gun (this game uses real BB bullet) #include "screen.h" #include "speaker.h" +#define LOG_GENERAL (1 << 0) +#define LOG_CDROM (1 << 1) +#define LOG_CONTROL (1 << 2) +#define LOG_SECURITY (1 << 3) +#define LOG_JVS (1 << 4) +#define LOG_IOBOARD (1 << 5) +// #define VERBOSE (LOG_GENERAL | LOG_CDROM | LOG_CONTROL | LOG_SECURITY | LOG_JVS | LOG_IOBOARD) +// #define LOG_OUTPUT_STREAM std::cout -#define VERBOSE_LEVEL ( 0 ) +#include "logmacro.h" + +#define LOGCDROM(...) LOGMASKED(LOG_CDROM, __VA_ARGS__) +#define LOGCONTROL(...) LOGMASKED(LOG_CONTROL, __VA_ARGS__) +#define LOGSECURITY(...) LOGMASKED(LOG_SECURITY, __VA_ARGS__) +#define LOGJVS(...) LOGMASKED(LOG_JVS, __VA_ARGS__) +#define LOGIOBOARD(...) LOGMASKED(LOG_IOBOARD, __VA_ARGS__) #define ATAPI_CYCLES_PER_SECTOR ( 5000 ) // plenty of time to allow DMA setup etc. BIOS requires this be at least 2000, individual games may vary. +/* + * Class declaration for sys573_jvs_host + */ + +DECLARE_DEVICE_TYPE(SYS573_JVS_HOST, sys573_jvs_host) + +class sys573_jvs_host : public jvs_host +{ +public: + // construction/destruction + sys573_jvs_host(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + void send_packet(uint8_t *data, int length); + int received_packet(uint8_t *buffer); + + DECLARE_READ_LINE_MEMBER( jvs_sense_r ); + +private: + int output_buffer_size = 0; +}; + +DEFINE_DEVICE_TYPE(SYS573_JVS_HOST, sys573_jvs_host, "sys573_jvs_host", "JVS Host (System 573)") + +sys573_jvs_host::sys573_jvs_host(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : jvs_host(mconfig, SYS573_JVS_HOST, tag, owner, clock) +{ + output_buffer_size = 0; +} + +READ_LINE_MEMBER( sys573_jvs_host::jvs_sense_r ) +{ + return !get_address_set_line(); +} + +int sys573_jvs_host::received_packet(uint8_t *buffer) +{ + if (jvs_sense_r()) { + // The game will send the command twice to reset, but the command + // shouldn't return any data or else a "JVS SUBS RESET ERROR" appears + return 0; + } + + uint32_t length; + const uint8_t *data; + + get_raw_reply(data, length); + + if (length > 0) { + // The games don't unescape the data in memory. + // This causes issues any time 0xe0 or 0xd0 shows up in + // the original response data and were escaped. + // Sending an unescaped "encoded" packet works perfectly + // in-game. + uint8_t checksum = std::accumulate(data, data + length, 0); + + buffer[0] = 0xe0; + memcpy(buffer + 1, data, length); + buffer[length+1] = checksum; + buffer[length+2] = 0; + length += 2; + + commit_encoded(); + } + + return (int)length; +} + +void sys573_jvs_host::send_packet(uint8_t *data, int length) +{ + while (length > 0) + { + push(*data); + data++; + length--; + } + + commit_raw(); +} + + class ksys573_state : public driver_device { public: @@ -394,6 +488,7 @@ public: m_maincpu(*this, "maincpu"), m_ram(*this, "maincpu:ram"), m_flashbank(*this, "flashbank"), + m_in2(*this, "IN2"), m_out1(*this, "OUT1"), m_out2(*this, "OUT2"), m_cd(*this, "CD"), @@ -404,7 +499,8 @@ public: m_encoder(*this, "ENCODER"), m_gunmania_id(*this, "gunmania_id"), m_duart(*this, "mb89371"), - m_lamps(*this, "lamp%u", 0U) + m_lamps(*this, "lamp%u", 0U), + m_sys573_jvs_host(*this, "sys573_jvs_host") { } void drmn9m(machine_config &config); @@ -422,6 +518,7 @@ public: void ddrsbm(machine_config &config); void ddr3mp(machine_config &config); void dsftkd(machine_config &config); + void dsfdct(machine_config &config); void dsfdcta(machine_config &config); void mamboagga(machine_config &config); void gunmania(machine_config &config); @@ -433,7 +530,9 @@ public: void animechmp(machine_config &config); void salarymc(machine_config &config); void gbbchmp(machine_config &config); + void ddr2mc2(machine_config &config); void ddr2ml(machine_config &config); + void ddrbocd(machine_config &config); void konami573(machine_config &config); void drmn2m(machine_config &config); void gtrfrk3m(machine_config &config); @@ -448,6 +547,7 @@ public: void konami573x(machine_config &config); void ddrusa(machine_config &config); void dsem(machine_config &config); + void dsem2(machine_config &config); void dmx(machine_config &config); void drmn(machine_config &config); void k573d(machine_config &config); @@ -507,6 +607,8 @@ public: DECLARE_WRITE_LINE_MEMBER( h8_clk_w ); + DECLARE_READ_LINE_MEMBER( jvs_rx_r ); + double m_pad_position[ 6 ]; optional_ioport m_pads; @@ -516,6 +618,12 @@ protected: virtual void driver_start() override; private: + bool jvs_is_valid_packet(); + + void jvs_input_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + uint16_t jvs_input_r(offs_t offset, uint16_t mem_mask = ~0); + + uint16_t port_in2_jvs_r(offs_t offset, uint16_t mem_mask = ~0); uint16_t control_r(offs_t offset, uint16_t mem_mask = ~0); void control_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); @@ -564,7 +672,6 @@ private: required_ioport m_analog2; required_ioport m_analog3; - inline void ATTR_PRINTF( 3,4 ) verboselog( int n_level, const char *s_fmt, ... ); void update_disc(); void gx700pwbf_output( int offset, uint8_t data ); void gx700pwfbf_init( void ( ksys573_state::*output_callback_func )( ATTR_UNUSED offs_t offset, ATTR_UNUSED uint8_t data ) ); @@ -620,9 +727,15 @@ private: int m_tank_shutter_position; int m_cable_holder_release; + int m_jvs_input_idx_r, m_jvs_input_idx_w; + int m_jvs_output_idx_w, m_jvs_output_len_w; + uint8_t m_jvs_input_buffer[512]; + uint8_t m_jvs_output_buffer[512]; + required_device m_maincpu; required_device m_ram; required_device m_flashbank; + required_ioport m_in2; required_ioport m_out1; required_ioport m_out2; required_ioport m_cd; @@ -634,27 +747,16 @@ private: optional_device m_gunmania_id; optional_device m_duart; output_finder<2> m_lamps; -}; -void ATTR_PRINTF( 3,4 ) ksys573_state::verboselog( int n_level, const char *s_fmt, ... ) -{ - if( VERBOSE_LEVEL >= n_level ) - { - va_list v; - char buf[ 32768 ]; - va_start( v, s_fmt ); - vsprintf( buf, s_fmt, v ); - va_end( v ); - logerror( "%s: %s", machine().describe_context(), buf ); - } -} + required_device m_sys573_jvs_host; +}; void ksys573_state::konami573_map(address_map &map) { map(0x1f000000, 0x1f3fffff).m(m_flashbank, FUNC(address_map_bank_device::amap16)); map(0x1f400000, 0x1f400003).portr("IN0").portw("OUT0"); map(0x1f400004, 0x1f400007).portr("IN1"); - map(0x1f400008, 0x1f40000b).portr("IN2"); + map(0x1f400008, 0x1f40000b).r(FUNC(ksys573_state::port_in2_jvs_r)); map(0x1f40000c, 0x1f40000f).portr("IN3"); map(0x1f480000, 0x1f48000f).rw(m_ata, FUNC(ata_interface_device::cs0_r), FUNC(ata_interface_device::cs0_w)); map(0x1f500000, 0x1f500001).rw(FUNC(ksys573_state::control_r), FUNC(ksys573_state::control_w)); // Konami can't make a game without a "control" register. @@ -662,6 +764,7 @@ void ksys573_state::konami573_map(address_map &map) map(0x1f5c0000, 0x1f5c0003).nopw(); // watchdog? map(0x1f600000, 0x1f600003).portw("LAMPS"); map(0x1f620000, 0x1f623fff).rw("m48t58", FUNC(timekeeper_device::read), FUNC(timekeeper_device::write)).umask32(0x00ff00ff); + map(0x1f680000, 0x1f680001).rw(FUNC(ksys573_state::jvs_input_r), FUNC(ksys573_state::jvs_input_w)); map(0x1f6a0000, 0x1f6a0001).rw(FUNC(ksys573_state::security_r), FUNC(ksys573_state::security_w)); } @@ -710,9 +813,104 @@ void ksys573_state::gbbchmp_map(address_map& map) map(0x1f640000, 0x1f640007).rw(m_duart, FUNC(mb89371_device::read), FUNC(mb89371_device::write)).umask32(0x00ff00ff); } +bool ksys573_state::jvs_is_valid_packet() +{ + if (m_jvs_input_idx_w < 5) { + // A valid packet will have at the very least + // - sync (0xe0) + // - node number (non-zero) + // - size + // - at least 1 byte in the request message + // - checksum + return false; + } + + if (m_jvs_input_buffer[0] != 0xe0 || m_jvs_input_buffer[1] == 0x00) { + return false; + } + + int command_size = m_jvs_input_buffer[2] + 3; + if (m_jvs_input_idx_w < command_size) { + return false; + } + + uint8_t checksum = 0; + for (int i = 1; i < command_size - 1; i++) { + checksum += m_jvs_input_buffer[i]; + } + + return checksum == m_jvs_input_buffer[command_size - 1]; +} + +void ksys573_state::jvs_input_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + m_jvs_input_buffer[m_jvs_input_idx_w++] = data & 0xff; + m_jvs_input_buffer[m_jvs_input_idx_w++] = data >> 8; + + if (m_jvs_input_buffer[0] != 0xe0) { + m_jvs_input_idx_w = 0; + } + + if (jvs_is_valid_packet()) { + LOGJVS("jvs_input_w( %08x, %08x, %02x %02x )\n", offset, mem_mask, data & 0xff, data >> 8 ); + for (int i = 0; i < m_jvs_input_idx_w; i++) + LOGJVS("%02x ", m_jvs_input_buffer[i]); + LOGJVS("\n"); + + int command_size = m_jvs_input_buffer[2] + 3; + m_sys573_jvs_host->send_packet(m_jvs_input_buffer + 1, command_size - 2); // jvshost doesn't actually check the checksum, so don't send it + + m_jvs_input_idx_w = 0; + m_jvs_input_idx_r = 0; + + m_jvs_input_buffer[0] = 0; + } +} + +uint16_t ksys573_state::jvs_input_r(offs_t offset, uint16_t mem_mask) +{ + uint16_t data = m_jvs_input_buffer[m_jvs_input_idx_r++]; + data |= m_jvs_input_buffer[m_jvs_input_idx_r++] << 8; + + return data; +} + +uint16_t ksys573_state::port_in2_jvs_r(offs_t offset, uint16_t mem_mask) +{ + if (offset == 0) { + // 0x1f400008-0x1f400009 are for inputs + return m_in2->read(); + } + + if (m_jvs_output_len_w <= 0) { + return 0; + } + + uint16_t data = m_jvs_output_buffer[m_jvs_output_idx_w] | (m_jvs_output_buffer[m_jvs_output_idx_w+1] << 8); + m_jvs_output_idx_w += 2; + + if (m_jvs_output_idx_w >= m_jvs_output_len_w) { + m_jvs_output_idx_w = 0; + m_jvs_output_len_w = 0; + } + + LOGJVS("m_jvs_output_r %08x %08x | %02x %02x | %02x\n", offset, mem_mask, data & 0xff, data >> 8, m_jvs_output_idx_w); + + return data; +} + +READ_LINE_MEMBER( ksys573_state::jvs_rx_r ) +{ + if (m_jvs_output_len_w <= 0) { + m_jvs_output_len_w = m_sys573_jvs_host->received_packet(m_jvs_output_buffer); + } + + return m_jvs_output_len_w > 0; +} + uint16_t ksys573_state::control_r(offs_t offset, uint16_t mem_mask) { - verboselog( 2, "control_r( %08x, %08x ) %08x\n", offset, mem_mask, m_control ); + LOGCONTROL( "control_r( %08x, %08x ) %08x\n", offset, mem_mask, m_control ); return m_control; } @@ -721,7 +919,7 @@ void ksys573_state::control_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA( &m_control ); - verboselog( 2, "control_w( %08x, %08x, %08x )\n", offset, mem_mask, data ); + LOGCONTROL( "control_w( %08x, %08x, %08x )\n", offset, mem_mask, data ); m_out2->write( data, mem_mask ); @@ -765,7 +963,7 @@ void ksys573_state::atapi_reset_w(uint16_t data) void ksys573_state::cdrom_dma_read( uint32_t *ram, uint32_t n_address, int32_t n_size ) { - verboselog( 2, "cdrom_dma_read( %08x, %08x )\n", n_address, n_size ); + LOGCDROM( "cdrom_dma_read( %08x, %08x )\n", n_address, n_size ); // osd_printf_debug( "DMA read: address %08x size %08x\n", n_address, n_size ); } @@ -773,7 +971,7 @@ void ksys573_state::cdrom_dma_write( uint32_t *ram, uint32_t n_address, int32_t { m_p_n_psxram = ram; - verboselog( 2, "cdrom_dma_write( %08x, %08x )\n", n_address, n_size ); + LOGCDROM( "cdrom_dma_write( %08x, %08x )\n", n_address, n_size ); // osd_printf_debug( "DMA write: address %08x size %08x\n", n_address, n_size ); m_atapi_xferbase = n_address; @@ -786,7 +984,7 @@ void ksys573_state::security_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA( &m_n_security_control ); - verboselog( 2, "security_w( %08x, %08x, %08x )\n", offset, mem_mask, data ); + LOGSECURITY( "security_w( %08x, %08x, %08x )\n", offset, mem_mask, data ); m_out1->write( data, mem_mask ); } @@ -794,7 +992,7 @@ void ksys573_state::security_w(offs_t offset, uint16_t data, uint16_t mem_mask) uint16_t ksys573_state::security_r(offs_t offset, uint16_t mem_mask) { uint16_t data = m_n_security_control; - verboselog( 2, "security_r( %08x, %08x ) %08x\n", offset, mem_mask, data ); + LOGSECURITY( "security_r( %08x, %08x ) %08x\n", offset, mem_mask, data ); return data; } @@ -834,10 +1032,6 @@ void ksys573_state::driver_start() m_available_cdroms[ 0 ] = cdrom_open(machine().rom_load().get_disk_handle(":cdrom0")); m_available_cdroms[ 1 ] = cdrom_open(machine().rom_load().get_disk_handle(":cdrom1")); - m_n_security_control = 0; - m_control = 0; - m_h8_index = 0; - save_item( NAME( m_n_security_control ) ); save_item( NAME( m_control ) ); } @@ -846,52 +1040,22 @@ void ksys573_state::machine_reset() { update_disc(); + m_n_security_control = 0; + m_control = 0; + m_h8_index = 0; m_h8_clk = 0; + + m_jvs_input_idx_r = m_jvs_input_idx_w = 0; + m_jvs_output_idx_w = m_jvs_output_len_w = 0; + + std::fill_n(m_jvs_input_buffer, sizeof(m_jvs_input_buffer), 0); + std::fill_n(m_jvs_output_buffer, sizeof(m_jvs_output_buffer), 0); } WRITE_LINE_MEMBER(ksys573_state::sys573_vblank) { update_disc(); - - /// TODO: emulate the memory controller board - if( strcmp( machine().system().name, "ddr2ml" ) == 0 ) - { - /* patch out security-plate error */ - - uint32_t *p_n_psxram = (uint32_t *) m_ram->pointer(); - - /* install cd */ - - /* 801e1540: jal $801e1f7c */ - if( p_n_psxram[ 0x1e1540 / 4 ] == 0x0c0787df ) - { - /* 801e1540: j $801e1560 */ - p_n_psxram[ 0x1e1540 / 4 ] = 0x08078558; - } - - /* flash */ - - /* 8001f850: jal $80031fd8 */ - if( p_n_psxram[ 0x1f850 / 4 ] == 0x0c00c7f6 ) - { - /* 8001f850: j $8001f888 */ - p_n_psxram[ 0x1f850 / 4 ] = 0x08007e22; - } - } - else if( strcmp( machine().system().name, "ddr2mla" ) == 0 ) - { - /* patch out security-plate error */ - - uint32_t *p_n_psxram = (uint32_t *) m_ram->pointer(); - - /* 8001f850: jal $8003221c */ - if( p_n_psxram[ 0x1f850 / 4 ] == 0x0c00c887 ) - { - /* 8001f850: j $8001f888 */ - p_n_psxram[ 0x1f850 / 4 ] = 0x08007e22; - } - } } // H8 check at startup (JVS related) @@ -952,11 +1116,11 @@ uint16_t ksys573_state::ge765pwbba_r(offs_t offset, uint16_t mem_mask) return m_upd4701->read_y(offset & 1); default: - verboselog( 0, "ge765pwbba_r: unhandled offset %08x %08x\n", offset, mem_mask ); + LOGIOBOARD( "ge765pwbba_r: unhandled offset %08x %08x\n", offset, mem_mask ); break; } - verboselog( 2, "ge765pwbba_r( %08x, %08x )\n", offset, mem_mask ); + LOGIOBOARD( "ge765pwbba_r( %08x, %08x )\n", offset, mem_mask ); return 0; } @@ -981,11 +1145,11 @@ void ksys573_state::ge765pwbba_w(offs_t offset, uint16_t data, uint16_t mem_mask break; default: - verboselog( 0, "ge765pwbba_w: unhandled offset %08x %08x %08x\n", offset, mem_mask, data ); + LOGIOBOARD( "ge765pwbba_w: unhandled offset %08x %08x %08x\n", offset, mem_mask, data ); break; } - verboselog( 2, "ge765pwbba_w( %08x, %08x, %08x )\n", offset, mem_mask, data ); + LOGIOBOARD( "ge765pwbba_w( %08x, %08x, %08x )\n", offset, mem_mask, data ); } /* @@ -1022,7 +1186,7 @@ uint16_t ksys573_state::gx700pwbf_io_r(offs_t offset, uint16_t mem_mask) break; } - verboselog( 2, "gx700pwbf_io_r( %08x, %08x ) %08x\n", offset, mem_mask, data ); + LOGIOBOARD( "gx700pwbf_io_r( %08x, %08x ) %08x\n", offset, mem_mask, data ); return data; } @@ -1048,7 +1212,7 @@ void ksys573_state::gx700pwbf_output( int offset, uint8_t data ) void ksys573_state::gx700pwbf_io_w(offs_t offset, uint16_t data, uint16_t mem_mask) { - verboselog( 2, "gx700pwbf_io_w( %08x, %08x, %08x )\n", offset, mem_mask, data ); + LOGIOBOARD( "gx700pwbf_io_w( %08x, %08x, %08x )\n", offset, mem_mask, data ); switch( offset ) { @@ -1076,7 +1240,7 @@ void ksys573_state::gx700pwbf_io_w(offs_t offset, uint16_t data, uint16_t mem_ma void ksys573_state::gx700pwfbf_init( void ( ksys573_state::*output_callback_func )( ATTR_UNUSED offs_t offset, ATTR_UNUSED uint8_t data ) ) { - memset( m_gx700pwbf_output_data, 0, sizeof( m_gx700pwbf_output_data ) ); + std::fill_n( m_gx700pwbf_output_data, sizeof( m_gx700pwbf_output_data ), 0); m_gx700pwfbf_output_callback = output_callback_func; @@ -1153,7 +1317,7 @@ void ksys573_state::gn845pwbb_clk_w( int offset, int data ) } } - verboselog( 2, "stage: %dp data clk=%d state=%d d0=%d shift=%08x bit=%d stage_mask=%08x\n", offset + 1, clk, + LOGIOBOARD( "stage: %dp data clk=%d state=%d d0=%d shift=%08x bit=%d stage_mask=%08x\n", offset + 1, clk, m_stage_state[ offset ].state, m_stage_state[ offset ].DO, m_stage_state[ offset ].shift, m_stage_state[ offset ].bit, m_stage_mask ); } @@ -1606,7 +1770,7 @@ WRITE_LINE_MEMBER( ksys573_state::animechmp_lamp_clock ) { if( ( m_serial_lamp_shift & ~0xfff ) != 0 ) { - verboselog( 0, "unknown bits in serial_lamp_shift %08x\n", m_serial_lamp_shift & ~0xfff ); + LOG( "unknown bits in serial_lamp_shift %08x\n", m_serial_lamp_shift & ~0xfff ); } output().set_value( "player 1 red", ( m_serial_lamp_shift >> 11 ) & 1 ); @@ -1670,7 +1834,7 @@ WRITE_LINE_MEMBER( ksys573_state::salarymc_lamp_clock ) { if( ( m_serial_lamp_shift & ~0xe38 ) != 0 ) { - verboselog( 0, "unknown bits in serial_lamp_shift %08x\n", m_serial_lamp_shift & ~0xe38 ); + LOG( "unknown bits in serial_lamp_shift %08x\n", m_serial_lamp_shift & ~0xe38 ); } output().set_value( "player 1 red", ( m_serial_lamp_shift >> 11 ) & 1 ); @@ -1871,7 +2035,7 @@ double konami573_cassette_xi_device::punchmania_inputs_callback(uint8_t input) int pads = state->m_pads->read(); for( int i = 0; i < 6; i++ ) { - if( ( pads & ( 1 << i ) ) != 0 ) + if( BIT( pads, i ) ) { pad_position[ i ] = 5; } @@ -2127,7 +2291,7 @@ void ksys573_state::gunmania_w(offs_t offset, uint16_t data, uint16_t mem_mask) break; } - verboselog( 2, "gunmania_w %08x %08x %08x\n", offset, mem_mask, data ); + LOGIOBOARD( "gunmania_w %08x %08x %08x\n", offset, mem_mask, data ); } READ_LINE_MEMBER( ksys573_state::gunmania_tank_shutter_sensor ) @@ -2165,7 +2329,7 @@ uint16_t ksys573_state::gunmania_r(offs_t offset, uint16_t mem_mask) break; } - verboselog( 2, "gunmania_r %08x %08x %08x\n", offset, mem_mask, data ); + LOGIOBOARD( "gunmania_r %08x %08x %08x\n", offset, mem_mask, data ); return data; } @@ -2250,6 +2414,8 @@ void ksys573_state::konami573(machine_config &config) adc0834_device &adc(ADC0834(config, "adc0834")); adc.set_input_callback(FUNC(ksys573_state::analogue_inputs_callback)); + + SYS573_JVS_HOST(config, m_sys573_jvs_host, 0); } // Variants with additional digital sound board @@ -2354,11 +2520,26 @@ void ksys573_state::ddr(machine_config &config) cassx(config); } +void ksys573_state::ddr2mc2(machine_config &config) +{ + k573a(config); + cassx(config); + + KONAMI_573_MEMORY_CARD_READER(config, "k573mcr", 0, m_sys573_jvs_host); +} + void ksys573_state::ddr2ml(machine_config &config) { k573a(config); - KONAMI_573_MEMORY_CARD_READER(config, "k573mcr", 0); + pccard1_16mb(config); + cassx(config); + KONAMI_573_MEMORY_CARD_READER(config, "k573mcr", 0, m_sys573_jvs_host); +} + +void ksys573_state::ddrbocd(machine_config &config) +{ + k573a(config); pccard1_16mb(config); cassx(config); } @@ -2370,6 +2551,8 @@ void ksys573_state::ddr3m(machine_config &config) pccard2_32mb(config); cassyyi(config); + + KONAMI_573_MEMORY_CARD_READER(config, "k573mcr", 0, m_sys573_jvs_host); } void ksys573_state::ddr3mp(machine_config &config) @@ -2379,6 +2562,8 @@ void ksys573_state::ddr3mp(machine_config &config) pccard2_32mb(config); cassxzi(config); + + KONAMI_573_MEMORY_CARD_READER(config, "k573mcr", 0, m_sys573_jvs_host); } void ksys573_state::ddrusa(machine_config &config) @@ -2396,10 +2581,21 @@ void ksys573_state::ddr5m(machine_config &config) pccard2_32mb(config); casszi(config); + + KONAMI_573_MEMORY_CARD_READER(config, "k573mcr", 0, m_sys573_jvs_host); } // Dancing Stage +void ksys573_state::dsfdct(machine_config &config) +{ + k573d(config); + subdevice("k573dio")->output_callback().set(FUNC(ksys573_state::ddr_output_callback)); + + pccard2_32mb(config); + cassyyi(config); +} + void ksys573_state::dsfdcta(machine_config &config) { k573a(config); @@ -2429,6 +2625,15 @@ void ksys573_state::dsem(machine_config &config) cassxi(config); } +void ksys573_state::dsem2(machine_config &config) +{ + k573d(config); + subdevice("k573dio")->output_callback().set(FUNC(ksys573_state::ddr_output_callback)); + + pccard2_32mb(config); + casszi(config); +} + // Dance Dance Revolution Solo void ksys573_state::ddrsolo(machine_config &config) @@ -2463,6 +2668,8 @@ void ksys573_state::ddr4ms(machine_config &config) pccard2_32mb(config); cassxzi(config); + + KONAMI_573_MEMORY_CARD_READER(config, "k573mcr", 0, m_sys573_jvs_host); } // DrumMania @@ -2524,6 +2731,9 @@ void ksys573_state::gtrfrk2m(machine_config &config) k573a(config); cassyi(config); pccard1_32mb(config); // HACK: The installation tries to check and erase 32mb but only flashes 16mb. + + // For Guitar Freaks 2nd Mix Link Ver 1 (memory cards) and Link Ver 2 (memory cards + controllers) + KONAMI_573_MEMORY_CARD_READER(config, "k573mcr", 0, m_sys573_jvs_host); } void ksys573_state::gtrfrk3m(machine_config &config) @@ -2531,6 +2741,8 @@ void ksys573_state::gtrfrk3m(machine_config &config) k573d(config); cassxzi(config); pccard1_16mb(config); + + KONAMI_573_MEMORY_CARD_READER(config, "k573mcr", 0, m_sys573_jvs_host); } void ksys573_state::gtrfrk5m(machine_config &config) @@ -2693,7 +2905,6 @@ void ksys573_state::mamboagga(machine_config &config) KONAMI_573_NETWORK_PCB_UNIT(config, "k573npu", 0); } - static INPUT_PORTS_START( konami573 ) PORT_START( "IN0" ) PORT_BIT( 0xffffffff, IP_ACTIVE_LOW, IPT_UNKNOWN ) @@ -2734,9 +2945,9 @@ static INPUT_PORTS_START( konami573 ) PORT_BIT( 0x00010000, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER( "adc0834", adc083x_device, do_read ) // PORT_BIT( 0x00020000, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x00040000, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER( "cassette", konami573_cassette_slot_device, read_line_secflash_sda ) - PORT_BIT( 0x00080000, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x00100000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) /* skip hang at startup */ - PORT_BIT( 0x00200000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) /* skip hang at startup */ + PORT_BIT( 0x00080000, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER( "sys573_jvs_host", sys573_jvs_host, jvs_sense_r ) + PORT_BIT( 0x00100000, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER( DEVICE_SELF, ksys573_state, jvs_rx_r ) + PORT_BIT( 0x00200000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) // JVS-related // PORT_BIT( 0x00400000, IP_ACTIVE_LOW, IPT_UNKNOWN ) // PORT_BIT( 0x00800000, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x01000000, IP_ACTIVE_LOW, IPT_COIN1 ) @@ -2763,7 +2974,6 @@ static INPUT_PORTS_START( konami573 ) PORT_BIT( 0x00000040, IP_ACTIVE_LOW, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER( "cassette", konami573_cassette_slot_device, write_line_zs01_sda ) PORT_START( "IN2" ) - PORT_BIT( 0xffff0000, IP_ACTIVE_HIGH, IPT_UNKNOWN ) PORT_BIT( 0x00000100, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER( 1 ) PORT_BIT( 0x00000200, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER( 1 ) PORT_BIT( 0x00000400, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER( 1 ) @@ -5329,9 +5539,9 @@ GAME( 1999, fbaitmca, fbaitmc, fbaitbc, fbaitmc, ksys573_state, empty_ini GAME( 1999, ddr2m, sys573, ddr, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dance Dance Revolution 2nd Mix (GN895 VER. JAA)", MACHINE_IMPERFECT_SOUND ) GAME( 1999, ddr2ml, ddr2m, ddr2ml, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dance Dance Revolution 2nd Mix - Link Ver (GE885 VER. JAB)", MACHINE_IMPERFECT_SOUND ) GAME( 1999, ddr2mla, ddr2m, ddr2ml, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dance Dance Revolution 2nd Mix - Link Ver (GE885 VER. JAA)", MACHINE_IMPERFECT_SOUND ) -GAME( 1999, ddrbocd, ddr2m, ddr2ml, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dance Dance Revolution Best of Cool Dancers (GE892 VER. JAA)", MACHINE_IMPERFECT_SOUND ) +GAME( 1999, ddrbocd, ddr2m, ddrbocd, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dance Dance Revolution Best of Cool Dancers (GE892 VER. JAA)", MACHINE_IMPERFECT_SOUND ) GAME( 1999, ddr2mc, ddr2m, ddr, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dance Dance Revolution 2nd Mix with beatmaniaIIDX CLUB VERSiON (GE896 VER. JAA)", MACHINE_IMPERFECT_SOUND ) -GAME( 1999, ddr2mc2, ddr2m, ddr, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dance Dance Revolution 2nd Mix with beatmaniaIIDX substream CLUB VERSiON 2 (GE984 VER. JAA)", MACHINE_IMPERFECT_SOUND ) +GAME( 1999, ddr2mc2, ddr2m, ddr2mc2, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dance Dance Revolution 2nd Mix with beatmaniaIIDX substream CLUB VERSiON 2 (GE984 VER. JAA)", MACHINE_IMPERFECT_SOUND ) GAME( 1999, gtrfrk2m, sys573, gtrfrk2m, gtrfrks, ksys573_state, empty_init, ROT0, "Konami", "Guitar Freaks 2nd Mix Ver 1.01 (GQ883 VER. JAD)", MACHINE_IMPERFECT_SOUND ) GAME( 1999, dsftkd, sys573, dsftkd, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dancing Stage featuring TRUE KiSS DESTiNATiON (G*884 VER. JAA)", MACHINE_IMPERFECT_SOUND ) GAME( 1999, cr589fw, sys573, konami573, konami573, ksys573_state, empty_init, ROT0, "Konami", "CD-ROM Drive Updater 2.0 (700B04)", MACHINE_IMPERFECT_SOUND ) @@ -5345,7 +5555,7 @@ GAME( 1999, ddrs2k, sys573, ddrs2k, ddrsolo, ksys573_state, empty_ini GAME( 1999, ddrs2kj, ddrs2k, ddrs2k, ddrsolo, ksys573_state, empty_init, ROT0, "Konami", "Dance Dance Revolution Solo 2000 (GC905 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.2 */ GAME( 1999, hypbbc2p, sys573, hypbbc2p, hypbbc2p, ksys573_state, init_hyperbbc, ROT0, "Konami", "Hyper Bishi Bashi Champ - 2 Player (GX908 1999/08/24 VER. JAA)", MACHINE_IMPERFECT_SOUND ) GAME( 1999, hypbbc2pk, hypbbc2p, hypbbc2p, hypbbc2p, ksys573_state, init_hyperbbc, ROT0, "Konami", "Hyper Bishi Bashi Champ - 2 Player (GX908 1999/08/24 VER. KAA)", MACHINE_IMPERFECT_SOUND ) -GAME( 1999, dsfdct, sys573, ddr3m, ddr, ksys573_state, empty_init, ROT0, "Konami", "Dancing Stage featuring Dreams Come True (GC910 VER. JCA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) +GAME( 1999, dsfdct, sys573, dsfdct, ddr, ksys573_state, empty_init, ROT0, "Konami", "Dancing Stage featuring Dreams Come True (GC910 VER. JCA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) GAME( 1999, dsfdcta, dsfdct, dsfdcta, ddr, ksys573_state, init_ddr, ROT0, "Konami", "Dancing Stage featuring Dreams Come True (GC910 VER. JAA)", MACHINE_IMPERFECT_SOUND ) GAME( 2000, pcnfrk2m, sys573, drmn2m, drmn, ksys573_state, empty_init, ROT0, "Konami", "Percussion Freaks 2nd Mix (GE912 VER. KAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.5 */ GAME( 1999, drmn2m, pcnfrk2m, drmn2m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 2nd Mix (GE912 VER. JAB)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.5 */ @@ -5396,12 +5606,12 @@ GAME( 2001, gtrfrk7m, sys573, gtrfrk7m, gtrfrks, ksys573_state, empty_ini GAME( 2001, ddrmax, sys573, ddr5m, ddr, ksys573_state, empty_init, ROT0, "Konami", "DDR Max - Dance Dance Revolution 6th Mix (G*B19 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.9 */ GAME( 2002, ddrmax2, sys573, ddr5m, ddr, ksys573_state, empty_init, ROT0, "Konami", "DDR Max 2 - Dance Dance Revolution 7th Mix (G*B20 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ GAME( 2002, mrtlbeat, sys573, ddr5m, ddr, ksys573_state, empty_init, ROT0, "Konami", "Martial Beat (G*B47 VER. JBA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.9 */ -GAME( 2002, gbbchmp, sys573, gbbchmp, hyperbbc, ksys573_state, init_serlamp, ROT0, "Konami", "Great Bishi Bashi Champ (GBA48 VER. JAB)", MACHINE_IMPERFECT_SOUND ) +GAME( 2002, gbbchmp, sys573, gbbchmp, hyperbbc, ksys573_state, init_serlamp, ROT0, "Konami", "Great Bishi Bashi Champ (GBA48 VER. JAB)", MACHINE_IMPERFECT_SOUND ) GAME( 2002, drmn7m, sys573, drmn4m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 7th Mix power-up ver. (G*C07 VER. JBA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ GAME( 2002, drmn7ma, drmn7m, drmn4m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 7th Mix (G*C07 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ GAME( 2002, gtrfrk8m, sys573, gtrfrk7m, gtrfrks, ksys573_state, empty_init, ROT0, "Konami", "Guitar Freaks 8th Mix power-up ver. (G*C08 VER. JBA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ GAME( 2002, gtrfrk8ma, gtrfrk8m, gtrfrk7m, gtrfrks, ksys573_state, empty_init, ROT0, "Konami", "Guitar Freaks 8th Mix (G*C08 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ -GAME( 2002, dsem2, sys573, ddr5m, ddr, ksys573_state, empty_init, ROT0, "Konami", "Dancing Stage Euro Mix 2 (G*C23 VER. EAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ +GAME( 2002, dsem2, sys573, dsem2, ddr, ksys573_state, empty_init, ROT0, "Konami", "Dancing Stage Euro Mix 2 (G*C23 VER. EAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ GAME( 2002, ddrextrm, sys573, ddr5m, ddr, ksys573_state, empty_init, ROT0, "Konami", "Dance Dance Revolution Extreme (G*C36 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ GAME( 2003, drmn8m, sys573, drmn4m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 8th Mix (G*C07 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ GAME( 2003, gtrfrk9m, sys573, gtrfrk7m, gtrfrks, ksys573_state, empty_init, ROT0, "Konami", "Guitar Freaks 9th Mix (G*C39 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */ diff --git a/src/mame/machine/k573mcr.cpp b/src/mame/machine/k573mcr.cpp index 1255fa50a20..19fa5669799 100644 --- a/src/mame/machine/k573mcr.cpp +++ b/src/mame/machine/k573mcr.cpp @@ -1,24 +1,485 @@ // license:BSD-3-Clause -// copyright-holders:smf +// copyright-holders:smf, windyfairy /* * Konami 573 Memory Card Reader - * - */ -#include "emu.h" -#include "k573mcr.h" + Main PCB Layout + --------------- + GE885-PWB(A)A + (C)1999 KONAMI CO. LTD. + |---------------------------------------| + | PQ30RV11 | + | |----------| |-------| CN61 | + | DIP8 | | | XCS05 | | + | | TMPR | | /10 | | + | | 3904AF | |-------| CN62 | + | | | | + | CN65 |----------| | + | 8.25 MHz CN67 | + | EP4M16 | + | DRAM4M | + | | + | --------| + | USB-A CN64 | + | | + | ADM485JR | + | USB-B CN63 | + |-------------------------------| -/* - GE885-PWB(A)A ( contains Toshiba tmpr3904af, ram, rom, tranceiver and glue ). +Notes: + DIP8 - 8-position DIP switch + CN61 - BS8PSHF1AA 8 pin connector, connects to memory card harness + CN62 - BS8PSHF1AA 8 pin connector + CN63 - 6P-SHVQ labeled "0", GE885-JB security dongle is connected here + CN64 - 6P-SHVQ labeled "1" + CN65 - B4PS-VH, 4 pin power connector + CN67 - BS15PSHF1AA, 15-pin connector, unpopulated + USB-A - USB-A connector + USB-B - USB-B connector, connects to USB on System 573 motherboard + ADM485JR - Analog Devices ADM485 low power EIA RS-485 transceiver + TMPR3904AF - Toshiba TMPR3904AF RISC Microprocessor + XCS05/10 - XILINX XCS10XL VQ100AKP9909 A2026631A + DRAM4M - Silicon Magic 66 MHz C9742 SM81C256K16CJ-35, 256K x 16 EDO DRAM + EP4M16 - ROM labeled "855-A01" */ +#include "emu.h" +#include "k573mcr.h" + k573mcr_device::k573mcr_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - device_t(mconfig, KONAMI_573_MEMORY_CARD_READER, tag, owner, clock) + jvs_device(mconfig, KONAMI_573_MEMORY_CARD_READER, tag, owner, clock), + m_controllers(*this, "controllers"), + m_meta(*this, "META") { + m_ram = std::make_unique(RAM_SIZE); } void k573mcr_device::device_start() { + jvs_device::device_start(); + + save_pointer(NAME(m_ram), RAM_SIZE); + save_item(NAME(m_is_memcard_initialized)); + save_item(NAME(m_status)); + save_item(NAME(m_psx_clock)); + save_item(NAME(m_psx_rx_data)); + save_item(NAME(m_psx_rx_bit)); +} + +void k573mcr_device::device_reset() +{ + jvs_device::device_reset(); + + std::fill_n(m_ram.get(), RAM_SIZE, 0); + m_is_memcard_initialized = false; + m_status = 0; + m_psx_clock = false; + m_psx_rx_data = 0; + m_psx_rx_bit = 0; +} + +void k573mcr_device::device_add_mconfig(machine_config &config) +{ + // The actual controllers are only used in Guitar Freaks and it was + // meant to be used with the home version PS1 guitar controller. + // The PS1 guitar controller isn't emulated in MAME and it doesn't + // make much sense to have it enabled by default. You can still select + // a controller through the slots menu. + // The memory card ports are still usable even without a controller + // enabled which is the main reason for using the PSX controller ports. + PSXCONTROLLERPORTS(config, m_controllers, 0); + m_controllers->rxd().set(FUNC(k573mcr_device::write_rxd)); + PSX_CONTROLLER_PORT(config, "port1", psx_controllers, nullptr); + PSX_CONTROLLER_PORT(config, "port2", psx_controllers, nullptr); +} + +const char *k573mcr_device::device_id() +{ + return "KONAMI CO.,LTD.;White I/O;Ver1.0;White I/O PCB"; +} + +uint8_t k573mcr_device::command_format_version() +{ + return 0x11; +} + +uint8_t k573mcr_device::jvs_standard_version() +{ + return 0x20; +} + +uint8_t k573mcr_device::comm_method_version() +{ + return 0x10; +} + +WRITE_LINE_MEMBER(k573mcr_device::write_rxd) +{ + if (m_psx_clock) { + if (m_psx_rx_bit == 0) { + m_psx_rx_data = 0; + } + + m_psx_rx_data |= state << m_psx_rx_bit; + m_psx_rx_bit = (m_psx_rx_bit + 1) % 8; + } +} + +void k573mcr_device::controller_set_port(uint32_t port_no) +{ + m_controllers->write_dtr(!!port_no); + m_controllers->write_dtr(!port_no); +} + +uint8_t k573mcr_device::controller_port_send_byte(uint8_t data) +{ + for (int i = 0; i < 8; i++) { + m_controllers->write_sck(m_psx_clock); + m_psx_clock = !m_psx_clock; + m_controllers->write_txd(BIT(data, i)); + m_controllers->write_sck(m_psx_clock); + m_psx_clock = !m_psx_clock; + } + + return m_psx_rx_data; +} + +bool k573mcr_device::pad_read(uint32_t port_no, uint8_t *output) +{ + controller_set_port(port_no); + controller_port_send_byte(0x01); + uint8_t a = controller_port_send_byte('B'); + uint8_t b = controller_port_send_byte(0); + *output++ = controller_port_send_byte(0); + *output++ = controller_port_send_byte(0); + + return a == 0x41 && b == 0x5a; +} + +bool k573mcr_device::memcard_read(uint32_t port_no, uint16_t block_addr, uint8_t *output) +{ + controller_set_port(port_no); + controller_port_send_byte(0x81); + + if (controller_port_send_byte('R') == 0xff) { // state_command, Request read + return false; + } + + controller_port_send_byte(0); // state_command -> state_cmdack + controller_port_send_byte(0); // state_cmdack -> state_wait + controller_port_send_byte(block_addr >> 8); // state_wait -> state_addr_hi + controller_port_send_byte(block_addr & 0xff); // state_addr_hi -> state_addr_lo + + if (controller_port_send_byte(0) != 0x5c) { // state_addr_lo -> state_read + // If the command wasn't correct then it transitions to state_illegal at this point + return false; + } + + controller_port_send_byte(0); // Skip 0x5d + controller_port_send_byte(0); // Skip addr hi + controller_port_send_byte(0); // Skip addr lo + + for (int i = 0; i < MEMCARD_BLOCK_SIZE; i++) { + auto c = controller_port_send_byte(0); + if (output != nullptr) { + *output++ = c; + } + } + + controller_port_send_byte(0); + + return controller_port_send_byte(0) == 'G'; +} + +bool k573mcr_device::memcard_write(uint32_t port_no, uint16_t block_addr, uint8_t *input) +{ + controller_set_port(port_no); + controller_port_send_byte(0x81); + + if (controller_port_send_byte('W') == 0xff) { // state_command, Request write + return false; + } + + controller_port_send_byte(0); // state_command -> state_cmdack + controller_port_send_byte(0); // state_cmdack -> state_wait + controller_port_send_byte(block_addr >> 8); // state_wait -> state_addr_hi + controller_port_send_byte(block_addr & 0xff); // state_addr_hi -> state_addr_lo + + uint8_t checksum = (block_addr >> 8) ^ (block_addr & 0xff); + for (int i = 0; i < MEMCARD_BLOCK_SIZE; i++) { + controller_port_send_byte(input[i]); // state_read + checksum ^= input[i]; + } + + controller_port_send_byte(checksum); + controller_port_send_byte(0); + controller_port_send_byte(0); + + return controller_port_send_byte(0) == 'G'; +} + +int k573mcr_device::handle_message(const uint8_t *send_buffer, uint32_t send_size, uint8_t *&recv_buffer) +{ + // Notes: + // 80678::E0:01:06:70:02:01:C0:00:3A: <- Command from game (E0:01:...) + // 80681::E0:00:03:01:01:05: <- Response from memory card reader device (E0:00:...) + // + // The returned value of this function should be 0 (invalid parameters), -1 (unknown command), or the number of bytes in the message. + // The number of bytes to return is covered by the xx section: + // 80678::E0:01:yy:xx:xx:xx:xx:xx:3A: + // This should correspond to yy - 1, but you don't actually get access to yy in the message handler so you must calculate it yourself. + // + // recv_buffer will correspond to this part when returning data: + // 80681::E0:00:03:01:rr:05: + // In some special cases there is an empty respond but that is not supported in MAME currently so a single byte is returned instead. + + switch(send_buffer[0]) { + case 0xf0: + // The bootloader for System 573 games checks for the master calendar which initializes the JVS device. + // After the bootloader, the actual game's code tries to initialize the JVS device again and (seemingly) + // expects it to be in a fresh state. Since it was already initialized in the bootloader, it will throw + // the error message "JVS SUBS RESET ERROR". + // There might be something else that happens on real hardware between when it loads the bootloader + // and when it starts the actual game's code that resets the JVS device, but I do not have hands on + // access to test such a thing. + // Reset immediately to hack around that error. + device_reset(); + break; + + case 0x14: + // Function list returns nothing on + // 75502::E0:01:02:14:17: + // 75503::E0:00:04:01:01:00:06: + *recv_buffer++ = 0x01; + *recv_buffer++ = 0x00; + return 1; + + case 0x70: + { + int ram_addr = (send_buffer[2] << 16) | (send_buffer[3] << 8) | send_buffer[4]; + int target_len = send_buffer[5]; + + if (send_buffer[1] == 0) { + // Buffer read + // 39595::E0:01:07:70:00:02:00:00:80:FA: + // 39596::E0:00:83:01:01:4D:43:00:00:00:00:00:00:00:00:00 + // :00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 + // :00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 + // :00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 + // :00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 + // :00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 + // :00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 + // :00:00:00:00:00:00: + *recv_buffer++ = 0x01; + + if (target_len > 0 && ram_addr + target_len < RAM_SIZE) { + memcpy(recv_buffer, &m_ram[ram_addr], target_len); + recv_buffer += target_len; + } + + return 6; + } else if (send_buffer[1] == 1) { + // Buffer write + // 77565::E0:01:87:70:01:02:04:00:80:24:A5:04:70:30:30:7A + // :24:87:C6:85:10:00:64:05:58:02:44:45:CA:8C:FD:A2 + // :00:04:A3:00:04:8C:A6:00:FF:08:8C:00:82:00:20:00 + // :E8:00:F4:00:DD:F8:C0:24:EF:85:FC:21:20:20:04:5B + // :14:40:51:73:77: + // 77583::E0:00:01: + if (target_len > 0) { + memcpy(&m_ram[ram_addr], send_buffer + 6, target_len); + } + + *recv_buffer++ = 0x01; + + return 6 + target_len; + } else if (send_buffer[1] == 2) { + // Set execution address + // 80678::E0:01:06:70:02:01:C0:00:3A: + // 80681::E0:00:03:01:01:05: + *recv_buffer++ = 0x01; + + return 5; + } + + return -1; + } + + case 0x71: + { + // Status request + // 81681::E0:01:02:71:74: + // 81682::E0:00:05:01:01:00:00:07: + *recv_buffer++ = 0x01; + *recv_buffer++ = m_status >> 8; + *recv_buffer++ = m_status & 0xff; + + return 1; + } + + case 0x72: + { + // Security plate + uint8_t cmd = send_buffer[1] & ~1; + int sec_slot = send_buffer[1] & 1; + + if (cmd == 0x00) { + // Packet: e0 01 03 72 00 76 slot 0 (CN63) + // Packet: e0 01 03 72 01 77 slot 1 (CN64) + *recv_buffer++ = 0x01; + + return 2; + } else if (cmd == 0x10) { + // Set password (presumably to unlock/read security dongle) + // Packet: e0 01 0b 72 10 a4 60 f0 5d ea c4 5d ec d6 + *recv_buffer++ = 0x01; + + return 10; + } else if (cmd == 0x20) { + // Get dongle data? (slot 0: GE885-JB, slot 1: ?) + // Packet: e0 01 0a 72 20 00 00 02 00 00 00 08 a7 + // It seems that as long as the checksum matches, the game will accept it as valid + int ram_addr = (send_buffer[4] << 16) | (send_buffer[5] << 8) | send_buffer[6]; + + m_ram[ram_addr] = 'J'; + m_ram[ram_addr + 1] = sec_slot ? 'A' : 'B'; // GF uses GE929-JA, but the games will accept anything + m_ram[ram_addr + 2] = 0x00; + m_ram[ram_addr + 3] = 0x00; + m_ram[ram_addr + 4] = ~(m_ram[ram_addr] + m_ram[ram_addr + 1]); // Checksum byte + + *recv_buffer++ = 0x01; + + return 9; + } else if (cmd == 0x40) { + // Get some kind of registration info from dongle? + // Game code calls it "config register" + // Packet: e0 01 06 72 40 02 00 00 bb + int ram_addr = (send_buffer[2] << 16) | (send_buffer[3] << 8) | send_buffer[4]; + + m_ram[ram_addr] = 0xFF; + m_ram[ram_addr + 1] = 0xFF; + m_ram[ram_addr + 2] = 0xAC; + m_ram[ram_addr + 3] = 0x09; + m_ram[ram_addr + 4] = 0x00; + + *recv_buffer++ = 0x01; + + return 5; + } + + return -1; + } + + case 0x73: + { + // Execute previously set address (0x70 0x02) + // 81674::E0:01:02:73:76: + // 81675::E0:00:03:01:01:05: + *recv_buffer++ = 0x01; + + return 1; + } + + case 0x76: + { + // Memory card + if (send_buffer[1] == 0x74) { + // Read from card + // Packet (port 2): e0 01 0a 76 74 80 00 02 00 00 00 01 78 + int memcard_port = send_buffer[2] >> 7; + int memcard_addr = ((send_buffer[2] << 8) | send_buffer[3]) & 0x7fff; + int ram_addr = (send_buffer[4] << 16) | (send_buffer[5] << 8) | send_buffer[6]; + int block_count = (send_buffer[7] << 8) | send_buffer[8]; + bool is_ejected = BIT(m_meta->read(), memcard_port); // Forcefully ejected using hotkey + + if (!is_ejected && memcard_read(memcard_port, 0, nullptr)) { + // Check if card is inserted + m_status = MEMCARD_AVAILABLE; + } else { + m_status = MEMCARD_UNAVAILABLE; + } + + if (m_status == MEMCARD_AVAILABLE) { + for (int i = 0; i < block_count; i++) { + m_status |= MEMCARD_READING; + + if (memcard_read(memcard_port, memcard_addr + i, &m_ram[ram_addr + (i * MEMCARD_BLOCK_SIZE)])) { + m_status = MEMCARD_AVAILABLE; + } else { + m_status = MEMCARD_ERROR; + break; + } + } + } + + *recv_buffer++ = 0x01; + *recv_buffer++ = 0x01; + + return 9; + } else if (send_buffer[1] == 0x75) { + // Write to card + // Packet: e0 01 0a 76 75 02 00 00 00 3f 00 01 38 + int ram_addr = (send_buffer[2] << 16) | (send_buffer[3] << 8) | send_buffer[4]; + int memcard_port = send_buffer[5] >> 7; + int memcard_addr = ((send_buffer[5] << 8) | send_buffer[6]) & 0x7fff; + int block_count = (send_buffer[7] << 8) | send_buffer[8]; + bool is_ejected = BIT(m_meta->read(), memcard_port); // Forcefully ejected using hotkey + + if (!is_ejected && memcard_read(memcard_port, 0, nullptr)) { + // Check if card is inserted + m_status = MEMCARD_AVAILABLE; + } else { + m_status = MEMCARD_UNAVAILABLE; + } + + if (m_status == MEMCARD_AVAILABLE && m_is_memcard_initialized) { + for (int i = 0; i < block_count; i++) { + m_status |= MEMCARD_WRITING; + + if (memcard_write(memcard_port, memcard_addr + i, &m_ram[ram_addr + (i * MEMCARD_BLOCK_SIZE)])) { + m_status = MEMCARD_AVAILABLE; + } else { + m_status = MEMCARD_ERROR; + break; + } + } + } + + *recv_buffer++ = 0x01; + *recv_buffer++ = 0x01; + + // The game will write a block of 0xffs to block 3f immediately after inserting a memory card for the first time. + // I believe it's some kind of initialization, and based on the game's code, the 0xff buffer is prepared when the firmware is being sent. + // Based on someone else's research with real hardware, the 0xff block doesn't actually get written to the memory card. + m_is_memcard_initialized = true; + + return 9; + } + + return -1; + } + + case 0x77: + { + // Controller ports + // Packet: e0 01 02 77 7a + // + // Only used by Guitar Freaks. + // This was introduced in Guitar Freaks 2nd Mix Link Kit 2, which allowed players to bring their own PS1 guitar controllers to the arcade + // and plug it into the machine to use as a controller instead of the normal machine's guitar controllers. + + *recv_buffer++ = 0x01; + pad_read(0, recv_buffer); + pad_read(1, recv_buffer + 2); + + recv_buffer += 4; + + return 1; + } + } + + // Command not recognized, pass it off to the base message handler + return jvs_device::handle_message(send_buffer, send_size, recv_buffer); } ROM_START( k573mcr ) @@ -31,4 +492,16 @@ const tiny_rom_entry *k573mcr_device::device_rom_region() const return ROM_NAME( k573mcr ); } + +INPUT_PORTS_START( k573mcr_meta_controls ) + PORT_START("META") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1) PORT_TOGGLE PORT_NAME("Insert/Eject Memory Card 1") + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_SERVICE2) PORT_TOGGLE PORT_NAME("Insert/Eject Memory Card 2") +INPUT_PORTS_END + +ioport_constructor k573mcr_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(k573mcr_meta_controls); +} + DEFINE_DEVICE_TYPE(KONAMI_573_MEMORY_CARD_READER, k573mcr_device, "k573mcr", "Konami 573 Memory Card Reader") diff --git a/src/mame/machine/k573mcr.h b/src/mame/machine/k573mcr.h index 29e44ca098e..2dab03d13ea 100644 --- a/src/mame/machine/k573mcr.h +++ b/src/mame/machine/k573mcr.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:smf +// copyright-holders:smf, windyfairy /* * Konami 573 Memory Card Reader * @@ -9,19 +9,73 @@ #pragma once +#include "bus/psx/ctlrport.h" +#include "machine/jvsdev.h" +#include "machine/timer.h" - -DECLARE_DEVICE_TYPE(KONAMI_573_MEMORY_CARD_READER, k573mcr_device) - -class k573mcr_device : public device_t +class k573mcr_device : public jvs_device { public: + template + k573mcr_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&jvs_host_tag) + : k573mcr_device(mconfig, tag, owner, clock) + { + host.set_tag(std::forward(jvs_host_tag)); + } + k573mcr_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + virtual ioport_constructor device_input_ports() const override; + + DECLARE_WRITE_LINE_MEMBER(write_rxd); + protected: + template void set_port_tags() { } + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; virtual const tiny_rom_entry *device_rom_region() const override; + + // JVS device overrides + virtual const char *device_id() override; + virtual uint8_t command_format_version() override; + virtual uint8_t jvs_standard_version() override; + virtual uint8_t comm_method_version() override; + virtual int handle_message(const uint8_t *send_buffer, uint32_t send_size, uint8_t *&recv_buffer) override; + +private: + enum { + MEMCARD_UNINITIALIZED = 0x0000, // Default value, also used after writing? + MEMCARD_ERROR = 0x0002, + MEMCARD_UNAVAILABLE = 0x0008, // Card is not inserted + MEMCARD_READING = 0x0200, // Read request is executing + MEMCARD_WRITING = 0x0400, // Write request is executing + MEMCARD_AVAILABLE = 0x8000 // Can be combined with MEMCARD_READING and MEMCARD_WRITING for busy state + }; + + enum { + RAM_SIZE = 0x400000, + MEMCARD_BLOCK_SIZE = 128 + }; + + void controller_set_port(uint32_t port_no); + uint8_t controller_port_send_byte(uint8_t data); + bool pad_read(uint32_t port_no, uint8_t *output); + bool memcard_read(uint32_t port_no, uint16_t block_addr, uint8_t *output); + bool memcard_write(uint32_t port_no, uint16_t block_addr, uint8_t *input); + + std::unique_ptr m_ram; + uint16_t m_status; + bool m_is_memcard_initialized; + uint8_t m_psx_rx_data, m_psx_rx_bit; + bool m_psx_clock; + + required_device m_controllers; + required_ioport m_meta; }; +DECLARE_DEVICE_TYPE(KONAMI_573_MEMORY_CARD_READER, k573mcr_device) + #endif // MAME_MACHINE_K573_MCR_H -- cgit v1.2.3