mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-25 00:04:54 +02:00
* Netplay sync inputs * use wrapping add * non blocking * more * more * more * more * more * more * more * more * more * more * more * more * message * more * close * fixes * redirect rumble * more * fix * more * messages
191 lines
6.9 KiB
Rust
191 lines
6.9 KiB
Rust
use crate::device;
|
|
use crate::netplay;
|
|
use crate::ui;
|
|
|
|
pub mod mempak;
|
|
pub mod rumble;
|
|
pub mod vru;
|
|
|
|
pub const JCMD_STATUS: u8 = 0x00;
|
|
pub const JCMD_CONTROLLER_READ: u8 = 0x01;
|
|
const JCMD_PAK_READ: u8 = 0x02;
|
|
const JCMD_PAK_WRITE: u8 = 0x03;
|
|
pub const JCMD_RESET: u8 = 0xff;
|
|
|
|
//const JDT_NONE: u16 = 0x0000;
|
|
const JDT_JOY_ABS_COUNTERS: u16 = 0x0001; /* joystick with absolute coordinates */
|
|
//const JDT_JOY_REL_COUNTERS: u16 = 0x0002; /* joystick with relative coordinates (= mouse) */
|
|
const JDT_JOY_PORT: u16 = 0x0004; /* has port for external paks */
|
|
const PAK_CHUNK_SIZE: usize = 0x20;
|
|
const CONT_STATUS_PAK_PRESENT: u8 = 1;
|
|
const CONT_STATUS_PAK_NOT_PRESENT: u8 = 2;
|
|
const CONT_FLAVOR: u16 = JDT_JOY_ABS_COUNTERS | JDT_JOY_PORT;
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
pub enum PakType {
|
|
None = 0,
|
|
MemPak = 1,
|
|
RumblePak = 2,
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct PakHandler {
|
|
pub read: fn(&mut device::Device, usize, u16, usize, usize),
|
|
pub write: fn(&mut device::Device, usize, u16, usize, usize),
|
|
pub pak_type: PakType,
|
|
}
|
|
|
|
pub fn process(device: &mut device::Device, channel: usize) {
|
|
let cmd = device.pif.ram[device.pif.channels[channel].tx_buf.unwrap()];
|
|
|
|
match cmd {
|
|
JCMD_RESET | JCMD_STATUS => {
|
|
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = CONT_FLAVOR as u8;
|
|
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 1] =
|
|
(CONT_FLAVOR >> 8) as u8;
|
|
if device.pif.channels[channel].pak_handler.is_none() {
|
|
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] =
|
|
CONT_STATUS_PAK_NOT_PRESENT;
|
|
} else {
|
|
device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] =
|
|
CONT_STATUS_PAK_PRESENT;
|
|
}
|
|
}
|
|
JCMD_CONTROLLER_READ => {
|
|
let offset = device.pif.channels[channel].rx_buf.unwrap();
|
|
let input = if device.netplay.is_none() {
|
|
ui::input::get(&mut device.ui, channel)
|
|
} else {
|
|
if device.netplay.as_ref().unwrap().player_number as usize == channel {
|
|
let local_input = ui::input::get(&mut device.ui, 0);
|
|
netplay::send_input(device.netplay.as_ref().unwrap(), local_input);
|
|
}
|
|
|
|
netplay::get_input(device, channel)
|
|
};
|
|
|
|
device.pif.ram[offset..offset + 4].copy_from_slice(&input.0.to_ne_bytes());
|
|
if input.1 {
|
|
// pak change button pressed
|
|
if device::events::get_event(device, device::events::EventType::PakSwitch).is_none()
|
|
{
|
|
device.pif.channels[channel].change_pak =
|
|
device.pif.channels[channel].pak_handler.unwrap().pak_type;
|
|
device.pif.channels[channel].pak_handler = None;
|
|
device::events::create_event(
|
|
device,
|
|
device::events::EventType::PakSwitch,
|
|
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize]
|
|
+ (device.cpu.clock_rate), // 1 second
|
|
pak_switch_event,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
JCMD_PAK_READ => pak_read_block(
|
|
device,
|
|
device.pif.channels[channel].tx_buf.unwrap() + 1,
|
|
device.pif.channels[channel].rx_buf.unwrap(),
|
|
device.pif.channels[channel].rx_buf.unwrap() + 32,
|
|
channel,
|
|
),
|
|
JCMD_PAK_WRITE => pak_write_block(
|
|
device,
|
|
device.pif.channels[channel].tx_buf.unwrap() + 1,
|
|
device.pif.channels[channel].tx_buf.unwrap() + 3,
|
|
device.pif.channels[channel].rx_buf.unwrap(),
|
|
channel,
|
|
),
|
|
_ => println!("unknown controller command {}", cmd),
|
|
}
|
|
}
|
|
|
|
fn pak_read_block(
|
|
device: &mut device::Device,
|
|
addr_acrc: usize,
|
|
data: usize,
|
|
dcrc: usize,
|
|
channel: usize,
|
|
) {
|
|
let address =
|
|
((device.pif.ram[addr_acrc] as u16) << 8) | (device.pif.ram[addr_acrc + 1] & 0xe0) as u16;
|
|
let handler = device.pif.channels[channel].pak_handler;
|
|
|
|
if handler.is_some() {
|
|
(handler.unwrap().read)(device, channel, address, data, PAK_CHUNK_SIZE);
|
|
device.pif.ram[dcrc] = data_crc(device, data, PAK_CHUNK_SIZE)
|
|
} else {
|
|
device.pif.ram[dcrc] = !data_crc(device, data, PAK_CHUNK_SIZE)
|
|
}
|
|
}
|
|
|
|
fn pak_write_block(
|
|
device: &mut device::Device,
|
|
addr_acrc: usize,
|
|
data: usize,
|
|
dcrc: usize,
|
|
channel: usize,
|
|
) {
|
|
let address =
|
|
((device.pif.ram[addr_acrc] as u16) << 8) | (device.pif.ram[addr_acrc + 1] & 0xe0) as u16;
|
|
let handler = device.pif.channels[channel].pak_handler;
|
|
|
|
if handler.is_some() {
|
|
(handler.unwrap().write)(device, channel, address, data, PAK_CHUNK_SIZE);
|
|
device.pif.ram[dcrc] = data_crc(device, data, PAK_CHUNK_SIZE)
|
|
} else {
|
|
device.pif.ram[dcrc] = !data_crc(device, data, PAK_CHUNK_SIZE)
|
|
}
|
|
}
|
|
|
|
fn data_crc(device: &device::Device, data_offset: usize, size: usize) -> u8 {
|
|
let mut i = 0;
|
|
let mut crc = 0;
|
|
|
|
while i <= size {
|
|
let mut mask = 0x80;
|
|
while mask >= 1 {
|
|
let xor_tap = if crc & 0x80 != 0 { 0x85 } else { 0x00 };
|
|
crc <<= 1;
|
|
if i != size && (device.pif.ram[data_offset + i] & mask) != 0 {
|
|
crc |= 1;
|
|
}
|
|
crc ^= xor_tap;
|
|
mask >>= 1
|
|
}
|
|
i += 1;
|
|
}
|
|
crc
|
|
}
|
|
|
|
fn pak_switch_event(device: &mut device::Device) {
|
|
for (i, channel) in device.pif.channels.iter_mut().enumerate() {
|
|
if channel.change_pak != PakType::None {
|
|
if channel.change_pak == PakType::RumblePak {
|
|
if device.netplay.is_none() {
|
|
device::ui::input::set_rumble(&mut device.ui, i, 0);
|
|
} else if device.netplay.as_ref().unwrap().player_number as usize == i {
|
|
device::ui::input::set_rumble(&mut device.ui, 0, 0);
|
|
}
|
|
|
|
let handler = device::controller::PakHandler {
|
|
read: device::controller::mempak::read,
|
|
write: device::controller::mempak::write,
|
|
pak_type: device::controller::PakType::MemPak,
|
|
};
|
|
channel.pak_handler = Some(handler);
|
|
ui::audio::play_pak_switch(&mut device.ui, PakType::MemPak);
|
|
} else if channel.change_pak == PakType::MemPak {
|
|
let handler = device::controller::PakHandler {
|
|
read: device::controller::rumble::read,
|
|
write: device::controller::rumble::write,
|
|
pak_type: device::controller::PakType::RumblePak,
|
|
};
|
|
channel.pak_handler = Some(handler);
|
|
ui::audio::play_pak_switch(&mut device.ui, PakType::RumblePak);
|
|
}
|
|
channel.change_pak = PakType::None;
|
|
}
|
|
}
|
|
}
|