mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
3bc7c96191
* move input polling * more * more * more
218 lines
7.9 KiB
Rust
218 lines
7.9 KiB
Rust
use crate::{device, savestates, ui};
|
|
|
|
pub mod gbcart;
|
|
pub mod mempak;
|
|
pub mod rumble;
|
|
pub mod transferpak;
|
|
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, serde::Serialize, serde::Deserialize)]
|
|
pub enum PakType {
|
|
None = 0,
|
|
MemPak = 1,
|
|
RumblePak = 2,
|
|
TransferPak = 3,
|
|
}
|
|
|
|
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct PakHandler {
|
|
#[serde(skip, default = "savestates::default_pak_handler")]
|
|
pub read: fn(&mut device::Device, usize, u16, usize, usize),
|
|
#[serde(skip, default = "savestates::default_pak_handler")]
|
|
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 let Some(netplay) = &mut device.netplay {
|
|
netplay.inputs[channel].0
|
|
} else {
|
|
ui::input::get(&mut device.ui, channel)
|
|
};
|
|
|
|
device.pif.ram[offset..offset + 4].copy_from_slice(&input.data.to_ne_bytes());
|
|
if input.pak_change_pressed {
|
|
// pak change button pressed
|
|
if device::events::get_event(device, device::events::EVENT_TYPE_PAK).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::EVENT_TYPE_PAK,
|
|
device.cpu.clock_rate / 2, // 500ms
|
|
)
|
|
}
|
|
}
|
|
}
|
|
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,
|
|
),
|
|
_ => eprintln!("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 let Some(handler) = handler {
|
|
(handler.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 let Some(handler) = handler {
|
|
(handler.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
|
|
}
|
|
|
|
pub fn pak_switch_event(device: &mut device::Device) {
|
|
for (i, channel) in device.pif.channels.iter_mut().enumerate() {
|
|
if channel.change_pak != PakType::None {
|
|
//stop rumble if it is on
|
|
if let Some(netplay) = &device.netplay {
|
|
if netplay.player_number == i {
|
|
device::ui::input::set_rumble(&device.ui, 0, 0);
|
|
}
|
|
} else {
|
|
device::ui::input::set_rumble(&device.ui, i, 0);
|
|
}
|
|
|
|
let new_pak_type = match channel.change_pak {
|
|
PakType::MemPak => PakType::RumblePak,
|
|
PakType::RumblePak => {
|
|
if device.transferpaks[i].cart.rom.is_empty() {
|
|
PakType::MemPak
|
|
} else {
|
|
PakType::TransferPak
|
|
}
|
|
}
|
|
PakType::TransferPak => PakType::MemPak,
|
|
_ => {
|
|
panic!("Invalid pak type");
|
|
}
|
|
};
|
|
|
|
if new_pak_type == PakType::MemPak {
|
|
channel.pak_handler = Some(device::controller::PakHandler {
|
|
read: device::controller::mempak::read,
|
|
write: device::controller::mempak::write,
|
|
pak_type: new_pak_type,
|
|
});
|
|
ui::video::onscreen_message(
|
|
&format!("P{} MemPak selected", i + 1),
|
|
ui::video::MESSAGE_LENGTH_MESSAGE_SHORT,
|
|
);
|
|
} else if new_pak_type == PakType::RumblePak {
|
|
channel.pak_handler = Some(device::controller::PakHandler {
|
|
read: device::controller::rumble::read,
|
|
write: device::controller::rumble::write,
|
|
pak_type: new_pak_type,
|
|
});
|
|
ui::video::onscreen_message(
|
|
&format!("P{} RumblePak selected", i + 1),
|
|
ui::video::MESSAGE_LENGTH_MESSAGE_SHORT,
|
|
);
|
|
} else if new_pak_type == PakType::TransferPak {
|
|
channel.pak_handler = Some(device::controller::PakHandler {
|
|
read: device::controller::transferpak::read,
|
|
write: device::controller::transferpak::write,
|
|
pak_type: new_pak_type,
|
|
});
|
|
ui::video::onscreen_message(
|
|
&format!("P{} TransferPak selected", i + 1),
|
|
ui::video::MESSAGE_LENGTH_MESSAGE_SHORT,
|
|
);
|
|
}
|
|
channel.change_pak = PakType::None;
|
|
}
|
|
}
|
|
}
|