2021-11-24 21:59:48 +01:00
// v406 USIO emulator
# 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 " ;
2023-07-23 19:49:00 +02:00
case usio_btn : : service : return " Service " ;
case usio_btn : : enter : return " Enter/Start " ;
2023-05-21 15:57:57 +02:00
case usio_btn : : up : return " Up " ;
case usio_btn : : down : return " Down " ;
2023-07-23 19:49:00 +02:00
case usio_btn : : left : return " Left " ;
case usio_btn : : right : return " Right " ;
case usio_btn : : taiko_hit_side_left : return " Taiko Hit Side Left " ;
case usio_btn : : taiko_hit_side_right : return " Taiko Hit Side Right " ;
case usio_btn : : taiko_hit_center_left : return " Taiko Hit Center Left " ;
case usio_btn : : taiko_hit_center_right : return " Taiko Hit Center Right " ;
case usio_btn : : tekken_button1 : return " Tekken Button 1 " ;
case usio_btn : : tekken_button2 : return " Tekken Button 2 " ;
case usio_btn : : tekken_button3 : return " Tekken Button 3 " ;
case usio_btn : : tekken_button4 : return " Tekken Button 4 " ;
case usio_btn : : tekken_button5 : return " Tekken Button 5 " ;
2023-05-21 15:57:57 +02:00
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-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 ;
2023-07-23 11:17:49 +02:00
void init ( )
{
backup_memory . clear ( ) ;
2023-07-23 19:49:00 +02:00
backup_memory . resize ( page_size * page_count ) ;
2023-07-23 11:17:49 +02:00
}
2023-07-23 19:49:00 +02:00
static constexpr usz page_size = 0x10000 ;
static constexpr usz page_count = 0x10 ;
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
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
2023-07-19 11:01:44 +02:00
//switch (bmRequestType)
2021-11-24 21:59:48 +01:00
{
2023-07-19 11:01:44 +02:00
//default:
2021-11-24 21:59:48 +01:00
// Follow to default emulated handler
usb_device_emulated : : control_transfer ( bmRequestType , bRequest , wValue , wIndex , wLength , buf_size , buf , transfer ) ;
2023-07-19 11:01:44 +02:00
//break;
2021-11-24 21:59:48 +01:00
}
}
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-07-23 11:17:49 +02:00
g_fxo - > get < usio_memory > ( ) . init ( ) ;
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
}
2023-07-23 19:49:00 +02:00
void usb_device_usio : : translate_input_taiko ( )
2021-11-24 21:59:48 +01:00
{
std : : lock_guard lock ( pad : : g_pad_mutex ) ;
const auto handler = pad : : get_current_handler ( ) ;
2023-09-06 17:10:30 +02:00
std : : vector < u8 > input_buf ( 0x60 ) ;
2023-07-23 19:49:00 +02:00
constexpr le_t < u16 > c_hit = 0x1800 ;
le_t < u16 > digital_input = 0 ;
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-07-23 19:49:00 +02:00
const usz offset = player * 8ULL ;
2023-09-06 17:10:30 +02:00
auto & status = m_io_status [ 0 ] ;
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-09-06 17:10:30 +02:00
if ( pressed & & ! status . test_key_pressed ) // Solve the need to hold the Test key
status . test_on = ! status . test_on ;
status . test_key_pressed = pressed ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : coin :
if ( player ! = 0 ) break ;
2023-09-06 17:10:30 +02:00
if ( pressed & & ! status . coin_key_pressed ) // Ensure only one coin is inserted each time the Coin key is pressed
status . coin_counter + + ;
status . coin_key_pressed = pressed ;
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
case usio_btn : : service :
if ( player = = 0 & & pressed )
digital_input | = 0x4000 ;
break ;
2023-05-20 14:34:35 +02:00
case usio_btn : : enter :
2023-05-22 23:10:13 +02:00
if ( player = = 0 & & pressed )
2023-07-23 19:49:00 +02:00
digital_input | = 0x200 ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : up :
2023-05-22 23:10:13 +02:00
if ( player = = 0 & & pressed )
2023-07-23 19:49:00 +02:00
digital_input | = 0x2000 ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : down :
2023-05-22 23:10:13 +02:00
if ( player = = 0 & & pressed )
2023-07-23 19:49:00 +02:00
digital_input | = 0x1000 ;
break ;
case usio_btn : : taiko_hit_side_left :
if ( pressed )
std : : memcpy ( input_buf . data ( ) + 32 + offset , & c_hit , sizeof ( u16 ) ) ;
break ;
case usio_btn : : taiko_hit_center_right :
if ( pressed )
std : : memcpy ( input_buf . data ( ) + 36 + offset , & c_hit , sizeof ( u16 ) ) ;
break ;
case usio_btn : : taiko_hit_side_right :
if ( pressed )
std : : memcpy ( input_buf . data ( ) + 38 + offset , & c_hit , sizeof ( u16 ) ) ;
break ;
case usio_btn : : taiko_hit_center_left :
if ( pressed )
std : : memcpy ( input_buf . data ( ) + 34 + offset , & c_hit , sizeof ( u16 ) ) ;
break ;
default :
break ;
}
} ) ;
2023-09-06 17:10:30 +02:00
if ( player = = 0 & & status . test_on )
digital_input | = 0x80 ;
2023-07-23 19:49:00 +02:00
} ;
for ( usz i = 0 ; i < g_cfg_usio . players . size ( ) ; i + + )
translate_from_pad ( i , i ) ;
std : : memcpy ( input_buf . data ( ) , & digital_input , sizeof ( u16 ) ) ;
2023-09-06 17:10:30 +02:00
std : : memcpy ( input_buf . data ( ) + 16 , & m_io_status [ 0 ] . coin_counter , sizeof ( u16 ) ) ;
2023-07-23 19:49:00 +02:00
response = std : : move ( input_buf ) ;
}
void usb_device_usio : : translate_input_tekken ( )
{
std : : lock_guard lock ( pad : : g_pad_mutex ) ;
const auto handler = pad : : get_current_handler ( ) ;
2023-09-06 17:10:30 +02:00
std : : vector < u8 > input_buf ( 0x180 ) ;
le_t < u64 > digital_input [ 2 ] { } ;
2023-07-24 08:06:13 +02:00
le_t < u16 > digital_input_lm = 0 ;
2023-07-23 19:49:00 +02:00
auto translate_from_pad = [ & ] ( usz pad_number , usz player )
{
if ( ! is_input_allowed ( ) )
{
return ;
}
const auto & pad = : : at32 ( handler - > GetPads ( ) , pad_number ) ;
if ( ! ( pad - > m_port_status & CELL_PAD_STATUS_CONNECTED ) )
{
return ;
}
2023-09-06 17:10:30 +02:00
const usz shift = ( player % 2 ) * 24ULL ;
auto & status = m_io_status [ player / 2 ] ;
auto & input = digital_input [ player / 2 ] ;
2023-07-23 19:49:00 +02:00
const auto & cfg = : : at32 ( g_cfg_usio . players , pad_number ) ;
cfg - > handle_input ( pad , false , [ & ] ( usio_btn btn , u16 /*value*/ , bool pressed )
{
switch ( btn )
{
case usio_btn : : test :
2023-09-06 17:10:30 +02:00
if ( player % 2 ! = 0 )
2023-07-23 19:49:00 +02:00
break ;
2023-09-06 17:10:30 +02:00
if ( pressed & & ! status . test_key_pressed ) // Solve the need to hold the Test button
status . test_on = ! status . test_on ;
status . test_key_pressed = pressed ;
2023-07-23 19:49:00 +02:00
break ;
case usio_btn : : coin :
2023-09-06 17:10:30 +02:00
if ( player % 2 ! = 0 )
2023-07-23 19:49:00 +02:00
break ;
2023-09-06 17:10:30 +02:00
if ( pressed & & ! status . coin_key_pressed ) // Ensure only one coin is inserted each time the Coin button is pressed
status . coin_counter + + ;
status . coin_key_pressed = pressed ;
2023-05-20 14:34:35 +02:00
break ;
case usio_btn : : service :
2023-09-06 17:10:30 +02:00
if ( player % 2 = = 0 & & pressed )
input | = 0x4000 ;
2023-07-23 19:49:00 +02:00
break ;
case usio_btn : : enter :
if ( pressed )
2023-07-24 08:06:13 +02:00
{
2023-09-06 17:10:30 +02:00
input | = 0x800000ULL < < shift ;
2023-07-24 08:06:13 +02:00
if ( player = = 0 )
digital_input_lm | = 0x800 ;
}
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
case usio_btn : : up :
2023-05-22 23:10:13 +02:00
if ( pressed )
2023-07-24 08:06:13 +02:00
{
2023-09-06 17:10:30 +02:00
input | = 0x200000ULL < < shift ;
2023-07-24 08:06:13 +02:00
if ( player = = 0 )
digital_input_lm | = 0x200 ;
}
2023-07-23 19:49:00 +02:00
break ;
case usio_btn : : down :
2023-07-26 11:36:44 +02:00
if ( pressed )
2023-07-24 08:06:13 +02:00
{
2023-09-06 17:10:30 +02:00
input | = 0x100000ULL < < shift ;
2023-07-24 08:06:13 +02:00
if ( player = = 0 )
digital_input_lm | = 0x400 ;
}
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
case usio_btn : : left :
2023-05-22 23:10:13 +02:00
if ( pressed )
2023-07-24 08:06:13 +02:00
{
2023-09-06 17:10:30 +02:00
input | = 0x80000ULL < < shift ;
2023-07-24 08:06:13 +02:00
if ( player = = 0 )
digital_input_lm | = 0x2000 ;
}
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
case usio_btn : : right :
2023-05-22 23:10:13 +02:00
if ( pressed )
2023-07-24 08:06:13 +02:00
{
2023-09-06 17:10:30 +02:00
input | = 0x40000ULL < < shift ;
2023-07-24 08:06:13 +02:00
if ( player = = 0 )
digital_input_lm | = 0x4000 ;
}
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
case usio_btn : : tekken_button1 :
2023-05-22 23:10:13 +02:00
if ( pressed )
2023-07-24 08:06:13 +02:00
{
2023-09-06 17:10:30 +02:00
input | = 0x20000ULL < < shift ;
2023-07-24 08:06:13 +02:00
if ( player = = 0 )
digital_input_lm | = 0x100 ;
}
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
case usio_btn : : tekken_button2 :
2023-05-22 23:10:13 +02:00
if ( pressed )
2023-09-06 17:10:30 +02:00
input | = 0x10000ULL < < shift ;
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
case usio_btn : : tekken_button3 :
2023-05-22 23:10:13 +02:00
if ( pressed )
2023-09-06 17:10:30 +02:00
input | = 0x40000000ULL < < shift ;
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
case usio_btn : : tekken_button4 :
2023-05-22 23:10:13 +02:00
if ( pressed )
2023-09-06 17:10:30 +02:00
input | = 0x20000000ULL < < shift ;
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
case usio_btn : : tekken_button5 :
2023-05-22 23:10:13 +02:00
if ( pressed )
2023-09-06 17:10:30 +02:00
input | = 0x80000000ULL < < shift ;
2023-05-20 14:34:35 +02:00
break ;
2023-07-23 19:49:00 +02:00
default :
2023-05-21 13:43:27 +02:00
break ;
2021-11-24 21:59:48 +01:00
}
2023-05-22 23:10:13 +02:00
} ) ;
2023-09-06 17:10:30 +02:00
if ( player % 2 = = 0 & & status . test_on )
{
input | = 0x80 ;
if ( player = = 0 )
digital_input_lm | = 0x1000 ;
}
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
2023-09-06 17:10:30 +02:00
for ( usz i = 0 ; i < 2 ; i + + )
2023-07-24 08:06:13 +02:00
{
2023-09-06 17:10:30 +02:00
std : : memcpy ( input_buf . data ( ) - i * 0x80 + 0x100 , & digital_input [ i ] , sizeof ( u64 ) ) ;
std : : memcpy ( input_buf . data ( ) - i * 0x80 + 0x100 + 0x10 , & m_io_status [ i ] . coin_counter , sizeof ( u16 ) ) ;
2023-07-24 08:06:13 +02:00
}
std : : memcpy ( input_buf . data ( ) , & digital_input_lm , sizeof ( u16 ) ) ;
2023-07-23 19:49:00 +02:00
2023-09-06 17:10:30 +02:00
input_buf [ 2 ] = 0b00010000 ; // DIP switches, 8 in total
2022-10-20 19:50:14 +02:00
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
{
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 :
{
2023-07-23 19:49:00 +02:00
usio_log . trace ( " Unhandled channel 0 register write(reg: 0x%04X, size: 0x%04X, data: %s) " , reg , data . size ( ) , fmt : : buf_to_hexstring ( data . data ( ) , data . size ( ) ) ) ;
2021-11-24 21:59:48 +01:00
break ;
}
}
}
else if ( channel > = 2 )
{
2023-07-23 19:49:00 +02:00
const u8 page = channel - 2 ;
usio_log . trace ( " Usio write of sram(page: 0x%02X, addr: 0x%04X, size: 0x%04X, data: %s) " , page , reg , data . size ( ) , fmt : : buf_to_hexstring ( data . data ( ) , data . size ( ) ) ) ;
2023-07-23 11:17:49 +02:00
auto & memory = g_fxo - > get < usio_memory > ( ) . backup_memory ;
const usz addr_end = reg + data . size ( ) ;
2023-07-23 19:49:00 +02:00
if ( data . size ( ) > 0 & & page < usio_memory : : page_count & & addr_end < = usio_memory : : page_size )
std : : memcpy ( & memory [ usio_memory : : page_size * page + reg ] , data . data ( ) , data . size ( ) ) ;
2023-07-23 11:17:49 +02:00
else
2023-07-23 19:49:00 +02:00
usio_log . error ( " Usio sram invalid write operation(page: 0x%02X, addr: 0x%04X, size: 0x%04X, data: %s) " , page , reg , data . size ( ) , fmt : : buf_to_hexstring ( data . data ( ) , data . size ( ) ) ) ;
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.
2023-07-23 19:49:00 +02:00
usio_log . trace ( " Unsupported write operation(channel: 0x%02X, addr: 0x%04X, size: 0x%04X, data: %s) " , channel , reg , data . size ( ) , fmt : : buf_to_hexstring ( data . data ( ) , data . size ( ) ) ) ;
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-09-06 17:10:30 +02: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 , 0x02 , 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 ;
}
2023-07-23 19:49:00 +02:00
case 0x1000 :
{
// Often called, gets input from usio for Tekken
translate_input_tekken ( ) ;
break ;
}
2021-11-24 21:59:48 +01:00
case 0x1080 :
{
2023-07-23 19:49:00 +02:00
// Often called, gets input from usio for Taiko
translate_input_taiko ( ) ;
2021-11-24 21:59:48 +01:00
break ;
}
case 0x1800 :
case 0x1880 :
{
// Seems to contain a few extra bytes of info in addition to the firmware string
// Firmware
2023-07-23 19:49:00 +02:00
// "NBGI.;USIO01;Ver1.00;JPN,Multipurpose with PPG."
2023-09-06 17:10:30 +02:00
constexpr std : : array < u8 , 0x180 > info { 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 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x4E , 0x42 , 0x47 , 0x49 , 0x31 , 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 , 0x02 , 0x03 , 0x02 , 0x00 , 0x08 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x03 , 0x00 , 0x03 , 0x00 , 0x75 , 0x6C , 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 , 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 , 0x02 , 0x03 , 0x02 , 0x00 , 0x08 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x03 , 0x00 , 0x03 , 0x00 , 0x75 , 0x6C , 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 } ;
2023-07-24 08:06:13 +02:00
response = { info . begin ( ) + ( reg - 0x1800 ) , info . end ( ) } ;
2021-11-24 21:59:48 +01:00
break ;
}
default :
{
2023-07-23 19:49:00 +02:00
usio_log . trace ( " Unhandled channel 0 register read(reg: 0x%04X, size: 0x%04X) " , reg , size ) ;
2021-11-24 21:59:48 +01:00
break ;
}
}
}
else if ( channel > = 2 )
{
2023-07-23 19:49:00 +02:00
const u8 page = channel - 2 ;
usio_log . trace ( " Usio read of sram(page: 0x%02X, addr: 0x%04X, size: 0x%04X) " , page , reg , size ) ;
2023-07-23 11:17:49 +02:00
auto & memory = g_fxo - > get < usio_memory > ( ) . backup_memory ;
const usz addr_end = reg + size ;
2023-07-23 19:49:00 +02:00
if ( size > 0 & & page < usio_memory : : page_count & & addr_end < = usio_memory : : page_size )
response . insert ( response . end ( ) , memory . begin ( ) + ( usio_memory : : page_size * page + reg ) , memory . begin ( ) + ( usio_memory : : page_size * page + addr_end ) ) ;
2023-07-23 11:17:49 +02:00
else
2023-07-23 19:49:00 +02:00
usio_log . error ( " Usio sram invalid read operation(page: 0x%02X, addr: 0x%04X, size: 0x%04X) " , page , reg , size ) ;
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.
2023-07-23 19:49:00 +02:00
usio_log . trace ( " Unsupported read operation(channel: 0x%02X, addr: 0x%04X, size: 0x%04X) " , channel , reg , size ) ;
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 ;
}
}