2021-11-24 21:59:48 +01:00
// v406 USIO emulator
// Responses may be specific to Taiko no Tatsujin
# include "stdafx.h"
# include "usio.h"
# include "Input/pad_thread.h"
2023-05-21 13:26:32 +02:00
# include "Emu/Io/usio_config.h"
2022-09-16 18:35:24 +02:00
# include "Emu/IdManager.h"
2021-11-24 21:59:48 +01:00
2023-05-20 14:34:35 +02:00
LOG_CHANNEL ( usio_log , " USIO " ) ;
2021-11-24 21:59:48 +01:00
2023-05-21 15:57:57 +02:00
template < >
void fmt_class_string < usio_btn > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( usio_btn value )
{
switch ( value )
{
case usio_btn : : test : return " Test " ;
case usio_btn : : coin : return " Coin " ;
case usio_btn : : enter : return " Enter " ;
case usio_btn : : up : return " Up " ;
case usio_btn : : down : return " Down " ;
case usio_btn : : service : return " Service " ;
case usio_btn : : strong_hit_side_left : return " Strong Hit Side Left " ;
case usio_btn : : strong_hit_side_right : return " Strong Hit Side Right " ;
case usio_btn : : strong_hit_center_left : return " Strong Hit Center Left " ;
case usio_btn : : strong_hit_center_right : return " Strong Hit Center Right " ;
case usio_btn : : small_hit_side_left : return " Small Hit Side Left " ;
case usio_btn : : small_hit_side_right : return " Small Hit Side Right " ;
case usio_btn : : small_hit_center_left : return " Small Hit Center Left " ;
case usio_btn : : small_hit_center_right : return " Small Hit Center Right " ;
case usio_btn : : count : return " Count " ;
}
return unknown ;
} ) ;
}
2022-09-21 13:06:16 +02:00
struct usio_memory
2022-09-16 18:35:24 +02:00
{
2022-09-21 13:06:16 +02:00
std : : vector < u8 > backup_memory ;
2022-09-28 02:37:37 +02:00
std : : vector < u8 > last_game_status ;
2022-09-16 18:35:24 +02:00
2022-09-21 13:06:16 +02:00
usio_memory ( const usio_memory & ) = delete ;
usio_memory & operator = ( const usio_memory & ) = delete ;
2022-09-16 18:35:24 +02:00
} ;
2021-11-23 15:52:41 +01:00
usb_device_usio : : usb_device_usio ( const std : : array < u8 , 7 > & location )
: usb_device_emulated ( location )
2021-11-24 21:59:48 +01:00
{
device = UsbDescriptorNode ( USB_DESCRIPTOR_DEVICE ,
UsbDeviceDescriptor {
. bcdUSB = 0x0110 ,
. bDeviceClass = 0xff ,
. bDeviceSubClass = 0x00 ,
. bDeviceProtocol = 0xff ,
. bMaxPacketSize0 = 0x8 ,
. idVendor = 0x0b9a ,
. idProduct = 0x0910 ,
. bcdDevice = 0x0910 ,
. iManufacturer = 0x01 ,
. iProduct = 0x02 ,
. iSerialNumber = 0x00 ,
. bNumConfigurations = 0x01 } ) ;
auto & config0 = device . add_node ( UsbDescriptorNode ( USB_DESCRIPTOR_CONFIG ,
UsbDeviceConfiguration {
. wTotalLength = 39 ,
. bNumInterfaces = 0x01 ,
. bConfigurationValue = 0x01 ,
. iConfiguration = 0x00 ,
. bmAttributes = 0xc0 ,
. bMaxPower = 0x32 // ??? 100ma
} ) ) ;
config0 . add_node ( UsbDescriptorNode ( USB_DESCRIPTOR_INTERFACE ,
UsbDeviceInterface {
. bInterfaceNumber = 0x00 ,
. bAlternateSetting = 0x00 ,
. bNumEndpoints = 0x03 ,
. bInterfaceClass = 0x00 ,
. bInterfaceSubClass = 0x00 ,
. bInterfaceProtocol = 0x00 ,
. iInterface = 0x00 } ) ) ;
config0 . add_node ( UsbDescriptorNode ( USB_DESCRIPTOR_ENDPOINT ,
UsbDeviceEndpoint {
. bEndpointAddress = 0x01 ,
. bmAttributes = 0x02 ,
. wMaxPacketSize = 0x0040 ,
. bInterval = 0x00 } ) ) ;
config0 . add_node ( UsbDescriptorNode ( USB_DESCRIPTOR_ENDPOINT ,
UsbDeviceEndpoint {
. bEndpointAddress = 0x82 ,
. bmAttributes = 0x02 ,
. wMaxPacketSize = 0x0040 ,
. bInterval = 0x00 } ) ) ;
config0 . add_node ( UsbDescriptorNode ( USB_DESCRIPTOR_ENDPOINT ,
UsbDeviceEndpoint {
. bEndpointAddress = 0x83 ,
. bmAttributes = 0x03 ,
. wMaxPacketSize = 0x0008 ,
. bInterval = 16 } ) ) ;
2022-09-16 18:35:24 +02:00
2023-01-18 03:23:06 +01:00
g_fxo - > get < usio_memory > ( ) . backup_memory . clear ( ) ;
2022-09-21 13:06:16 +02:00
g_fxo - > get < usio_memory > ( ) . backup_memory . resize ( 0xB8 ) ;
2022-10-15 13:32:51 +02:00
g_fxo - > get < usio_memory > ( ) . last_game_status . clear ( ) ;
2022-09-21 13:06:16 +02:00
g_fxo - > get < usio_memory > ( ) . last_game_status . resize ( 0x28 ) ;
2022-09-29 18:38:16 +02:00
load_backup ( ) ;
2021-11-24 21:59:48 +01:00
}
usb_device_usio : : ~ usb_device_usio ( )
{
2022-09-28 02:37:37 +02:00
save_backup ( ) ;
2021-11-24 21:59:48 +01:00
}
void usb_device_usio : : control_transfer ( u8 bmRequestType , u8 bRequest , u16 wValue , u16 wIndex , u16 wLength , u32 buf_size , u8 * buf , UsbTransfer * transfer )
{
transfer - > fake = true ;
// Control transfers are nearly instant
switch ( bmRequestType )
{
default :
// Follow to default emulated handler
usb_device_emulated : : control_transfer ( bmRequestType , bRequest , wValue , wIndex , wLength , buf_size , buf , transfer ) ;
break ;
}
}
2022-07-05 21:47:05 +02:00
extern bool is_input_allowed ( ) ;
2022-09-29 18:38:16 +02:00
void usb_device_usio : : load_backup ( )
2022-09-28 02:37:37 +02:00
{
2023-01-11 07:19:15 +01:00
fs : : file usio_backup_file ;
2022-09-28 02:37:37 +02:00
2023-01-11 07:19:15 +01:00
if ( ! usio_backup_file . open ( usio_backup_path , fs : : read ) )
2022-09-28 02:37:37 +02:00
{
2022-09-29 18:38:16 +02:00
usio_log . trace ( " Failed to load the USIO Backup file: %s " , usio_backup_path ) ;
2022-09-28 02:37:37 +02:00
return ;
}
2022-10-15 13:32:51 +02:00
const u64 file_size = g_fxo - > get < usio_memory > ( ) . backup_memory . size ( ) ;
2022-09-28 02:37:37 +02:00
if ( usio_backup_file . size ( ) ! = file_size )
{
2023-01-10 17:04:42 +01:00
usio_log . trace ( " Invalid USIO Backup file detected: %s " , usio_backup_path ) ;
2022-09-28 02:37:37 +02:00
return ;
}
2023-01-11 07:19:15 +01:00
usio_backup_file . read ( g_fxo - > get < usio_memory > ( ) . backup_memory . data ( ) , file_size ) ;
2022-09-28 02:37:37 +02:00
}
void usb_device_usio : : save_backup ( )
{
2023-01-11 07:19:15 +01:00
if ( ! is_used )
return ;
fs : : file usio_backup_file ;
if ( ! usio_backup_file . open ( usio_backup_path , fs : : create + fs : : write + fs : : lock ) )
2022-09-28 02:37:37 +02:00
{
2023-01-11 07:19:15 +01:00
usio_log . error ( " Failed to save the USIO Backup file: %s " , usio_backup_path ) ;
2022-09-28 02:37:37 +02:00
return ;
}
2023-01-11 07:19:15 +01:00
const u64 file_size = g_fxo - > get < usio_memory > ( ) . backup_memory . size ( ) ;
usio_backup_file . write ( g_fxo - > get < usio_memory > ( ) . backup_memory . data ( ) , file_size ) ;
usio_backup_file . trunc ( file_size ) ;
2022-09-28 02:37:37 +02:00
}
2021-11-24 21:59:48 +01:00
void usb_device_usio : : translate_input ( )
{
std : : lock_guard lock ( pad : : g_pad_mutex ) ;
const auto handler = pad : : get_current_handler ( ) ;
2023-01-10 17:04:42 +01:00
std : : vector < u8 > input_buf = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x07 , 0x00 , 0x00 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x60 , 0x00 , 0x60 , 0x00 , 0x60 , 0x00 , 0x60 , 0x00 , 0x60 , 0x00 , 0x60 , 0x00 , 0x60 , 0x00 , 0x60 , 0x00 , 0x60 , 0x00 , 0xFF , 0xFF , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2022-10-20 19:50:14 +02:00
constexpr le_t < u16 > c_small_hit = 0x4D0 ;
constexpr le_t < u16 > c_big_hit = 0x1800 ;
le_t < u16 > test_keys = 0x0000 ;
2021-11-24 21:59:48 +01:00
2023-05-23 01:15:13 +02:00
auto translate_from_pad = [ & ] ( usz pad_number , usz player )
2021-11-24 21:59:48 +01:00
{
2022-07-05 21:47:05 +02:00
if ( ! is_input_allowed ( ) )
{
return ;
}
2023-05-20 14:34:35 +02:00
const auto & pad = : : at32 ( handler - > GetPads ( ) , pad_number ) ;
2021-11-24 21:59:48 +01:00
if ( ! ( pad - > m_port_status & CELL_PAD_STATUS_CONNECTED ) )
{
return ;
}
2023-05-20 14:34:35 +02:00
const std : : size_t offset = ( player * 8ULL ) ;
2021-11-24 21:59:48 +01:00
2023-05-22 23:10:13 +02:00
const auto & cfg = : : at32 ( g_cfg_usio . players , pad_number ) ;
2023-05-30 19:47:35 +02:00
cfg - > handle_input ( pad , false , [ & ] ( usio_btn btn , u16 /*value*/ , bool pressed )
2021-11-24 21:59:48 +01:00
{
2023-05-22 23:10:13 +02:00
switch ( btn )
2021-11-24 21:59:48 +01:00
{
2023-05-20 14:34:35 +02:00
case usio_btn : : test :
if ( player ! = 0 ) break ;
2023-05-22 23:10:13 +02:00
if ( pressed & & ! test_key_pressed ) // Solve the need to hold the Test key
2023-05-20 14:34:35 +02:00
test_on = ! test_on ;
2023-05-22 23:10:13 +02:00
test_key_pressed = pressed ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : coin :
if ( player ! = 0 ) break ;
2023-05-22 23:10:13 +02:00
if ( pressed & & ! coin_key_pressed ) // Ensure only one coin is inserted each time the Coin key is pressed
2023-05-20 14:34:35 +02:00
coin_counter + + ;
2023-05-22 23:10:13 +02:00
coin_key_pressed = pressed ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : enter :
2023-05-22 23:10:13 +02:00
if ( player = = 0 & & pressed )
2023-05-20 14:34:35 +02:00
test_keys | = 0x200 ; // Enter
break ;
case usio_btn : : up :
2023-05-22 23:10:13 +02:00
if ( player = = 0 & & pressed )
2023-05-20 14:34:35 +02:00
test_keys | = 0x2000 ; // Up
break ;
case usio_btn : : down :
2023-05-22 23:10:13 +02:00
if ( player = = 0 & & pressed )
2023-05-20 14:34:35 +02:00
test_keys | = 0x1000 ; // Down
break ;
case usio_btn : : service :
2023-05-22 23:10:13 +02:00
if ( player = = 0 & & pressed )
2023-05-20 14:34:35 +02:00
test_keys | = 0x4000 ; // Service
break ;
case usio_btn : : strong_hit_side_left :
// Strong hit side left
2023-05-22 23:10:13 +02:00
if ( pressed )
2022-09-13 15:08:55 +02:00
std : : memcpy ( input_buf . data ( ) + 32 + offset , & c_big_hit , sizeof ( u16 ) ) ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : strong_hit_center_right :
// Strong hit center right
2023-05-22 23:10:13 +02:00
if ( pressed )
2022-09-13 15:08:55 +02:00
std : : memcpy ( input_buf . data ( ) + 36 + offset , & c_big_hit , sizeof ( u16 ) ) ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : strong_hit_side_right :
// Strong hit side right
2023-05-22 23:10:13 +02:00
if ( pressed )
2022-09-13 15:08:55 +02:00
std : : memcpy ( input_buf . data ( ) + 38 + offset , & c_big_hit , sizeof ( u16 ) ) ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : strong_hit_center_left :
// Strong hit center left
2023-05-22 23:10:13 +02:00
if ( pressed )
2022-09-13 15:08:55 +02:00
std : : memcpy ( input_buf . data ( ) + 34 + offset , & c_big_hit , sizeof ( u16 ) ) ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : small_hit_center_left :
// Small hit center left
2023-05-22 23:10:13 +02:00
if ( pressed )
2022-09-13 15:08:55 +02:00
std : : memcpy ( input_buf . data ( ) + 34 + offset , & c_small_hit , sizeof ( u16 ) ) ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : small_hit_center_right :
// Small hit center right
2023-05-22 23:10:13 +02:00
if ( pressed )
2022-09-13 15:08:55 +02:00
std : : memcpy ( input_buf . data ( ) + 36 + offset , & c_small_hit , sizeof ( u16 ) ) ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : small_hit_side_left :
// Small hit side left
2023-05-22 23:10:13 +02:00
if ( pressed )
2022-09-13 15:08:55 +02:00
std : : memcpy ( input_buf . data ( ) + 32 + offset , & c_small_hit , sizeof ( u16 ) ) ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : small_hit_side_right :
// Small hit side right
2023-05-22 23:10:13 +02:00
if ( pressed )
2022-09-13 15:08:55 +02:00
std : : memcpy ( input_buf . data ( ) + 38 + offset , & c_small_hit , sizeof ( u16 ) ) ;
2023-05-20 14:34:35 +02:00
break ;
2023-05-21 13:43:27 +02:00
case usio_btn : : count :
break ;
2021-11-24 21:59:48 +01:00
}
2023-05-22 23:10:13 +02:00
} ) ;
2021-11-24 21:59:48 +01:00
} ;
2023-05-23 01:15:13 +02:00
for ( usz i = 0 ; i < g_cfg_usio . players . size ( ) ; i + + )
{
translate_from_pad ( i , i ) ;
}
2021-11-24 21:59:48 +01:00
2022-10-20 19:50:14 +02:00
test_keys | = test_on ? 0x80 : 0x00 ;
std : : memcpy ( input_buf . data ( ) , & test_keys , sizeof ( u16 ) ) ;
std : : memcpy ( input_buf . data ( ) + 16 , & coin_counter , sizeof ( u16 ) ) ;
2023-01-10 17:04:42 +01:00
response = std : : move ( input_buf ) ;
2021-11-24 21:59:48 +01:00
}
2023-01-10 17:04:42 +01:00
void usb_device_usio : : usio_write ( u8 channel , u16 reg , std : : vector < u8 > & data )
2021-11-24 21:59:48 +01:00
{
2023-01-10 17:04:42 +01:00
auto write_memory = [ & ] ( std : : vector < u8 > & memory )
2022-09-21 13:06:16 +02:00
{
2023-01-10 17:04:42 +01:00
auto size = memory . size ( ) ;
memory = std : : move ( data ) ;
memory . resize ( size ) ;
2022-09-21 13:06:16 +02:00
} ;
2021-11-24 21:59:48 +01:00
const auto get_u16 = [ & ] ( std : : string_view usio_func ) - > u16
{
if ( data . size ( ) ! = 2 )
{
2023-01-10 17:04:42 +01:00
usio_log . error ( " data.size() is %d, expected 2 for get_u16 in %s " , data . size ( ) , usio_func ) ;
2021-11-24 21:59:48 +01:00
}
return * reinterpret_cast < const le_t < u16 > * > ( data . data ( ) ) ;
} ;
if ( channel = = 0 )
{
switch ( reg )
{
case 0x0002 :
{
2023-01-10 17:04:42 +01:00
usio_log . trace ( " SetSystemError: 0x%04X " , get_u16 ( " SetSystemError " ) ) ;
2021-11-24 21:59:48 +01:00
break ;
}
case 0x000A :
{
2023-01-10 17:04:42 +01:00
if ( get_u16 ( " ClearSram " ) = = 0x6666 )
usio_log . trace ( " ClearSram " ) ;
2021-11-24 21:59:48 +01:00
break ;
}
case 0x0028 :
{
2023-01-10 17:04:42 +01:00
usio_log . trace ( " SetExpansionMode: 0x%04X " , get_u16 ( " SetExpansionMode " ) ) ;
2021-11-24 21:59:48 +01:00
break ;
}
case 0x0048 :
case 0x0058 :
case 0x0068 :
case 0x0078 :
{
2023-01-10 17:04:42 +01:00
usio_log . trace ( " SetHopperRequest(Hopper: %d, Request: 0x%04X) " , ( reg - 0x48 ) / 0x10 , get_u16 ( " SetHopperRequest " ) ) ;
2021-11-24 21:59:48 +01:00
break ;
}
case 0x004A :
case 0x005A :
case 0x006A :
case 0x007A :
{
2023-01-10 17:04:42 +01:00
usio_log . trace ( " SetHopperRequest(Hopper: %d, Limit: 0x%04X) " , ( reg - 0x4A ) / 0x10 , get_u16 ( " SetHopperLimit " ) ) ;
2021-11-24 21:59:48 +01:00
break ;
}
default :
{
//usio_log.error("Unhandled channel 0 register write: 0x%04X", reg);
break ;
}
}
}
else if ( channel > = 2 )
{
usio_log . trace ( " Usio write of sram(chip: %d, addr: 0x%04X) " , channel - 2 , reg ) ;
2022-09-16 18:35:24 +02:00
if ( channel = = 2 )
{
switch ( reg )
{
2022-09-21 13:06:16 +02:00
case 0x0000 :
{
2023-01-10 17:04:42 +01:00
write_memory ( g_fxo - > get < usio_memory > ( ) . backup_memory ) ;
2022-09-21 13:06:16 +02:00
break ;
}
2022-09-16 18:35:24 +02:00
case 0x0180 :
{
2022-09-21 13:06:16 +02:00
write_memory ( g_fxo - > get < usio_memory > ( ) . last_game_status ) ;
2022-09-16 18:35:24 +02:00
break ;
}
}
}
2021-11-24 21:59:48 +01:00
}
else
{
2022-09-05 04:55:19 +02:00
// Channel 1 is the endpoint for firmware update.
// We are not using any firmware since this is emulation.
usio_log . warning ( " Unsupported write operation(channel: 0x%02X, addr: 0x%04X) " , channel , reg ) ;
2021-11-24 21:59:48 +01:00
}
}
void usb_device_usio : : usio_read ( u8 channel , u16 reg , u16 size )
{
if ( channel = = 0 )
{
switch ( reg )
{
case 0x0000 :
{
// Get Buffer, rarely gives a reply on real HW
// First U16 seems to be a timestamp of sort
2022-12-05 08:24:53 +01:00
// Purpose seems related to connectivity check
2023-01-10 17:04:42 +01:00
response = { 0x7E , 0xE4 , 0x00 , 0x00 , 0x74 , 0x01 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x7E , 0x00 , 0x7E , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x80 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2021-11-24 21:59:48 +01:00
break ;
}
case 0x0080 :
{
2022-12-05 08:24:53 +01:00
// Card reader check - 1
2023-01-10 17:04:42 +01:00
response = { 0x02 , 0x03 , 0x06 , 0x00 , 0xFF , 0x0F , 0x00 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x05 , 0x10 , 0x00 } ;
2022-12-05 08:24:53 +01:00
break ;
}
case 0x7000 :
{
// Card reader check - 2
2023-01-26 14:42:21 +01:00
// No data returned
2021-11-24 21:59:48 +01:00
break ;
}
case 0x1080 :
{
// Often called, gets input from usio
translate_input ( ) ;
break ;
}
case 0x1800 :
{
// Firmware
// "NBGI.;USIO01;Ver1.00;JPN,Multipurpose with PPG."
2023-01-10 17:04:42 +01:00
response = { 0x4E , 0x42 , 0x47 , 0x49 , 0x2E , 0x3B , 0x55 , 0x53 , 0x49 , 0x4F , 0x30 , 0x31 , 0x3B , 0x56 , 0x65 , 0x72 , 0x31 , 0x2E , 0x30 , 0x30 , 0x3B , 0x4A , 0x50 , 0x4E , 0x2C , 0x4D , 0x75 , 0x6C , 0x74 , 0x69 , 0x70 , 0x75 , 0x72 , 0x70 , 0x6F , 0x73 , 0x65 , 0x20 , 0x77 , 0x69 , 0x74 , 0x68 , 0x20 , 0x50 , 0x50 , 0x47 , 0x2E , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2021-11-24 21:59:48 +01:00
break ;
}
case 0x1880 :
{
// Seems to contain a few extra bytes of info in addition to the firmware string
// Firmware
// "NBGI2;USIO01;Ver1.00;JPN,Multipurpose with PPG."
2023-01-10 17:04:42 +01:00
response = { 0x4E , 0x42 , 0x47 , 0x49 , 0x32 , 0x3B , 0x55 , 0x53 , 0x49 , 0x4F , 0x30 , 0x31 , 0x3B , 0x56 , 0x65 , 0x72 , 0x31 , 0x2E , 0x30 , 0x30 , 0x3B , 0x4A , 0x50 , 0x4E , 0x2C , 0x4D , 0x75 , 0x6C , 0x74 , 0x69 , 0x70 , 0x75 , 0x72 , 0x70 , 0x6F , 0x73 , 0x65 , 0x20 , 0x77 , 0x69 , 0x74 , 0x68 , 0x20 , 0x50 , 0x50 , 0x47 , 0x2E , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x00 , 0x13 , 0x00 , 0x30 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x03 , 0x02 , 0x00 , 0x08 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x03 , 0x00 , 0x03 , 0x00 , 0x08 , 0xE2 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2021-11-24 21:59:48 +01:00
break ;
}
default :
{
2023-01-10 17:04:42 +01:00
usio_log . error ( " Unhandled channel 0 register read: 0x%04X " , reg ) ;
2021-11-24 21:59:48 +01:00
break ;
}
}
}
else if ( channel > = 2 )
{
u8 chip = channel - 2 ;
usio_log . trace ( " Usio read of sram(chip: %d, addr: 0x%04X) " , chip , reg ) ;
switch ( chip )
{
case 0 :
{
switch ( reg )
{
case 0x0000 :
{
2023-01-10 17:04:42 +01:00
response = g_fxo - > get < usio_memory > ( ) . backup_memory ;
2021-11-24 21:59:48 +01:00
break ;
}
case 0x0180 :
{
2023-01-10 17:04:42 +01:00
response = g_fxo - > get < usio_memory > ( ) . last_game_status ;
2021-11-24 21:59:48 +01:00
break ;
}
case 0x0200 :
{
2023-01-10 17:04:42 +01:00
//ensure(size == 0x100);
2021-11-24 21:59:48 +01:00
// No data returned
break ;
}
case 0x1000 :
{
2023-01-10 17:04:42 +01:00
//ensure(size == 0x1000);
2023-01-26 14:42:21 +01:00
// No data returned
2021-11-24 21:59:48 +01:00
break ;
}
default :
{
2023-01-10 17:04:42 +01:00
usio_log . error ( " Unhandled read of sram(chip: %d, addr: 0x%04X) " , channel - 2 , reg ) ;
2021-11-24 21:59:48 +01:00
break ;
}
}
break ;
}
default :
{
2023-01-10 17:04:42 +01:00
usio_log . error ( " Unhandled read of sram(chip: %d, addr: 0x%04X) " , channel - 2 , reg ) ;
2021-11-24 21:59:48 +01:00
break ;
}
}
}
else
{
2022-09-05 04:55:19 +02:00
// Channel 1 is the endpoint for firmware update.
// We are not using any firmware since this is emulation.
usio_log . warning ( " Unsupported read operation(channel: 0x%02X, addr: 0x%04X) " , channel , reg ) ;
2021-11-24 21:59:48 +01:00
}
2023-01-26 14:42:21 +01:00
response . resize ( size ) ; // Always resize the response vector to the given size
2021-11-24 21:59:48 +01:00
}
void usb_device_usio : : interrupt_transfer ( u32 buf_size , u8 * buf , u32 endpoint , UsbTransfer * transfer )
{
constexpr u8 USIO_COMMAND_WRITE = 0x90 ;
constexpr u8 USIO_COMMAND_READ = 0x10 ;
2023-01-10 17:04:42 +01:00
constexpr u8 USIO_COMMAND_INIT = 0xA0 ;
2021-11-24 21:59:48 +01:00
static bool expecting_data = false ;
static std : : vector < u8 > usio_data ;
2023-01-10 17:04:42 +01:00
static u32 response_seek = 0 ;
2021-11-24 21:59:48 +01:00
static u8 usio_channel = 0 ;
static u16 usio_register = 0 ;
static u16 usio_length = 0 ;
transfer - > fake = true ;
transfer - > expected_result = HC_CC_NOERR ;
// The latency varies per operation but it doesn't seem to matter for this device so let's go fast!
2023-04-01 22:51:00 +02:00
transfer - > expected_time = get_timestamp ( ) + 1'000 ;
2021-11-24 21:59:48 +01:00
2023-01-11 07:19:15 +01:00
is_used = true ;
2021-11-24 21:59:48 +01:00
switch ( endpoint )
{
case 0x01 :
{
// Write endpoint
transfer - > expected_count = buf_size ;
if ( expecting_data )
{
usio_data . insert ( usio_data . end ( ) , buf , buf + buf_size ) ;
usio_length - = buf_size ;
if ( usio_length = = 0 )
{
expecting_data = false ;
usio_write ( usio_channel , usio_register , usio_data ) ;
}
return ;
}
// Commands
2023-01-18 03:23:06 +01:00
if ( buf_size ! = 6 )
{
usio_log . error ( " Expected a command but buf_size != 6 " ) ;
return ;
}
2021-11-24 21:59:48 +01:00
usio_channel = buf [ 0 ] & 0xF ;
usio_register = * reinterpret_cast < le_t < u16 > * > ( & buf [ 2 ] ) ;
usio_length = * reinterpret_cast < le_t < u16 > * > ( & buf [ 4 ] ) ;
if ( ( buf [ 0 ] & USIO_COMMAND_WRITE ) = = USIO_COMMAND_WRITE )
{
usio_log . trace ( " UsioWrite(Channel: 0x%02X, Register: 0x%04X, Length: 0x%04X) " , usio_channel , usio_register , usio_length ) ;
2023-01-18 03:23:06 +01:00
if ( ( ( ~ ( usio_register > > 8 ) ) & 0xF0 ) ! = buf [ 1 ] )
{
usio_log . error ( " Invalid UsioWrite command " ) ;
return ;
}
2021-11-24 21:59:48 +01:00
expecting_data = true ;
usio_data . clear ( ) ;
}
2023-01-10 17:04:42 +01:00
else if ( ( buf [ 0 ] & USIO_COMMAND_READ ) = = USIO_COMMAND_READ )
2021-11-24 21:59:48 +01:00
{
usio_log . trace ( " UsioRead(Channel: 0x%02X, Register: 0x%04X, Length: 0x%04X) " , usio_channel , usio_register , usio_length ) ;
2023-01-10 17:04:42 +01:00
response_seek = 0 ;
response . clear ( ) ;
2021-11-24 21:59:48 +01:00
usio_read ( usio_channel , usio_register , usio_length ) ;
}
2023-01-10 17:04:42 +01:00
else if ( ( buf [ 0 ] & USIO_COMMAND_INIT ) = = USIO_COMMAND_INIT ) // Init and reset commands
2021-11-24 21:59:48 +01:00
{
2023-01-10 17:04:42 +01:00
//const std::array<u8, 2> init_command = {0xA0, 0xF0}; // This kind of command starts with 0xA0, 0xF0 commonly. For example, {0xA0, 0xF0, 0x28, 0x00, 0x00, 0x80}
//ensure(memcmp(buf, init_command.data(), 2) == 0);
2021-11-24 21:59:48 +01:00
}
2023-01-10 17:04:42 +01:00
else
{
usio_log . error ( " Received an unexpected command: 0x%02X " , buf [ 0 ] ) ;
}
break ;
2021-11-24 21:59:48 +01:00
}
case 0x82 :
{
// Read endpoint
2023-01-10 17:04:42 +01:00
const u32 size = std : : min ( buf_size , static_cast < u32 > ( response . size ( ) - response_seek ) ) ;
memcpy ( buf , response . data ( ) + response_seek , size ) ;
response_seek + = size ;
transfer - > expected_count = size ;
2021-11-24 21:59:48 +01:00
break ;
}
default :
2023-01-10 17:04:42 +01:00
usio_log . error ( " Unhandled endpoint: 0x%x " , endpoint ) ;
2021-11-24 21:59:48 +01:00
break ;
}
}