Compare commits

...

31 Commits

Author SHA1 Message Date
Logan McNaughton b065f807a2 Change box for audio (#157)
* Remove box for audio

* more
2025-01-17 11:27:30 +01:00
Logan McNaughton ee95816385 Use Box for audio files (#156) 2025-01-17 11:07:39 +01:00
Logan McNaughton d021cb5d32 use mimalloc (#155) 2025-01-17 11:06:57 +01:00
Logan McNaughton 9819fad475 fix crash 2025-01-17 09:08:53 +01:00
Logan McNaughton dd767047fd make sure rumble is off when removing pak 2025-01-16 22:21:33 +01:00
Logan McNaughton f6480ea312 slight fix in audio code 2025-01-16 20:04:23 +01:00
Logan McNaughton e4174c7d4b cleanup some pak switching code 2025-01-16 19:49:20 +01:00
Logan McNaughton f3d8080a26 Rumble pak support (#154)
* Rumble pak support

* more

* more

* more

* more

* more

* more

* more

* more
2025-01-16 18:54:44 +01:00
Logan McNaughton b581bdcd40 Use GameController API in profile setup (#153)
* Use GameController API in profile setup

* more

* more

* more

* more

* more

* more

* more
2025-01-16 18:49:46 +01:00
Logan McNaughton 842c81988f Update README.md 2025-01-15 18:38:25 +01:00
Logan McNaughton 45d05cecf0 ignore HatState::Centered when configuring input (#151) 2025-01-15 07:33:31 +01:00
Logan McNaughton eae81c4623 Add integer scaling option (#150)
* Add integer scaling option

* more

* more

* more
2025-01-14 23:23:26 +01:00
Logan McNaughton b3f8688ba5 Bump to 0.1.19 (#149) 2025-01-14 21:53:20 +01:00
Logan McNaughton 0a10b2c0ba use SDL_AddEventWatch 2025-01-14 21:42:03 +01:00
Logan McNaughton be67cb264c Fix linux arm builds (#148) 2025-01-14 21:08:03 +01:00
Logan McNaughton c6c2187652 add fullscreen option (#146)
* add fullscreen option

* fix crash
2025-01-14 19:50:35 +01:00
Logan McNaughton b2c1d88ea4 cleanup translate_address func (#145) 2025-01-14 19:36:59 +01:00
Logan McNaughton 80176766a7 Fix arm linux builds (#144) 2025-01-14 15:34:59 +01:00
Logan McNaughton 7ac6f2adb1 Github actions fix (#142) 2025-01-14 14:15:01 +01:00
Logan McNaughton 643338e086 SDL code cleanup (#141)
* SDL code cleanup

* more
2025-01-14 13:59:47 +01:00
Logan McNaughton d9a08f112d cleanup rsp code (#140)
* cleanup rsp code

* more

* fix
2025-01-14 09:43:04 +01:00
Logan McNaughton 344f41cf47 Add minimum rust version (#138) 2025-01-13 22:56:33 +01:00
Logan McNaughton 9978e28cc3 Bump to 0.1.18 (#137) 2025-01-13 22:30:34 +01:00
Logan McNaughton d3c26fb359 Remove loops in RSP code (#136)
* Remove loops in RSP cose

* more

* more

* more

* more

* more
2025-01-13 22:21:35 +01:00
Logan McNaughton cd106345c9 use weave deinterlacing (#135) 2025-01-13 14:58:35 +01:00
Logan McNaughton f75636f7e5 Cleanup unsafe functions (#134)
* cleanup unsafe blocks

* comment out flush mode stuff

* remove unsafe from input
2025-01-13 10:16:27 +01:00
Logan McNaughton 97e4921b6a Don't transmute values in RSP code (#132)
* remove transmute

* fix

* more

* more

* fix
2025-01-13 09:13:14 +01:00
dependabot[bot] 21300ab7db Update dirs requirement from 5.0 to 6.0 (#133)
---
updated-dependencies:
- dependency-name: dirs
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 06:49:13 +01:00
Logan McNaughton 8fd7688aa7 remove un-needed var 2025-01-12 22:17:47 +01:00
Logan McNaughton a013e72fda set aarch64 linker from env variable (#131) 2025-01-12 20:09:15 +01:00
Logan McNaughton f2812b0434 Bump to 0.1.17 (#130) 2025-01-12 18:31:10 +01:00
30 changed files with 1345 additions and 829 deletions
-3
View File
@@ -3,6 +3,3 @@ rustflags = ["-C", "target-cpu=x86-64-v3"]
[target.'cfg(target_arch = "aarch64")']
rustflags = ["-C", "target-cpu=cortex-a76"]
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
+38 -11
View File
@@ -13,6 +13,7 @@ jobs:
build-linux:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu]
steps:
@@ -30,43 +31,67 @@ jobs:
Types: deb
URIs: http://archive.ubuntu.com/ubuntu/
Suites: noble
Components: main restricted universe
Components: main multiverse universe
Architectures: amd64
Types: deb
URIs: http://security.ubuntu.com/ubuntu/
Suites: noble-security
Components: main restricted universe
Components: main multiverse universe
Architectures: amd64
Types: deb
URIs: http://archive.ubuntu.com/ubuntu/
Suites: noble-updates
Components: main restricted universe
Components: main multiverse universe
Architectures: amd64
Types: deb
URIs: http://azure.ports.ubuntu.com/ubuntu-ports/
URIs: http://archive.ubuntu.com/ubuntu/
Suites: noble-backports
Components: main multiverse universe
Architectures: amd64
Types: deb
URIs: http://ports.ubuntu.com/
Suites: noble
Components: main restricted multiverse universe
Components: main multiverse universe
Architectures: arm64
Types: deb
URIs: http://azure.ports.ubuntu.com/ubuntu-ports/
URIs: http://ports.ubuntu.com/
Suites: noble-security
Components: main multiverse universe
Architectures: arm64
Types: deb
URIs: http://ports.ubuntu.com/
Suites: noble-updates
Components: main restricted multiverse universe
Components: main multiverse universe
Architectures: arm64
Types: deb
URIs: http://ports.ubuntu.com/
Suites: noble-backports
Components: main multiverse universe
Architectures: arm64
EOF
sudo mv deb822sources /etc/apt/sources.list.d/ubuntu.sources
sudo dpkg --add-architecture arm64
sudo apt update
sudo apt -y install libsdl2-dev:arm64 g++-aarch64-linux-gnu
sudo apt-get update
sudo apt-get -y dist-upgrade
sudo apt-get -y install aptitude
sudo aptitude -y install libsdl2-dev:arm64 g++-aarch64-linux-gnu
- name: Install SDL2
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: |
sudo apt update
sudo apt -y install libsdl2-dev
sudo apt-get update
sudo apt-get -y dist-upgrade
sudo apt-get -y install libsdl2-dev
- name: Set aarch64 linker
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
- name: Build
run: |
cargo build --release -v --target=${{ matrix.target }}
@@ -79,6 +104,7 @@ jobs:
build-windows:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
target: [x86_64-pc-windows-msvc]
steps:
@@ -101,6 +127,7 @@ jobs:
build-macos:
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
target: [x86_64-apple-darwin, aarch64-apple-darwin]
steps:
+4 -2
View File
@@ -1,12 +1,13 @@
[package]
name = "gopher64"
version = "0.1.16"
version = "0.1.19"
edition = "2021"
rust-version = "1.82"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dirs = "5.0"
dirs = "6.0"
zip = "2.1"
governor = "0.8"
sevenz-rust = "0.6"
@@ -21,6 +22,7 @@ futures = "0.3"
sdl2-sys = "0.37"
spin_sleep = "1.3"
encoding_rs = "0.8"
mimalloc = "0.1"
clap = { version = "4.4", features = ["derive"] }
[dependencies.sdl2]
+4
View File
@@ -24,6 +24,10 @@ Options:
-V, --version
Print version
```
## discord
There is no dedicated server at this point, but there is a #gopher64 channel on the simple64 server: https://discord.gg/R2cz7S94vD
## goals
1. Performance. I want to be able to use this emulator on my laptop.
+3 -4
View File
@@ -1,5 +1,6 @@
fn main() {
println!("cargo::rerun-if-changed=parallel-rdp");
println!("cargo::rerun-if-changed=src/compat");
let mut simd_build = cc::Build::new();
let mut build = cc::Build::new();
@@ -55,7 +56,6 @@ fn main() {
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let mut simd_header = "";
if os == "windows" {
if arch == "x86_64" {
build.flag("/arch:AVX2");
@@ -76,8 +76,8 @@ fn main() {
} else if arch == "aarch64" {
build.flag("-march=armv8.2-a");
simd_build.flag("-march=armv8.2-a");
simd_build.flag("-DSSE2NEON_SUPPRESS_WARNINGS");
simd_build.file("src/compat/aarch64.c");
simd_header = "src/compat/sse2neon.h";
} else {
panic!("unknown arch")
}
@@ -107,7 +107,7 @@ fn main() {
if arch == "aarch64" {
let simd_bindings = bindgen::Builder::default()
.header(simd_header)
.header("src/compat/sse2neon.h")
.allowlist_function("_mm_setzero_si128")
.allowlist_function("_mm_set_epi8")
.allowlist_function("_mm_movemask_epi8")
@@ -119,7 +119,6 @@ fn main() {
.allowlist_function("_mm_set1_epi8")
.allowlist_function("_mm_mullo_epi16")
.allowlist_function("_mm_cmpeq_epi16")
.allowlist_function("_mm_srli_epi16")
.allowlist_function("_mm_add_epi16")
.allowlist_function("_mm_slli_epi16")
.allowlist_function("_mm_mulhi_epi16")
BIN
View File
Binary file not shown.
Binary file not shown.
+39 -23
View File
@@ -51,6 +51,7 @@ enum vi_registers
};
static bool fullscreen;
static bool integer_scaling;
static SDL_Window *window;
static RDP::CommandProcessor *processor;
static SDL_WSIPlatform *wsi_platform;
@@ -142,7 +143,10 @@ int sdl_event_filter(void *userdata, SDL_Event *event)
emu_running = false;
break;
case SDL_WINDOWEVENT_RESIZED:
wsi_platform->do_resize();
if (emu_running)
{
wsi_platform->do_resize();
}
break;
default:
break;
@@ -163,13 +167,14 @@ int sdl_event_filter(void *userdata, SDL_Event *event)
return 0;
}
void rdp_init(void *_window, GFX_INFO _gfx_info, bool _fullscreen, bool _upscale)
void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_scaling, bool _fullscreen)
{
window = (SDL_Window *)_window;
SDL_SetEventFilter(sdl_event_filter, nullptr);
SDL_AddEventWatch(sdl_event_filter, nullptr);
gfx_info = _gfx_info;
fullscreen = _fullscreen;
integer_scaling = _integer_scaling;
bool window_vsync = 0;
wsi = new WSI;
wsi_platform = new SDL_WSIPlatform;
@@ -231,39 +236,50 @@ void rdp_close()
static void calculate_viewport(float *x, float *y, float *width, float *height)
{
bool window_widescreen = false;
int32_t display_width = (window_widescreen ? 854 : 640);
int32_t display_height = 480;
const int32_t display_width = 320;
const int32_t display_height = 240;
int w, h;
SDL_GetWindowSize(window, &w, &h);
*width = w;
*height = h;
*x = 0;
*y = 0;
int32_t hw = display_height * *width;
int32_t wh = display_width * *height;
if (integer_scaling)
{
// Integer scaling path
int scale_x = w / display_width;
int scale_y = h / display_height;
int scale = (scale_x < scale_y) ? scale_x : scale_y;
if (scale < 1)
scale = 1;
// add letterboxes or pillarboxes if the window has a different aspect ratio
// than the current display mode
if (hw > wh)
{
int32_t w_max = wh / display_height;
*x += (*width - w_max) / 2;
*width = w_max;
// Calculate scaled dimensions
int scaled_width = display_width * scale;
int scaled_height = display_height * scale;
*width = scaled_width;
*height = scaled_height;
}
else if (hw < wh)
else
{
int32_t h_max = hw / display_width;
*y += (*height - h_max) / 2;
*height = h_max;
// Regular scaling path - maintain aspect ratio
float scale_x = w / (float)display_width;
float scale_y = h / (float)display_height;
float scale = (scale_x < scale_y) ? scale_x : scale_y;
*width = display_width * scale;
*height = display_height * scale;
}
// Center the viewport
*x = (w - *width) / 2.0f;
*y = (h - *height) / 2.0f;
}
static void render_frame(Vulkan::Device &device)
{
RDP::ScanoutOptions options = {};
options.persist_frame_on_invalid_input = true;
options.blend_previous_frame = true;
options.upscale_deinterlacing = false;
Vulkan::ImageHandle image = processor->scanout(options);
// Normally reflection is automated.
+1 -1
View File
@@ -23,7 +23,7 @@ extern "C"
uint32_t *VI_WIDTH_REG;
} GFX_INFO;
void rdp_init(void *_window, GFX_INFO _gfx_info, bool fullscreen, bool _upscale);
void rdp_init(void *_window, GFX_INFO _gfx_info, bool _upscale, bool _integer_scaling, bool _fullscreen);
void rdp_close();
void rdp_set_vi_register(uint32_t reg, uint32_t value);
bool rdp_update_screen();
+192
View File
@@ -3,3 +3,195 @@
// Static wrappers
__m128i _mm_srli_epi16__extern(__m128i __a, int __imm8) { return _mm_srli_epi16(__a, __imm8); }
__m128i _mm_insert_epi8__extern(__m128i __a, int __i, const int __imm8)
{
switch (__imm8)
{
case 0:
return _mm_insert_epi8(__a, __i, 0);
case 1:
return _mm_insert_epi8(__a, __i, 1);
case 2:
return _mm_insert_epi8(__a, __i, 2);
case 3:
return _mm_insert_epi8(__a, __i, 3);
case 4:
return _mm_insert_epi8(__a, __i, 4);
case 5:
return _mm_insert_epi8(__a, __i, 5);
case 6:
return _mm_insert_epi8(__a, __i, 6);
case 7:
return _mm_insert_epi8(__a, __i, 7);
case 8:
return _mm_insert_epi8(__a, __i, 8);
case 9:
return _mm_insert_epi8(__a, __i, 9);
case 10:
return _mm_insert_epi8(__a, __i, 10);
case 11:
return _mm_insert_epi8(__a, __i, 11);
case 12:
return _mm_insert_epi8(__a, __i, 12);
case 13:
return _mm_insert_epi8(__a, __i, 13);
case 14:
return _mm_insert_epi8(__a, __i, 14);
case 15:
return _mm_insert_epi8(__a, __i, 15);
default:
abort();
}
}
int _mm_extract_epi8__extern(__m128i __a, const int __imm8)
{
switch (__imm8)
{
case 0:
return _mm_extract_epi8(__a, 0);
case 1:
return _mm_extract_epi8(__a, 1);
case 2:
return _mm_extract_epi8(__a, 2);
case 3:
return _mm_extract_epi8(__a, 3);
case 4:
return _mm_extract_epi8(__a, 4);
case 5:
return _mm_extract_epi8(__a, 5);
case 6:
return _mm_extract_epi8(__a, 6);
case 7:
return _mm_extract_epi8(__a, 7);
case 8:
return _mm_extract_epi8(__a, 8);
case 9:
return _mm_extract_epi8(__a, 9);
case 10:
return _mm_extract_epi8(__a, 10);
case 11:
return _mm_extract_epi8(__a, 11);
case 12:
return _mm_extract_epi8(__a, 12);
case 13:
return _mm_extract_epi8(__a, 13);
case 14:
return _mm_extract_epi8(__a, 14);
case 15:
return _mm_extract_epi8(__a, 15);
default:
abort();
}
}
__m128i _mm_insert_epi16__extern(__m128i __a, int __i, const int __imm8)
{
switch (__imm8)
{
case 0:
return _mm_insert_epi16(__a, __i, 0);
case 1:
return _mm_insert_epi16(__a, __i, 1);
case 2:
return _mm_insert_epi16(__a, __i, 2);
case 3:
return _mm_insert_epi16(__a, __i, 3);
case 4:
return _mm_insert_epi16(__a, __i, 4);
case 5:
return _mm_insert_epi16(__a, __i, 5);
case 6:
return _mm_insert_epi16(__a, __i, 6);
case 7:
return _mm_insert_epi16(__a, __i, 7);
default:
abort();
}
}
int _mm_extract_epi16__extern(__m128i __a, const int __imm8)
{
switch (__imm8)
{
case 0:
return _mm_extract_epi16(__a, 0);
case 1:
return _mm_extract_epi16(__a, 1);
case 2:
return _mm_extract_epi16(__a, 2);
case 3:
return _mm_extract_epi16(__a, 3);
case 4:
return _mm_extract_epi16(__a, 4);
case 5:
return _mm_extract_epi16(__a, 5);
case 6:
return _mm_extract_epi16(__a, 6);
case 7:
return _mm_extract_epi16(__a, 7);
default:
abort();
}
}
__m128i _mm_insert_epi32__extern(__m128i __a, int __i, const int __imm8)
{
switch (__imm8)
{
case 0:
return _mm_insert_epi32(__a, __i, 0);
case 1:
return _mm_insert_epi32(__a, __i, 1);
case 2:
return _mm_insert_epi32(__a, __i, 2);
case 3:
return _mm_insert_epi32(__a, __i, 3);
default:
abort();
}
}
int _mm_extract_epi32__extern(__m128i __a, const int __imm8)
{
switch (__imm8)
{
case 0:
return _mm_extract_epi32(__a, 0);
case 1:
return _mm_extract_epi32(__a, 1);
case 2:
return _mm_extract_epi32(__a, 2);
case 3:
return _mm_extract_epi32(__a, 3);
default:
abort();
}
}
__m128i _mm_insert_epi64__extern(__m128i __a, int64_t __i, const int __imm8)
{
switch (__imm8)
{
case 0:
return _mm_insert_epi64(__a, __i, 0);
case 1:
return _mm_insert_epi64(__a, __i, 1);
default:
abort();
}
}
int64_t _mm_extract_epi64__extern(__m128i __a, const int __imm8)
{
switch (__imm8)
{
case 0:
return _mm_extract_epi64(__a, 0);
case 1:
return _mm_extract_epi64(__a, 1);
default:
abort();
}
}
+43
View File
@@ -1,4 +1,47 @@
unsafe extern "C" {
#[link_name = "_mm_srli_epi16__extern"]
pub fn _mm_srli_epi16(__a: __m128i, __imm8: ::std::os::raw::c_int) -> __m128i;
#[link_name = "_mm_insert_epi8__extern"]
pub fn _mm_insert_epi8(
__a: __m128i,
__i: ::std::os::raw::c_int,
__imm8: ::std::os::raw::c_int,
) -> __m128i;
#[link_name = "_mm_extract_epi8__extern"]
pub fn _mm_extract_epi8(__a: __m128i, __imm8: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
#[link_name = "_mm_insert_epi16__extern"]
pub fn _mm_insert_epi16(
__a: __m128i,
__i: ::std::os::raw::c_int,
__imm8: ::std::os::raw::c_int,
) -> __m128i;
#[link_name = "_mm_extract_epi16__extern"]
pub fn _mm_extract_epi16(__a: __m128i, __imm8: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
#[link_name = "_mm_insert_epi32__extern"]
pub fn _mm_insert_epi32(
__a: __m128i,
__i: ::std::os::raw::c_int,
__imm8: ::std::os::raw::c_int,
) -> __m128i;
#[link_name = "_mm_extract_epi32__extern"]
pub fn _mm_extract_epi32(__a: __m128i, __imm8: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
#[link_name = "_mm_insert_epi64__extern"]
pub fn _mm_insert_epi64(
__a: __m128i,
__i: ::std::os::raw::c_longlong,
__imm8: ::std::os::raw::c_int,
) -> __m128i;
#[link_name = "_mm_extract_epi64__extern"]
pub fn _mm_extract_epi64(
__a: __m128i,
__imm8: ::std::os::raw::c_int,
) -> ::std::os::raw::c_longlong;
}
+7 -8
View File
@@ -30,7 +30,6 @@ pub mod exceptions;
pub mod fpu_instructions;
pub mod is_viewer;
pub mod memory;
pub mod mempak;
pub mod mi;
pub mod pi;
pub mod pif;
@@ -46,7 +45,6 @@ pub mod sram;
pub mod tlb;
pub mod unmapped;
pub mod vi;
pub mod vru;
pub fn run_game(
file_path: &std::path::Path,
@@ -68,7 +66,7 @@ pub fn run_game(
mi::init(device);
pif::init(device);
if device.ui.config.input.emulate_vru {
vru::init(device);
controller::vru::init(device);
}
memory::init(device);
rsp_interface::init(device);
@@ -179,7 +177,7 @@ pub struct Device {
si: si::Si,
ri: ri::Ri,
flashram: sram::Flashram,
pub vru: vru::Vru,
pub vru: controller::vru::Vru,
}
impl Device {
@@ -240,8 +238,8 @@ impl Device {
cop1: cop1::Cop1 {
fcr0: 0,
fcr31: 0,
#[cfg(target_arch = "x86_64")]
flush_mode: 0,
//#[cfg(target_arch = "x86_64")]
//flush_mode: 0,
fgr32: [[0; 4]; 32],
fgr64: [[0; 8]; 32],
instrs: [cop1::reserved; 32],
@@ -288,6 +286,7 @@ impl Device {
rx_buf: None,
process: None,
pak_handler: None,
change_pak: controller::PakType::None,
}; 5],
},
cart: cart_rom::Cart {
@@ -341,7 +340,7 @@ impl Device {
sync_point: false,
cycle_counter: 0,
gpr: [0; 32],
vpr: [0; 32],
vpr: unsafe { [_mm_setzero_si128(); 32] },
reciprocals: [0; 512],
inverse_square_roots: [0; 512],
divdp: false,
@@ -419,7 +418,7 @@ impl Device {
silicon_id: [sram::FLASHRAM_TYPE_ID, sram::MX29L1100_ID],
mode: sram::FlashramMode::ReadArray,
},
vru: vru::Vru {
vru: controller::vru::Vru {
status: 0,
voice_state: 0,
load_offset: 0,
+56 -2
View File
@@ -1,6 +1,10 @@
use crate::device;
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;
@@ -12,14 +16,22 @@ 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_NOT_PRESENT: u8 = 0;
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) {
@@ -41,7 +53,23 @@ pub fn process(device: &mut device::Device, channel: usize) {
JCMD_CONTROLLER_READ => {
let offset = device.pif.channels[channel].rx_buf.unwrap();
let input = ui::input::get(&mut device.ui, channel);
device.pif.ram[offset..offset + 4].copy_from_slice(&input.to_ne_bytes());
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,
@@ -118,3 +146,29 @@ pub fn data_crc(device: &device::Device, data_offset: usize, size: usize) -> u8
}
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 {
if channel.change_pak == PakType::RumblePak {
device::ui::input::set_rumble(&mut device.ui, i, 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;
}
}
}
+20
View File
@@ -0,0 +1,20 @@
use crate::device;
pub fn read(device: &mut device::Device, _channel: usize, address: u16, data: usize, size: usize) {
let value: u8 = if (0x8000..0x9000).contains(&address) {
0x80
} else {
0x00
};
for i in 0..size {
device.pif.ram[data + i] = value;
}
}
pub fn write(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) {
if address == 0xc000 {
let rumble = device.pif.ram[data + size - 1];
device::ui::input::set_rumble(&mut device.ui, channel, rumble);
}
}
+19 -18
View File
@@ -17,8 +17,8 @@ use crate::device;
//const FCR31_CAUSE_INVALID_BIT: u32 = 1 << 16;
const FCR31_CAUSE_UNIMP_BIT: u32 = 1 << 17;
pub const FCR31_CMP_BIT: u32 = 1 << 23;
#[cfg(target_arch = "x86_64")]
const FCR31_FS_BIT: u32 = 1 << 24;
//#[cfg(target_arch = "x86_64")]
//const FCR31_FS_BIT: u32 = 1 << 24;
const FCR31_CAUSE_MASK: u32 = 0b00000000000000111111000000000000;
const FCR31_ENABLE_MASK: u32 = 0b00000000000000000000111110000000;
@@ -27,8 +27,8 @@ const FCR31_WRITE_MASK: u32 = 0b00000001100000111111111111111111;
pub struct Cop1 {
pub fcr0: u32,
pub fcr31: u32,
#[cfg(target_arch = "x86_64")]
pub flush_mode: u32,
//#[cfg(target_arch = "x86_64")]
//pub flush_mode: u32,
pub fgr32: [[u8; 4]; 32],
pub fgr64: [[u8; 8]; 32],
pub instrs: [fn(&mut device::Device, u32); 32],
@@ -368,27 +368,28 @@ pub fn set_control_registers_fpu(device: &mut device::Device, index: u32, data:
{
device::exceptions::floating_point_exception(device)
}
/*
#[cfg(target_arch = "x86_64")]
{
unsafe {
let flush_mode;
if (device.cpu.cop1.fcr31 & 2) != 0 {
if (device.cpu.cop1.fcr31 & FCR31_FS_BIT) != 0 {
flush_mode = std::arch::x86_64::_MM_FLUSH_ZERO_OFF
} else {
flush_mode = std::arch::x86_64::_MM_FLUSH_ZERO_ON;
}
let flush_mode;
if (device.cpu.cop1.fcr31 & 2) != 0 {
if (device.cpu.cop1.fcr31 & FCR31_FS_BIT) != 0 {
flush_mode = std::arch::x86_64::_MM_FLUSH_ZERO_OFF
} else {
flush_mode = std::arch::x86_64::_MM_FLUSH_ZERO_ON;
}
if flush_mode != device.cpu.cop1.flush_mode {
#[allow(deprecated)]
std::arch::x86_64::_MM_SET_FLUSH_ZERO_MODE(flush_mode);
device.cpu.cop1.flush_mode = flush_mode;
}
} else {
flush_mode = std::arch::x86_64::_MM_FLUSH_ZERO_ON;
}
if flush_mode != device.cpu.cop1.flush_mode {
#[allow(deprecated)]
unsafe {
std::arch::x86_64::_MM_SET_FLUSH_ZERO_MODE(flush_mode)
};
device.cpu.cop1.flush_mode = flush_mode;
}
}
*/
}
_ => {
panic!("unknown FCR register")
+1
View File
@@ -12,6 +12,7 @@ pub enum EventType {
SPDma,
Compare,
Vru,
PakSwitch,
Count,
}
+1 -4
View File
@@ -56,13 +56,10 @@ pub fn translate_address(
address: u64,
access_type: AccessType,
) -> (u64, bool, bool) {
let mut cached = false;
if (address & 0xc0000000) != 0x80000000 {
return device::tlb::get_physical_address(device, address, access_type);
} else if address & 0x20000000 == 0 {
cached = true;
}
(address & 0x1FFFFFFF, cached, false)
(address & 0x1FFFFFFF, address & 0x20000000 == 0, false)
}
pub fn data_read(
+5 -3
View File
@@ -15,6 +15,7 @@ pub struct PifChannel {
pub rx_buf: Option<usize>,
pub process: Option<fn(&mut device::Device, usize)>,
pub pak_handler: Option<device::controller::PakHandler>,
pub change_pak: device::controller::PakType,
}
pub const PIF_RAM_SIZE: usize = 64;
@@ -225,8 +226,9 @@ pub fn init(device: &mut device::Device) {
device.pif.ram[0x27] = device.cart.cic_seed;
let mempak_handler = device::controller::PakHandler {
read: device::mempak::read,
write: device::mempak::write,
read: device::controller::mempak::read,
write: device::controller::mempak::write,
pak_type: device::controller::PakType::MemPak,
};
for i in 0..4 {
@@ -237,7 +239,7 @@ pub fn init(device: &mut device::Device) {
}
if device.ui.config.input.emulate_vru {
device.pif.channels[3].pak_handler = None;
device.pif.channels[3].process = Some(device::vru::process);
device.pif.channels[3].process = Some(device::controller::vru::process);
}
device.pif.channels[4].process = Some(device::cart::process)
}
+1 -1
View File
@@ -35,7 +35,7 @@ pub struct Cpu {
pub cycle_counter: u64,
pub shuffle: [__m128i; 16],
pub gpr: [u32; 32],
pub vpr: [u128; 32],
pub vpr: [__m128i; 32],
pub reciprocals: [u16; 512],
pub inverse_square_roots: [u16; 512],
pub vcol: __m128i,
+331 -130
View File
@@ -2,6 +2,8 @@
use device::__m128i;
#[cfg(target_arch = "aarch64")]
include!(concat!(env!("OUT_DIR"), "/simd_bindings.rs"));
#[cfg(target_arch = "aarch64")]
include!("../compat/aarch64.rs");
use crate::device;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
@@ -44,28 +46,136 @@ pub fn sign_extend_7bit_offset(offset: u8, shift_amount: u32) -> u32 {
(((soffset) as i32) as u32) << shift_amount
}
pub fn modify_vpr_byte(vpr: &mut u128, element: u8, value: u8) {
let pos = 15 - (element & 15);
let mask = 0xFF << (pos * 8);
*vpr &= !mask;
*vpr |= (value as u128) << (pos * 8);
pub fn modify_vpr8(vpr: &mut __m128i, element: u8, value: u8) {
unsafe {
*vpr = match element & 15 {
0 => _mm_insert_epi8(*vpr, value as i32, 15),
1 => _mm_insert_epi8(*vpr, value as i32, 14),
2 => _mm_insert_epi8(*vpr, value as i32, 13),
3 => _mm_insert_epi8(*vpr, value as i32, 12),
4 => _mm_insert_epi8(*vpr, value as i32, 11),
5 => _mm_insert_epi8(*vpr, value as i32, 10),
6 => _mm_insert_epi8(*vpr, value as i32, 9),
7 => _mm_insert_epi8(*vpr, value as i32, 8),
8 => _mm_insert_epi8(*vpr, value as i32, 7),
9 => _mm_insert_epi8(*vpr, value as i32, 6),
10 => _mm_insert_epi8(*vpr, value as i32, 5),
11 => _mm_insert_epi8(*vpr, value as i32, 4),
12 => _mm_insert_epi8(*vpr, value as i32, 3),
13 => _mm_insert_epi8(*vpr, value as i32, 2),
14 => _mm_insert_epi8(*vpr, value as i32, 1),
15 => _mm_insert_epi8(*vpr, value as i32, 0),
_ => unreachable!(),
};
}
}
pub fn get_vpr_byte(vpr: u128, element: u8) -> u8 {
let pos = 15 - (element & 15);
(vpr >> (pos * 8)) as u8
pub fn get_vpr8(vpr: __m128i, element: u8) -> u8 {
unsafe {
match element & 15 {
0 => _mm_extract_epi8(vpr, 15) as u8,
1 => _mm_extract_epi8(vpr, 14) as u8,
2 => _mm_extract_epi8(vpr, 13) as u8,
3 => _mm_extract_epi8(vpr, 12) as u8,
4 => _mm_extract_epi8(vpr, 11) as u8,
5 => _mm_extract_epi8(vpr, 10) as u8,
6 => _mm_extract_epi8(vpr, 9) as u8,
7 => _mm_extract_epi8(vpr, 8) as u8,
8 => _mm_extract_epi8(vpr, 7) as u8,
9 => _mm_extract_epi8(vpr, 6) as u8,
10 => _mm_extract_epi8(vpr, 5) as u8,
11 => _mm_extract_epi8(vpr, 4) as u8,
12 => _mm_extract_epi8(vpr, 3) as u8,
13 => _mm_extract_epi8(vpr, 2) as u8,
14 => _mm_extract_epi8(vpr, 1) as u8,
15 => _mm_extract_epi8(vpr, 0) as u8,
_ => unreachable!(),
}
}
}
pub fn modify_vpr_element(vpr: &mut u128, element: u8, value: u16) {
let pos = 7 - (element & 7);
let mask = 0xFFFF << (pos * 16);
*vpr &= !mask;
*vpr |= (value as u128) << (pos * 16);
pub fn modify_vpr16(vpr: &mut __m128i, element: u8, value: u16) {
unsafe {
*vpr = match element & 7 {
0 => _mm_insert_epi16(*vpr, value as i32, 7),
1 => _mm_insert_epi16(*vpr, value as i32, 6),
2 => _mm_insert_epi16(*vpr, value as i32, 5),
3 => _mm_insert_epi16(*vpr, value as i32, 4),
4 => _mm_insert_epi16(*vpr, value as i32, 3),
5 => _mm_insert_epi16(*vpr, value as i32, 2),
6 => _mm_insert_epi16(*vpr, value as i32, 1),
7 => _mm_insert_epi16(*vpr, value as i32, 0),
_ => unreachable!(),
};
}
}
pub fn get_vpr_element(vpr: u128, element: u8) -> u16 {
let pos = 7 - (element & 7);
(vpr >> (pos * 16)) as u16
pub fn get_vpr16(vpr: __m128i, element: u8) -> u16 {
unsafe {
match element & 7 {
0 => _mm_extract_epi16(vpr, 7) as u16,
1 => _mm_extract_epi16(vpr, 6) as u16,
2 => _mm_extract_epi16(vpr, 5) as u16,
3 => _mm_extract_epi16(vpr, 4) as u16,
4 => _mm_extract_epi16(vpr, 3) as u16,
5 => _mm_extract_epi16(vpr, 2) as u16,
6 => _mm_extract_epi16(vpr, 1) as u16,
7 => _mm_extract_epi16(vpr, 0) as u16,
_ => unreachable!(),
}
}
}
pub fn modify_vpr32(vpr: &mut __m128i, element: u8, value: u32) {
unsafe {
*vpr = match element & 3 {
0 => _mm_insert_epi32(*vpr, value as i32, 3),
1 => _mm_insert_epi32(*vpr, value as i32, 2),
2 => _mm_insert_epi32(*vpr, value as i32, 1),
3 => _mm_insert_epi32(*vpr, value as i32, 0),
_ => unreachable!(),
};
}
}
pub fn get_vpr32(vpr: __m128i, element: u8) -> u32 {
unsafe {
match element & 3 {
0 => _mm_extract_epi32(vpr, 3) as u32,
1 => _mm_extract_epi32(vpr, 2) as u32,
2 => _mm_extract_epi32(vpr, 1) as u32,
3 => _mm_extract_epi32(vpr, 0) as u32,
_ => unreachable!(),
}
}
}
pub fn modify_vpr64(vpr: &mut __m128i, element: u8, value: u64) {
unsafe {
*vpr = match element & 1 {
0 => _mm_insert_epi64(*vpr, value as i64, 1),
1 => _mm_insert_epi64(*vpr, value as i64, 0),
_ => unreachable!(),
};
}
}
pub fn get_vpr64(vpr: __m128i, element: u8) -> u64 {
unsafe {
match element & 1 {
0 => _mm_extract_epi64(vpr, 1) as u64,
1 => _mm_extract_epi64(vpr, 0) as u64,
_ => unreachable!(),
}
}
}
pub fn modify_vpr128(vpr: &mut __m128i, value: u128) {
unsafe { *vpr = std::mem::transmute::<u128, __m128i>(value) }
}
pub fn get_vpr128(vpr: __m128i) -> u128 {
unsafe { std::mem::transmute::<__m128i, u128>(vpr) }
}
pub fn j(device: &mut device::Device, opcode: u32) {
@@ -486,8 +596,8 @@ pub fn mtc0(device: &mut device::Device, opcode: u32) {
}
pub fn mfc2(device: &mut device::Device, opcode: u32) {
let hi = get_vpr_byte(device.rsp.cpu.vpr[rd(opcode) as usize], velement(opcode));
let lo = get_vpr_byte(
let hi = get_vpr8(device.rsp.cpu.vpr[rd(opcode) as usize], velement(opcode));
let lo = get_vpr8(
device.rsp.cpu.vpr[rd(opcode) as usize],
velement(opcode) + 1,
);
@@ -525,13 +635,13 @@ pub fn cfc2(device: &mut device::Device, opcode: u32) {
}
pub fn mtc2(device: &mut device::Device, opcode: u32) {
modify_vpr_byte(
modify_vpr8(
&mut device.rsp.cpu.vpr[rd(opcode) as usize],
velement(opcode),
(device.rsp.cpu.gpr[rt(opcode) as usize] >> 8) as u8,
);
if velement(opcode) != 15 {
modify_vpr_byte(
modify_vpr8(
&mut device.rsp.cpu.vpr[rd(opcode) as usize],
velement(opcode) + 1,
device.rsp.cpu.gpr[rt(opcode) as usize] as u8,
@@ -594,7 +704,7 @@ pub fn lbv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 0));
let element = velement(opcode);
modify_vpr_byte(
modify_vpr8(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
@@ -606,15 +716,25 @@ pub fn lsv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 1));
let mut element = velement(opcode);
let end = std::cmp::min(element + 2, 16);
while element < end {
modify_vpr_byte(
if element % 2 == 0 {
modify_vpr16(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
element / 2,
(device.rsp.mem[(address & 0xFFF) as usize] as u16) << 8
| (device.rsp.mem[((address + 1) & 0xFFF) as usize] as u16),
);
address += 1;
element += 1;
} else {
let end = std::cmp::min(element + 2, 16);
while element < end {
modify_vpr8(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
);
address += 1;
element += 1;
}
}
}
@@ -623,15 +743,28 @@ pub fn llv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 2));
let mut element = velement(opcode);
let end = std::cmp::min(element + 4, 16);
while element < end {
modify_vpr_byte(
if element % 4 == 0 {
let mut value = 0;
for i in 0..4 {
value |= (device.rsp.mem[((address + i) & 0xFFF) as usize] as u32) << (8 * (3 - i));
}
modify_vpr32(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
element / 4,
value,
);
address += 1;
element += 1;
} else {
let end = std::cmp::min(element + 4, 16);
while element < end {
modify_vpr8(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
);
address += 1;
element += 1;
}
}
}
@@ -640,15 +773,28 @@ pub fn ldv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 3));
let mut element = velement(opcode);
let end = std::cmp::min(element + 8, 16);
while element < end {
modify_vpr_byte(
if element % 8 == 0 {
let mut value = 0;
for i in 0..8 {
value |= (device.rsp.mem[((address + i) & 0xFFF) as usize] as u64) << (8 * (7 - i));
}
modify_vpr64(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
element / 8,
value,
);
address += 1;
element += 1;
} else {
let end = std::cmp::min(element + 8, 16);
while element < end {
modify_vpr8(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
);
address += 1;
element += 1;
}
}
}
@@ -657,15 +803,24 @@ pub fn lqv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4));
let mut element = velement(opcode);
let end = std::cmp::min(16 + element - ((address & 15) as u8), 16);
while element < end {
modify_vpr_byte(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
);
address += 1;
element += 1;
if element == 0 && address % 16 == 0 {
let mut value = 0;
for i in 0..16 {
value |= (device.rsp.mem[((address + i) & 0xFFF) as usize] as u128) << (8 * (15 - i));
}
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
} else {
let end = std::cmp::min(16 + element - ((address & 15) as u8), 16);
while element < end {
modify_vpr8(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
);
address += 1;
element += 1;
}
}
}
@@ -675,14 +830,22 @@ pub fn lrv(device: &mut device::Device, opcode: u32) {
let mut element = 16u8.wrapping_sub(((address & 15) as u8).wrapping_sub(velement(opcode)));
address &= !15;
while element < 16 {
modify_vpr_byte(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
);
address += 1;
element += 1;
if element == 0 {
let mut value = 0;
for i in 0..16 {
value |= (device.rsp.mem[((address + i) & 0xFFF) as usize] as u128) << (8 * (15 - i));
}
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
} else {
while element < 16 {
modify_vpr8(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
element,
device.rsp.mem[(address & 0xFFF) as usize],
);
address += 1;
element += 1;
}
}
}
@@ -692,15 +855,15 @@ pub fn lpv(device: &mut device::Device, opcode: u32) {
let index = ((address & 7) as u8).wrapping_sub(velement(opcode));
address &= !7;
for offset in 0..8 {
modify_vpr_element(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
offset,
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(offset)) & 15) as u32))
& 0xFFF) as usize] as u16)
<< 8,
);
let mut value = 0;
for i in 0..8 {
value |= (((device.rsp.mem
[((address.wrapping_add(((index.wrapping_add(i)) & 15) as u32)) & 0xFFF) as usize]
as u16)
<< 8) as u128)
<< (16 * (7 - i));
}
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
}
pub fn luv(device: &mut device::Device, opcode: u32) {
@@ -709,15 +872,15 @@ pub fn luv(device: &mut device::Device, opcode: u32) {
let index = ((address & 7) as u8).wrapping_sub(velement(opcode));
address &= !7;
for offset in 0..8 {
modify_vpr_element(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
offset,
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(offset)) & 15) as u32))
& 0xFFF) as usize] as u16)
<< 7,
);
let mut value = 0;
for i in 0..8 {
value |= (((device.rsp.mem
[((address.wrapping_add(((index.wrapping_add(i)) & 15) as u32)) & 0xFFF) as usize]
as u16)
<< 7) as u128)
<< (16 * (7 - i));
}
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
}
pub fn lhv(device: &mut device::Device, opcode: u32) {
@@ -726,15 +889,15 @@ pub fn lhv(device: &mut device::Device, opcode: u32) {
let index = ((address & 7) as u8).wrapping_sub(velement(opcode));
address &= !7;
for offset in 0..8 {
modify_vpr_element(
&mut device.rsp.cpu.vpr[rt(opcode) as usize],
offset,
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(offset * 2)) & 15) as u32))
& 0xFFF) as usize] as u16)
<< 7,
);
let mut value = 0;
for i in 0..8 {
value |= (((device.rsp.mem
[((address.wrapping_add(((index.wrapping_add(i * 2)) & 15) as u32)) & 0xFFF) as usize]
as u16)
<< 7) as u128)
<< (16 * (7 - i));
}
modify_vpr128(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value);
}
pub fn lfv(device: &mut device::Device, opcode: u32) {
@@ -745,17 +908,17 @@ pub fn lfv(device: &mut device::Device, opcode: u32) {
address &= !7;
let start = velement(opcode);
let end = std::cmp::min(start + 8, 16);
let mut tmp: u128 = 0;
let mut tmp: __m128i = unsafe { _mm_setzero_si128() };
let mut offset: u8 = 0;
while offset < 4 {
modify_vpr_element(
modify_vpr16(
&mut tmp,
offset,
(device.rsp.mem[((address.wrapping_add(((index.wrapping_add(offset * 4)) & 15) as u32))
& 0xFFF) as usize] as u16)
<< 7,
);
modify_vpr_element(
modify_vpr16(
&mut tmp,
offset + 4,
(device.rsp.mem[((address
@@ -767,8 +930,8 @@ pub fn lfv(device: &mut device::Device, opcode: u32) {
}
offset = start;
while offset < end {
let value = get_vpr_byte(tmp, offset);
modify_vpr_byte(&mut device.rsp.cpu.vpr[rt(opcode) as usize], offset, value);
let value = get_vpr8(tmp, offset);
modify_vpr8(&mut device.rsp.cpu.vpr[rt(opcode) as usize], offset, value);
offset += 1;
}
}
@@ -784,7 +947,7 @@ pub fn ltv(device: &mut device::Device, opcode: u32) {
let vtbase = rt(opcode) & !7;
let mut vtoff = (velement(opcode)) as u32 >> 1;
for i in 0..8 {
modify_vpr_byte(
modify_vpr8(
&mut device.rsp.cpu.vpr[(vtbase + vtoff) as usize],
i * 2,
device.rsp.mem[(address & 0xFFF) as usize],
@@ -793,7 +956,7 @@ pub fn ltv(device: &mut device::Device, opcode: u32) {
if address == begin + 16 {
address = begin
}
modify_vpr_byte(
modify_vpr8(
&mut device.rsp.cpu.vpr[(vtbase + vtoff) as usize],
i * 2 + 1,
device.rsp.mem[(address & 0xFFF) as usize],
@@ -811,7 +974,7 @@ pub fn sbv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 0));
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], velement(opcode))
get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], velement(opcode))
}
pub fn ssv(device: &mut device::Device, opcode: u32) {
@@ -819,12 +982,20 @@ pub fn ssv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 1));
let mut element = velement(opcode);
let end = element + 2;
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element);
address += 1;
element += 1;
if element % 2 == 0 {
let start = (address & 0xFFF) as usize;
device.rsp.mem[start..start + 2].copy_from_slice(
&get_vpr16(device.rsp.cpu.vpr[rt(opcode) as usize], element / 2).to_be_bytes(),
);
} else {
let end = element + 2;
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], element);
address += 1;
element += 1;
}
}
}
@@ -833,12 +1004,20 @@ pub fn slv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 2));
let mut element = velement(opcode);
let end = element + 4;
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element);
address += 1;
element += 1;
if element % 4 == 0 {
let start = (address & 0xFFF) as usize;
device.rsp.mem[start..start + 4].copy_from_slice(
&get_vpr32(device.rsp.cpu.vpr[rt(opcode) as usize], element / 4).to_be_bytes(),
);
} else {
let end = element + 4;
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], element);
address += 1;
element += 1;
}
}
}
@@ -847,12 +1026,20 @@ pub fn sdv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 3));
let mut element = velement(opcode);
let end = element + 8;
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element);
address += 1;
element += 1;
if element % 8 == 0 {
let start = (address & 0xFFF) as usize;
device.rsp.mem[start..start + 8].copy_from_slice(
&get_vpr64(device.rsp.cpu.vpr[rt(opcode) as usize], element / 8).to_be_bytes(),
);
} else {
let end = element + 8;
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], element);
address += 1;
element += 1;
}
}
}
@@ -861,12 +1048,19 @@ pub fn sqv(device: &mut device::Device, opcode: u32) {
.wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4));
let mut element = velement(opcode);
let end = element + (16 - (address & 15)) as u8;
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element);
address += 1;
element += 1;
if element == 0 && address % 16 == 0 {
let start = (address & 0xFFF) as usize;
device.rsp.mem[start..start + 16]
.copy_from_slice(&get_vpr128(device.rsp.cpu.vpr[rt(opcode) as usize]).to_be_bytes());
} else {
let end = element + (16 - (address & 15)) as u8;
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], element);
address += 1;
element += 1;
}
}
}
@@ -878,11 +1072,18 @@ pub fn srv(device: &mut device::Device, opcode: u32) {
let end = element + (address & 15) as u8;
let base = (16 - (address & 15)) as u8;
address &= !15;
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element + base);
address += 1;
element += 1;
if element == 0 && base == 0 {
let start = (address & 0xFFF) as usize;
device.rsp.mem[start..start + 16]
.copy_from_slice(&get_vpr128(device.rsp.cpu.vpr[rt(opcode) as usize]).to_be_bytes());
} else {
while element < end {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], element + base);
address += 1;
element += 1;
}
}
}
@@ -895,10 +1096,10 @@ pub fn spv(device: &mut device::Device, opcode: u32) {
while element < end {
if (element & 15) < 8 {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], (element & 7) << 1);
get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], (element & 7) << 1);
} else {
device.rsp.mem[(address & 0xFFF) as usize] =
(get_vpr_element(device.rsp.cpu.vpr[rt(opcode) as usize], element) >> 7) as u8;
(get_vpr16(device.rsp.cpu.vpr[rt(opcode) as usize], element) >> 7) as u8;
}
address += 1;
element += 1;
@@ -914,10 +1115,10 @@ pub fn suv(device: &mut device::Device, opcode: u32) {
while element < end {
if (element & 15) < 8 {
device.rsp.mem[(address & 0xFFF) as usize] =
(get_vpr_element(device.rsp.cpu.vpr[rt(opcode) as usize], element) >> 7) as u8;
(get_vpr16(device.rsp.cpu.vpr[rt(opcode) as usize], element) >> 7) as u8;
} else {
device.rsp.mem[(address & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], (element & 7) << 1);
get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], (element & 7) << 1);
}
address += 1;
element += 1;
@@ -933,8 +1134,8 @@ pub fn shv(device: &mut device::Device, opcode: u32) {
address &= !7;
for offset in 0..8 {
let byte_val = element + offset * 2;
let value = (get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], byte_val) << 1)
| (get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], byte_val + 1) >> 7);
let value = (get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], byte_val) << 1)
| (get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], byte_val + 1) >> 7);
device.rsp.mem[((address + ((index + offset * 2) & 15) as u32) & 0xFFF) as usize] = value;
}
}
@@ -963,7 +1164,7 @@ pub fn sfv(device: &mut device::Device, opcode: u32) {
let mut offset = 0;
for element in elements {
device.rsp.mem[((address + ((base + offset) & 15)) & 0xFFF) as usize] =
(get_vpr_element(device.rsp.cpu.vpr[rt(opcode) as usize], element) >> 7) as u8;
(get_vpr16(device.rsp.cpu.vpr[rt(opcode) as usize], element) >> 7) as u8;
offset += 4;
}
}
@@ -978,7 +1179,7 @@ pub fn swv(device: &mut device::Device, opcode: u32) {
address &= !7;
while element < end {
device.rsp.mem[((address + (base & 15)) & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element);
get_vpr8(device.rsp.cpu.vpr[rt(opcode) as usize], element);
base += 1;
element += 1;
}
@@ -995,11 +1196,11 @@ pub fn stv(device: &mut device::Device, opcode: u32) {
let mut offset = start;
while offset < end {
device.rsp.mem[((address + (base & 15)) & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[offset as usize], element);
get_vpr8(device.rsp.cpu.vpr[offset as usize], element);
base = base.wrapping_add(1);
element += 1;
device.rsp.mem[((address + (base & 15)) & 0xFFF) as usize] =
get_vpr_byte(device.rsp.cpu.vpr[offset as usize], element);
get_vpr8(device.rsp.cpu.vpr[offset as usize], element);
base = base.wrapping_add(1);
element += 1;
offset += 1;
File diff suppressed because it is too large Load Diff
+9
View File
@@ -1,4 +1,6 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
mod device;
mod ui;
@@ -18,6 +20,12 @@ struct Args {
help = "Create a new input profile (keyboard/gamepad mappings)."
)]
configure_input_profile: Option<String>,
#[arg(
short,
long,
help = "Use DirectInput when configuring a new input profile."
)]
use_dinput: bool,
#[arg(
short,
long,
@@ -139,6 +147,7 @@ fn main() {
ui::input::configure_input_profile(
&mut device.ui,
args.configure_input_profile.unwrap(),
args.use_dinput,
);
return;
}
+6
View File
@@ -18,6 +18,7 @@ pub struct Ui {
pub sdl_context: Option<sdl2::Sdl>,
pub video_subsystem: Option<sdl2::VideoSubsystem>,
pub audio_subsystem: Option<sdl2::AudioSubsystem>,
pub pak_audio: Option<audio::PakAudio>,
pub joystick_subsystem: Option<sdl2::JoystickSubsystem>,
#[allow(dead_code)]
pub controller_subsystem: Option<sdl2::GameControllerSubsystem>,
@@ -59,18 +60,22 @@ impl Ui {
input::Controllers {
game_controller: None,
joystick: None,
rumble: false,
},
input::Controllers {
game_controller: None,
joystick: None,
rumble: false,
},
input::Controllers {
game_controller: None,
joystick: None,
rumble: false,
},
input::Controllers {
game_controller: None,
joystick: None,
rumble: false,
},
],
config_file_path,
@@ -99,6 +104,7 @@ impl Ui {
controller_subsystem: Some(controller_subsystem),
window: None,
audio_device: None,
pak_audio: None,
}
}
}
+54 -3
View File
@@ -1,6 +1,11 @@
use crate::device;
use crate::ui;
pub struct PakAudio {
mempak: Vec<u8>,
rumblepak: Vec<u8>,
}
pub fn init(ui: &mut ui::Ui, frequency: u64) {
let desired_spec = sdl2::audio::AudioSpecDesired {
freq: Some(frequency as i32),
@@ -9,16 +14,62 @@ pub fn init(ui: &mut ui::Ui, frequency: u64) {
};
ui.audio_device = Some(
ui.audio_subsystem
.as_mut()
.as_ref()
.unwrap()
.open_queue::<i16, _>(None, &desired_spec)
.unwrap(),
);
ui.audio_device.as_mut().unwrap().resume();
let audio_device = ui.audio_device.as_ref().unwrap();
audio_device.resume();
let mempak_audio = Box::new(sdl2::audio::AudioSpecWAV::load_wav_rw(
&mut sdl2::rwops::RWops::from_bytes(include_bytes!("../../data/mempak.wav"))
.expect("Could not mempak WAV file"),
))
.expect("Could not load mempak WAV file");
let rumblepak_audio = Box::new(
sdl2::audio::AudioSpecWAV::load_wav_rw(
&mut sdl2::rwops::RWops::from_bytes(include_bytes!("../../data/rumblepak.wav"))
.expect("Could not load rumblepak WAV file"),
)
.expect("Could not load rumblepak WAV file"),
);
let cvt = sdl2::audio::AudioCVT::new(
mempak_audio.format,
mempak_audio.channels,
mempak_audio.freq,
audio_device.spec().format,
audio_device.spec().channels,
audio_device.spec().freq,
)
.expect("Could not create AudioCVT");
ui.pak_audio = Some(PakAudio {
mempak: cvt.convert(mempak_audio.buffer().to_vec()),
rumblepak: cvt.convert(rumblepak_audio.buffer().to_vec()),
});
}
pub fn play_pak_switch(ui: &mut ui::Ui, pak: device::controller::PakType) {
let sound;
if pak == device::controller::PakType::RumblePak {
sound = &ui.pak_audio.as_ref().unwrap().rumblepak;
} else if pak == device::controller::PakType::MemPak {
sound = &ui.pak_audio.as_ref().unwrap().mempak;
} else {
return;
}
let audio_device = ui.audio_device.as_ref().unwrap();
let i16_buffer: Vec<i16> = sound
.chunks_exact(2)
.map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]]))
.collect();
audio_device.queue_audio(&i16_buffer).unwrap();
}
pub fn play_audio(device: &mut device::Device, dram_addr: usize, length: u64) {
let audio_device = device.ui.audio_device.as_mut().unwrap();
let audio_device = device.ui.audio_device.as_ref().unwrap();
let mut primary_buffer: Vec<i16> = vec![0; length as usize / 2];
let mut i = 0;
while i < length as usize / 2 {
+14 -7
View File
@@ -2,12 +2,13 @@ use crate::ui;
#[derive(serde::Serialize, serde::Deserialize)]
pub struct InputProfile {
pub keys: [(bool, i32); 18],
pub controller_buttons: [(bool, i32); 14],
pub controller_axis: [(bool, i32, i16); 18],
pub joystick_buttons: [(bool, u32); 14],
pub joystick_hat: [(bool, u32, i8); 14],
pub joystick_axis: [(bool, u32, i16); 18],
pub keys: [(bool, i32); ui::input::PROFILE_SIZE],
pub controller_buttons: [(bool, i32); ui::input::PROFILE_SIZE],
pub controller_axis: [(bool, i32, i16); ui::input::PROFILE_SIZE],
pub joystick_buttons: [(bool, u32); ui::input::PROFILE_SIZE],
pub joystick_hat: [(bool, u32, u8); ui::input::PROFILE_SIZE],
pub joystick_axis: [(bool, u32, i16); ui::input::PROFILE_SIZE],
pub dinput: bool,
}
#[derive(serde::Serialize, serde::Deserialize)]
@@ -21,6 +22,8 @@ pub struct Input {
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Video {
pub upscale: bool,
pub integer_scaling: bool,
pub fullscreen: bool,
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Config {
@@ -45,7 +48,11 @@ impl Config {
controller_enabled: [true, false, false, false],
emulate_vru: false,
},
video: Video { upscale: false },
video: Video {
upscale: false,
integer_scaling: false,
fullscreen: false,
},
}
}
}
+55 -27
View File
@@ -14,13 +14,26 @@ pub struct GopherEguiApp {
input_profiles: Vec<String>,
controller_enabled: [bool; 4],
upscale: bool,
integer_scaling: bool,
fullscreen: bool,
emulate_vru: bool,
dinput: bool,
show_vru_dialog: bool,
vru_window_receiver: Option<std::sync::mpsc::Receiver<Vec<String>>>,
vru_word_notifier: Option<std::sync::mpsc::Sender<String>>,
vru_word_list: Vec<String>,
}
struct SaveConfig {
selected_controller: [i32; 4],
selected_profile: [String; 4],
controller_enabled: [bool; 4],
upscale: bool,
integer_scaling: bool,
fullscreen: bool,
emulate_vru: bool,
}
fn get_input_profiles(game_ui: &ui::Ui) -> Vec<String> {
let mut profiles = vec![];
for key in game_ui.config.input.input_profiles.keys() {
@@ -84,8 +97,11 @@ impl GopherEguiApp {
input_profiles: get_input_profiles(&game_ui),
controller_enabled: game_ui.config.input.controller_enabled,
upscale: game_ui.config.video.upscale,
integer_scaling: game_ui.config.video.integer_scaling,
fullscreen: game_ui.config.video.fullscreen,
emulate_vru: game_ui.config.input.emulate_vru,
show_vru_dialog: false,
dinput: false,
vru_window_receiver: None,
vru_word_notifier: None,
vru_word_list: Vec::new(),
@@ -93,16 +109,9 @@ impl GopherEguiApp {
}
}
fn save_config(
game_ui: &mut ui::Ui,
selected_controller: [i32; 4],
selected_profile: [String; 4],
controller_enabled: [bool; 4],
upscale: bool,
emulate_vru: bool,
) {
fn save_config(game_ui: &mut ui::Ui, save_config_items: SaveConfig) {
let joystick_subsystem = game_ui.joystick_subsystem.as_ref().unwrap();
for (pos, item) in selected_controller.iter().enumerate() {
for (pos, item) in save_config_items.selected_controller.iter().enumerate() {
if *item != -1 {
game_ui.config.input.controller_assignment[pos] = Some(
joystick_subsystem
@@ -115,24 +124,28 @@ fn save_config(
}
}
game_ui.config.input.input_profile_binding = selected_profile;
game_ui.config.input.controller_enabled = controller_enabled;
game_ui.config.input.input_profile_binding = save_config_items.selected_profile;
game_ui.config.input.controller_enabled = save_config_items.controller_enabled;
game_ui.config.video.upscale = upscale;
game_ui.config.input.emulate_vru = emulate_vru;
game_ui.config.video.upscale = save_config_items.upscale;
game_ui.config.video.integer_scaling = save_config_items.integer_scaling;
game_ui.config.video.fullscreen = save_config_items.fullscreen;
game_ui.config.input.emulate_vru = save_config_items.emulate_vru;
}
impl Drop for GopherEguiApp {
fn drop(&mut self) {
let mut game_ui = ui::Ui::new(self.config_dir.clone());
save_config(
&mut game_ui,
self.selected_controller,
self.selected_profile.clone(),
self.controller_enabled,
self.upscale,
self.emulate_vru,
);
let save_config_items = SaveConfig {
selected_controller: self.selected_controller,
selected_profile: self.selected_profile.clone(),
controller_enabled: self.controller_enabled,
upscale: self.upscale,
integer_scaling: self.integer_scaling,
fullscreen: self.fullscreen,
emulate_vru: self.emulate_vru,
};
save_config(&mut game_ui, save_config_items);
}
}
@@ -147,13 +160,19 @@ impl eframe::App for GopherEguiApp {
ui.text_edit_singleline(&mut self.profile_name)
.labelled_by(name_label.id);
});
ui.checkbox(&mut self.dinput, "Use DirectInput");
ui.horizontal(|ui| {
if ui.button("Configure Profile").clicked() {
let profile_name = self.profile_name.clone();
let config_dir = self.config_dir.clone();
execute(async {
let dinput = self.dinput;
execute(async move {
let mut game_ui = ui::Ui::new(config_dir);
ui::input::configure_input_profile(&mut game_ui, profile_name);
ui::input::configure_input_profile(
&mut game_ui,
profile_name,
dinput,
);
});
self.configure_profile = false;
if !self.profile_name.is_empty()
@@ -181,6 +200,8 @@ impl eframe::App for GopherEguiApp {
let selected_profile = self.selected_profile.clone();
let controller_enabled = self.controller_enabled;
let upscale = self.upscale;
let integer_scaling = self.integer_scaling;
let fullscreen = self.fullscreen;
let emulate_vru = self.emulate_vru;
let config_dir = self.config_dir.clone();
let cache_dir = self.cache_dir.clone();
@@ -215,14 +236,18 @@ impl eframe::App for GopherEguiApp {
panic!("could not create running file: {}", result.err().unwrap())
}
let mut device = device::Device::new(config_dir);
save_config(
&mut device.ui,
let save_config_items = SaveConfig {
selected_controller,
selected_profile,
controller_enabled,
upscale,
integer_scaling,
fullscreen,
emulate_vru,
);
};
save_config(&mut device.ui, save_config_items);
if emulate_vru {
device.vru.window_notifier = Some(vru_window_notifier);
device.vru.word_receiver = Some(vru_word_receiver);
@@ -232,7 +257,7 @@ impl eframe::App for GopherEguiApp {
std::path::Path::new(file.path()),
data_dir,
&mut device,
false,
fullscreen,
);
let result = std::fs::remove_file(running_file.clone());
if result.is_err() {
@@ -299,6 +324,9 @@ impl eframe::App for GopherEguiApp {
});
ui.add_space(32.0);
ui.checkbox(&mut self.upscale, "High-Res Graphics");
ui.checkbox(&mut self.integer_scaling, "Integer Scaling");
ui.checkbox(&mut self.fullscreen, "Fullscreen (Esc closes game)");
ui.add_space(32.0);
ui.checkbox(
&mut self.emulate_vru,
"Emulate VRU (connects VRU to controller port 4)",
+271 -156
View File
@@ -16,17 +16,20 @@ const D_CBUTTON: usize = 10;
const U_CBUTTON: usize = 11;
const R_TRIG: usize = 12;
const L_TRIG: usize = 13;
const X_AXIS: usize = 16;
const Y_AXIS: usize = 24;
const AXIS_LEFT: usize = 14;
const AXIS_RIGHT: usize = 15;
const AXIS_UP: usize = 16;
const AXIS_DOWN: usize = 17;
const CHANGE_PAK: usize = 18;
pub const PROFILE_SIZE: usize = 19;
const X_AXIS_SHIFT: usize = 16;
const Y_AXIS_SHIFT: usize = 24;
const MAX_AXIS_VALUE: f64 = 85.0;
pub struct Controllers {
pub rumble: bool,
pub game_controller: Option<sdl2::controller::GameController>,
pub joystick: Option<sdl2::joystick::Joystick>,
}
@@ -78,94 +81,110 @@ pub fn set_axis_from_joystick(
(x, y)
}
fn get_axis_from_i32(axis: i32) -> sdl2::controller::Axis {
match axis {
0 => sdl2::controller::Axis::LeftX,
1 => sdl2::controller::Axis::LeftY,
2 => sdl2::controller::Axis::RightX,
3 => sdl2::controller::Axis::RightY,
4 => sdl2::controller::Axis::TriggerLeft,
5 => sdl2::controller::Axis::TriggerRight,
_ => panic!("Invalid axis"),
}
}
fn get_button_from_i32(button: i32) -> sdl2::controller::Button {
match button {
0 => sdl2::controller::Button::A,
1 => sdl2::controller::Button::B,
2 => sdl2::controller::Button::X,
3 => sdl2::controller::Button::Y,
4 => sdl2::controller::Button::Back,
5 => sdl2::controller::Button::Guide,
6 => sdl2::controller::Button::Start,
7 => sdl2::controller::Button::LeftStick,
8 => sdl2::controller::Button::RightStick,
9 => sdl2::controller::Button::LeftShoulder,
10 => sdl2::controller::Button::RightShoulder,
11 => sdl2::controller::Button::DPadUp,
12 => sdl2::controller::Button::DPadDown,
13 => sdl2::controller::Button::DPadLeft,
14 => sdl2::controller::Button::DPadRight,
_ => panic!("Invalid button"),
}
}
pub fn set_axis_from_controller(
profile: &ui::config::InputProfile,
controller: &sdl2::controller::GameController,
) -> (f64, f64) {
unsafe {
let mut x = 0.0;
let mut y = 0.0;
if profile.controller_axis[AXIS_LEFT].0 {
let axis_position =
controller.axis(std::mem::transmute::<i32, sdl2::controller::Axis>(
profile.controller_axis[AXIS_LEFT].1,
));
if axis_position as isize * profile.controller_axis[AXIS_LEFT].2 as isize > 0 {
x = axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64;
}
let mut x = 0.0;
let mut y = 0.0;
if profile.controller_axis[AXIS_LEFT].0 {
let axis_position =
controller.axis(get_axis_from_i32(profile.controller_axis[AXIS_LEFT].1));
if axis_position as isize * profile.controller_axis[AXIS_LEFT].2 as isize > 0 {
x = axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64;
}
if profile.controller_axis[AXIS_RIGHT].0 {
let axis_position =
controller.axis(std::mem::transmute::<i32, sdl2::controller::Axis>(
profile.controller_axis[AXIS_RIGHT].1,
));
if axis_position as isize * profile.controller_axis[AXIS_RIGHT].2 as isize > 0 {
x = axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64;
}
}
if profile.controller_axis[AXIS_DOWN].0 {
let axis_position =
controller.axis(std::mem::transmute::<i32, sdl2::controller::Axis>(
profile.controller_axis[AXIS_DOWN].1,
));
if axis_position as isize * profile.controller_axis[AXIS_DOWN].2 as isize > 0 {
y = (axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64).neg();
}
}
if profile.controller_axis[AXIS_UP].0 {
let axis_position =
controller.axis(std::mem::transmute::<i32, sdl2::controller::Axis>(
profile.controller_axis[AXIS_UP].1,
));
if axis_position as isize * profile.controller_axis[AXIS_UP].2 as isize > 0 {
y = (axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64).neg();
}
}
(x, y)
}
if profile.controller_axis[AXIS_RIGHT].0 {
let axis_position =
controller.axis(get_axis_from_i32(profile.controller_axis[AXIS_RIGHT].1));
if axis_position as isize * profile.controller_axis[AXIS_RIGHT].2 as isize > 0 {
x = axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64;
}
}
if profile.controller_axis[AXIS_DOWN].0 {
let axis_position =
controller.axis(get_axis_from_i32(profile.controller_axis[AXIS_DOWN].1));
if axis_position as isize * profile.controller_axis[AXIS_DOWN].2 as isize > 0 {
y = (axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64).neg();
}
}
if profile.controller_axis[AXIS_UP].0 {
let axis_position = controller.axis(get_axis_from_i32(profile.controller_axis[AXIS_UP].1));
if axis_position as isize * profile.controller_axis[AXIS_UP].2 as isize > 0 {
y = (axis_position as f64 * MAX_AXIS_VALUE / i16::MAX as f64).neg();
}
}
(x, y)
}
pub fn set_axis_from_keys(
profile: &ui::config::InputProfile,
keyboard_state: sdl2::keyboard::KeyboardState,
keyboard_state: &sdl2::keyboard::KeyboardState,
) -> (f64, f64) {
unsafe {
let mut x = 0.0;
let mut y = 0.0;
if profile.keys[AXIS_LEFT].0
&& keyboard_state
.is_scancode_pressed(std::mem::transmute::<i32, sdl2::keyboard::Scancode>(
profile.keys[AXIS_LEFT].1,
))
{
x = -MAX_AXIS_VALUE
}
if profile.keys[AXIS_RIGHT].0
&& keyboard_state
.is_scancode_pressed(std::mem::transmute::<i32, sdl2::keyboard::Scancode>(
profile.keys[AXIS_RIGHT].1,
))
{
x = MAX_AXIS_VALUE
}
if profile.keys[AXIS_DOWN].0
&& keyboard_state
.is_scancode_pressed(std::mem::transmute::<i32, sdl2::keyboard::Scancode>(
profile.keys[AXIS_DOWN].1,
))
{
y = -MAX_AXIS_VALUE
}
if profile.keys[AXIS_UP].0
&& keyboard_state
.is_scancode_pressed(std::mem::transmute::<i32, sdl2::keyboard::Scancode>(
profile.keys[AXIS_UP].1,
))
{
y = MAX_AXIS_VALUE
}
(x, y)
let mut x = 0.0;
let mut y = 0.0;
if profile.keys[AXIS_LEFT].0
&& keyboard_state.is_scancode_pressed(
sdl2::keyboard::Scancode::from_i32(profile.keys[AXIS_LEFT].1).unwrap(),
)
{
x = -MAX_AXIS_VALUE
}
if profile.keys[AXIS_RIGHT].0
&& keyboard_state.is_scancode_pressed(
sdl2::keyboard::Scancode::from_i32(profile.keys[AXIS_RIGHT].1).unwrap(),
)
{
x = MAX_AXIS_VALUE
}
if profile.keys[AXIS_DOWN].0
&& keyboard_state.is_scancode_pressed(
sdl2::keyboard::Scancode::from_i32(profile.keys[AXIS_DOWN].1).unwrap(),
)
{
y = -MAX_AXIS_VALUE
}
if profile.keys[AXIS_UP].0
&& keyboard_state.is_scancode_pressed(
sdl2::keyboard::Scancode::from_i32(profile.keys[AXIS_UP].1).unwrap(),
)
{
y = MAX_AXIS_VALUE
}
(x, y)
}
pub fn set_buttons_from_joystick(
@@ -182,9 +201,7 @@ pub fn set_buttons_from_joystick(
let profile_joystick_hat = profile.joystick_hat[i];
if profile_joystick_hat.0
&& joystick.hat(profile_joystick_hat.1).unwrap()
== unsafe {
std::mem::transmute::<i8, sdl2::joystick::HatState>(profile_joystick_hat.2)
}
== sdl2::joystick::HatState::from_raw(profile_joystick_hat.2)
{
*keys |= 1 << i;
}
@@ -208,21 +225,12 @@ pub fn set_buttons_from_controller(
) {
let profile_controller_button = profile.controller_buttons[i];
if profile_controller_button.0 {
unsafe {
*keys |= (controller.button(std::mem::transmute::<i32, sdl2::controller::Button>(
profile_controller_button.1,
)) as u32)
<< i;
}
*keys |= (controller.button(get_button_from_i32(profile_controller_button.1)) as u32) << i;
}
let profile_controller_axis = profile.controller_axis[i];
if profile_controller_axis.0 {
let axis_position = unsafe {
controller.axis(std::mem::transmute::<i32, sdl2::controller::Axis>(
profile_controller_axis.1,
))
};
let axis_position = controller.axis(get_axis_from_i32(profile_controller_axis.1));
if axis_position as isize * profile_controller_axis.2 as isize > 0
&& axis_position.saturating_abs() > i16::MAX / 2
{
@@ -231,9 +239,70 @@ pub fn set_buttons_from_controller(
}
}
pub fn get(ui: &mut ui::Ui, channel: usize) -> u32 {
let context = ui.sdl_context.as_mut().unwrap();
let events = context.event_pump().unwrap();
pub fn set_rumble(ui: &mut ui::Ui, channel: usize, rumble: u8) {
if !ui.controllers[channel].rumble {
return;
}
let controller = &mut ui.controllers[channel].game_controller;
let joystick = &mut ui.controllers[channel].joystick;
if controller.is_some() {
controller
.as_mut()
.unwrap()
.set_rumble(
(rumble & 1) as u16 * u16::MAX,
(rumble & 1) as u16 * u16::MAX,
(rumble & 1) as u32 * 60000,
)
.unwrap();
} else if joystick.is_some() {
joystick
.as_mut()
.unwrap()
.set_rumble(
(rumble & 1) as u16 * u16::MAX,
(rumble & 1) as u16 * u16::MAX,
(rumble & 1) as u32 * 60000,
)
.unwrap();
}
}
pub fn change_paks(
profile: &ui::config::InputProfile,
joystick: &Option<sdl2::joystick::Joystick>,
controller: &Option<sdl2::controller::GameController>,
keyboard_state: &sdl2::keyboard::KeyboardState,
) -> bool {
let controller_button = profile.controller_buttons[CHANGE_PAK];
let joystick_button = profile.joystick_buttons[CHANGE_PAK];
let joystick_hat = profile.joystick_hat[CHANGE_PAK];
let key = profile.keys[CHANGE_PAK];
let mut pressed = false;
if controller_button.0 && controller.is_some() {
pressed = controller
.as_ref()
.unwrap()
.button(get_button_from_i32(controller_button.1));
} else if joystick_button.0 && joystick.is_some() {
pressed = joystick
.as_ref()
.unwrap()
.button(joystick_button.1)
.unwrap();
} else if joystick_hat.0 && joystick.is_some() {
pressed = joystick.as_ref().unwrap().hat(joystick_hat.1).unwrap()
== sdl2::joystick::HatState::from_raw(joystick_hat.2);
} else if key.0 {
pressed =
keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::from_i32(key.1).unwrap());
}
pressed
}
pub fn get(ui: &mut ui::Ui, channel: usize) -> (u32, bool) {
let events = ui.sdl_context.as_ref().unwrap().event_pump().unwrap();
let keyboard_state = events.keyboard_state();
let profile_name = ui.config.input.input_profile_binding[channel].clone();
@@ -245,20 +314,17 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> u32 {
if profile_name != "default" || channel == 0 {
let profile_key = profile.keys[i];
if profile_key.0 {
unsafe {
keys |= (keyboard_state
.is_scancode_pressed(std::mem::transmute::<i32, sdl2::keyboard::Scancode>(
profile_key.1,
)) as u32)
<< i;
}
keys |= (keyboard_state
.is_scancode_pressed(sdl2::keyboard::Scancode::from_i32(profile_key.1).unwrap())
as u32)
<< i;
}
}
if joystick.is_some() {
set_buttons_from_joystick(profile, i, joystick.as_ref().unwrap(), &mut keys);
} else if controller.is_some() {
if controller.is_some() {
set_buttons_from_controller(profile, i, controller.as_ref().unwrap(), &mut keys);
} else if joystick.is_some() {
set_buttons_from_joystick(profile, i, joystick.as_ref().unwrap(), &mut keys);
}
}
@@ -266,19 +332,23 @@ pub fn get(ui: &mut ui::Ui, channel: usize) -> u32 {
let mut y: f64 = 0.0;
if profile_name != "default" || channel == 0 {
(x, y) = set_axis_from_keys(profile, keyboard_state);
(x, y) = set_axis_from_keys(profile, &keyboard_state);
}
if joystick.is_some() {
(x, y) = set_axis_from_joystick(profile, joystick.as_ref().unwrap())
} else if controller.is_some() {
if controller.is_some() {
(x, y) = set_axis_from_controller(profile, controller.as_ref().unwrap())
} else if joystick.is_some() {
(x, y) = set_axis_from_joystick(profile, joystick.as_ref().unwrap())
}
bound_axis(&mut x, &mut y);
keys |= (x.round() as i8 as u8 as u32) << X_AXIS;
keys |= (y.round() as i8 as u8 as u32) << Y_AXIS;
keys
keys |= (x.round() as i8 as u8 as u32) << X_AXIS_SHIFT;
keys |= (y.round() as i8 as u8 as u32) << Y_AXIS_SHIFT;
(
keys,
change_paks(profile, joystick, controller, &keyboard_state),
)
}
pub fn list_controllers(ui: &ui::Ui) {
@@ -322,7 +392,7 @@ pub fn clear_bindings(ui: &mut ui::Ui) {
}
}
pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
if profile == "default" {
println!("Profile name cannot be default");
return;
@@ -331,10 +401,20 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
println!("Profile name cannot be empty");
return;
}
let joystick_subsystem = ui.sdl_context.as_ref().unwrap().joystick().unwrap();
let controller_subsystem = ui.controller_subsystem.as_ref().unwrap();
let joystick_subsystem = ui.joystick_subsystem.as_ref().unwrap();
let mut controllers = vec![];
let mut joysticks = vec![];
for i in 0..joystick_subsystem.num_joysticks().unwrap() {
joysticks.push(ui.sdl_context.as_ref().unwrap().joystick().unwrap().open(i));
if !dinput {
if let Ok(controller) = controller_subsystem.open(i) {
controllers.push(controller);
continue;
}
}
if let Ok(joystick) = joystick_subsystem.open(i) {
joysticks.push(joystick);
}
}
let mut builder =
@@ -348,7 +428,7 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
let font =
rusttype::Font::try_from_bytes(include_bytes!("../../data/Roboto-Regular.ttf")).unwrap();
let key_labels = [
let key_labels: [(&str, usize); PROFILE_SIZE] = [
("A", A_BUTTON),
("B", B_BUTTON),
("Start", START_BUTTON),
@@ -367,17 +447,21 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
("Control Stick Down", AXIS_DOWN),
("Control Stick Left", AXIS_LEFT),
("Control Stick Right", AXIS_RIGHT),
("Change Pak", CHANGE_PAK),
];
let mut new_keys = [(false, 0); 18];
let mut new_joystick_buttons = [(false, 0u32); 14];
let mut new_joystick_hat = [(false, 0u32, 0); 14];
let mut new_joystick_axis = [(false, 0u32, 0); 18];
let mut new_keys = [(false, 0); PROFILE_SIZE];
let mut new_joystick_buttons = [(false, 0u32); PROFILE_SIZE];
let mut new_joystick_hat = [(false, 0u32, 0); PROFILE_SIZE];
let mut new_joystick_axis = [(false, 0u32, 0); PROFILE_SIZE];
let mut new_controller_buttons = [(false, 0i32); PROFILE_SIZE];
let mut new_controller_axis = [(false, 0i32, 0); PROFILE_SIZE];
let mut last_axis_result = (false, 0, 0);
let mut event_pump = ui.sdl_context.as_ref().unwrap().event_pump().unwrap();
let mut last_joystick_axis_result = (false, 0, 0);
let mut last_controller_axis_result = (false, 0, 0);
let mut events = ui.sdl_context.as_ref().unwrap().event_pump().unwrap();
for (key, value) in key_labels.iter() {
for _event in event_pump.poll_iter() {} // clear events
for _event in events.poll_iter() {} // clear events
ui::video::draw_text(
format!("Select binding for: {key}").as_str(),
@@ -388,7 +472,7 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
let mut key_set = false;
while !key_set {
std::thread::sleep(std::time::Duration::from_millis(100));
for event in event_pump.poll_iter() {
for event in events.poll_iter() {
match event {
sdl2::event::Event::Window {
win_event: sdl2::event::WindowEvent::Close,
@@ -401,34 +485,57 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
new_keys[*value] = (true, scancode as i32);
key_set = true
}
sdl2::event::Event::ControllerButtonDown { button, .. } => {
if !controllers.is_empty() {
new_controller_buttons[*value] = (true, button as i32);
key_set = true
}
}
sdl2::event::Event::ControllerAxisMotion {
axis,
value: axis_value,
..
} => {
if !controllers.is_empty() && axis_value.saturating_abs() > i16::MAX / 2 {
let result =
(true, axis as i32, axis_value / axis_value.saturating_abs());
if result != last_controller_axis_result {
new_controller_axis[*value] = result;
last_controller_axis_result = result;
key_set = true
}
}
}
sdl2::event::Event::JoyButtonDown { button_idx, .. } => {
new_joystick_buttons[*value] = (true, button_idx as u32);
key_set = true
if !joysticks.is_empty() {
new_joystick_buttons[*value] = (true, button_idx as u32);
key_set = true
}
}
sdl2::event::Event::JoyHatMotion { hat_idx, state, .. } => {
new_joystick_hat[*value] = unsafe {
(
if !joysticks.is_empty() && state != sdl2::joystick::HatState::Centered {
new_joystick_hat[*value] = (
true,
hat_idx as u32,
std::mem::transmute::<sdl2::joystick::HatState, i8>(state),
)
};
key_set = true
sdl2::joystick::HatState::to_raw(state),
);
key_set = true
}
}
sdl2::event::Event::JoyAxisMotion {
axis_idx,
value: axis_value,
..
} => {
if axis_value.saturating_abs() > 24576 {
if !joysticks.is_empty() && axis_value.saturating_abs() > i16::MAX / 2 {
let result = (
true,
axis_idx as u32,
axis_value / axis_value.saturating_abs(),
);
if result != last_axis_result {
if result != last_joystick_axis_result {
new_joystick_axis[*value] = result;
last_axis_result = result;
last_joystick_axis_result = result;
key_set = true
}
}
@@ -441,19 +548,20 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String) {
let new_profile = ui::config::InputProfile {
keys: new_keys,
controller_buttons: Default::default(),
controller_axis: Default::default(),
controller_buttons: new_controller_buttons,
controller_axis: new_controller_axis,
joystick_buttons: new_joystick_buttons,
joystick_hat: new_joystick_hat,
joystick_axis: new_joystick_axis,
dinput,
};
ui.config.input.input_profiles.insert(profile, new_profile);
}
pub fn get_default_profile() -> ui::config::InputProfile {
let mut default_controller_buttons = [(false, 0); 14];
let mut default_controller_axis = [(false, 0, 0); 18];
let mut default_keys = [(false, 0); 18];
let mut default_controller_buttons = [(false, 0); PROFILE_SIZE];
let mut default_controller_axis = [(false, 0, 0); PROFILE_SIZE];
let mut default_keys = [(false, 0); PROFILE_SIZE];
default_keys[R_DPAD] = (true, sdl2::keyboard::Scancode::D as i32);
default_keys[L_DPAD] = (true, sdl2::keyboard::Scancode::A as i32);
default_keys[D_DPAD] = (true, sdl2::keyboard::Scancode::S as i32);
@@ -472,6 +580,7 @@ pub fn get_default_profile() -> ui::config::InputProfile {
default_keys[AXIS_RIGHT] = (true, sdl2::keyboard::Scancode::Right as i32);
default_keys[AXIS_UP] = (true, sdl2::keyboard::Scancode::Up as i32);
default_keys[AXIS_DOWN] = (true, sdl2::keyboard::Scancode::Down as i32);
default_keys[CHANGE_PAK] = (true, sdl2::keyboard::Scancode::Comma as i32);
default_controller_buttons[R_DPAD] = (true, sdl2::controller::Button::DPadRight as i32);
default_controller_buttons[L_DPAD] = (true, sdl2::controller::Button::DPadLeft as i32);
@@ -491,6 +600,7 @@ pub fn get_default_profile() -> ui::config::InputProfile {
default_controller_axis[AXIS_RIGHT] = (true, sdl2::controller::Axis::LeftX as i32, 1);
default_controller_axis[AXIS_UP] = (true, sdl2::controller::Axis::LeftY as i32, -1);
default_controller_axis[AXIS_DOWN] = (true, sdl2::controller::Axis::LeftY as i32, 1);
default_controller_buttons[CHANGE_PAK] = (true, sdl2::controller::Button::Back as i32);
ui::config::InputProfile {
keys: default_keys,
@@ -499,11 +609,13 @@ pub fn get_default_profile() -> ui::config::InputProfile {
joystick_buttons: Default::default(),
joystick_hat: Default::default(),
joystick_axis: Default::default(),
dinput: false,
}
}
pub fn init(ui: &mut ui::Ui) {
let joystick_subsystem = ui.joystick_subsystem.as_ref().unwrap();
let controller_subsystem = ui.controller_subsystem.as_ref().unwrap();
let mut taken = [false; 4];
for i in 0..4 {
let controller_assignment = &ui.config.input.controller_assignment[i];
@@ -520,26 +632,26 @@ pub fn init(ui: &mut ui::Ui) {
}
}
if joystick_index < u32::MAX {
if ui.config.input.input_profile_binding[i] == "default" {
let controller_result = ui
.sdl_context
.as_ref()
.unwrap()
.game_controller()
.unwrap()
.open(joystick_index);
let profile_name = ui.config.input.input_profile_binding[i].clone();
let profile = ui.config.input.input_profiles.get(&profile_name).unwrap();
if !profile.dinput {
let controller_result = controller_subsystem.open(joystick_index);
if controller_result.is_ok() {
ui.controllers[i].game_controller = Some(controller_result.unwrap());
if ui.controllers[i]
.game_controller
.as_ref()
.unwrap()
.has_rumble()
{
ui.controllers[i].rumble = true;
}
}
}
if ui.controllers[i].game_controller.is_none() {
let joystick_result = ui
.sdl_context
.as_ref()
.unwrap()
.joystick()
.unwrap()
.open(joystick_index);
let joystick_result = joystick_subsystem.open(joystick_index);
if joystick_result.is_err() {
println!(
"could not connect joystick: {}",
@@ -547,6 +659,9 @@ pub fn init(ui: &mut ui::Ui) {
)
} else {
ui.controllers[i].joystick = Some(joystick_result.unwrap());
if ui.controllers[i].joystick.as_ref().unwrap().has_rumble() {
ui.controllers[i].rumble = true;
}
}
}
} else {
+3 -2
View File
@@ -36,10 +36,11 @@ pub fn init(device: &mut device::Device, fullscreen: bool) {
unsafe {
rdp_init(
device.ui.window.as_mut().unwrap().raw() as *mut std::ffi::c_void,
device.ui.window.as_ref().unwrap().raw() as *mut std::ffi::c_void,
gfx_info,
fullscreen,
device.ui.config.video.upscale,
device.ui.config.video.integer_scaling,
fullscreen,
)
}
}