├── .cargo └── config.toml ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── builtins └── cache.c ├── font ├── OFL.txt └── OpenSans-Regular.ttf ├── macros ├── Cargo.toml └── src │ └── lib.rs ├── rust-toolchain.toml ├── rustfmt.toml ├── src ├── bitset.rs ├── cartridge_io.rs ├── cartridge_metadata.rs ├── core │ ├── cp15.rs │ ├── cpu_regs.rs │ ├── cycle_manager.rs │ ├── div_sqrt.rs │ ├── emu.rs │ ├── exception_handler.rs │ ├── graphics │ │ ├── gl_glyph.rs │ │ ├── gl_utils.rs │ │ ├── gpu.rs │ │ ├── gpu_2d │ │ │ ├── mod.rs │ │ │ ├── registers_2d.rs │ │ │ ├── renderer_2d.rs │ │ │ ├── renderer_soft_2d.rs │ │ │ └── shaders │ │ │ │ ├── cg │ │ │ │ ├── bg_frag_affine_extended.cg │ │ │ │ ├── bg_frag_bitmap.cg │ │ │ │ ├── bg_frag_common.cg │ │ │ │ ├── bg_frag_display_3d.cg │ │ │ │ ├── bg_frag_text_4bpp.cg │ │ │ │ ├── bg_frag_text_8bpp.cg │ │ │ │ ├── bg_vert.cg │ │ │ │ ├── bg_vert_affine_extended.cg │ │ │ │ ├── bg_vert_bitmap.cg │ │ │ │ ├── blend_frag.cg │ │ │ │ ├── blend_vert.cg │ │ │ │ ├── obj_frag.cg │ │ │ │ ├── obj_vert.cg │ │ │ │ ├── rotate_frag.cg │ │ │ │ ├── rotate_vert.cg │ │ │ │ ├── vram_display_frag.cg │ │ │ │ ├── vram_display_vert.cg │ │ │ │ ├── win_bg_frag.cg │ │ │ │ └── win_bg_vert.cg │ │ │ │ └── glsl │ │ │ │ ├── bg_frag_affine_extended.glsl │ │ │ │ ├── bg_frag_bitmap.glsl │ │ │ │ ├── bg_frag_common.glsl │ │ │ │ ├── bg_frag_display_3d.glsl │ │ │ │ ├── bg_frag_text_4bpp.glsl │ │ │ │ ├── bg_frag_text_8bpp.glsl │ │ │ │ ├── bg_vert.glsl │ │ │ │ ├── bg_vert_affine_extended.glsl │ │ │ │ ├── bg_vert_bitmap.glsl │ │ │ │ ├── blend_frag.glsl │ │ │ │ ├── blend_vert.glsl │ │ │ │ ├── obj_frag.glsl │ │ │ │ ├── obj_vert.glsl │ │ │ │ ├── rotate_frag.glsl │ │ │ │ ├── rotate_vert.glsl │ │ │ │ ├── vram_display_frag.glsl │ │ │ │ ├── vram_display_vert.glsl │ │ │ │ ├── win_bg_frag.glsl │ │ │ │ └── win_bg_vert.glsl │ │ ├── gpu_3d │ │ │ ├── mod.rs │ │ │ ├── registers_3d.rs │ │ │ ├── renderer_3d.rs │ │ │ └── shaders │ │ │ │ ├── cg │ │ │ │ ├── render_frag.cg │ │ │ │ └── render_vert.cg │ │ │ │ └── glsl │ │ │ │ ├── render_frag.glsl │ │ │ │ └── render_vert.glsl │ │ ├── gpu_mem_buf.rs │ │ ├── gpu_renderer.rs │ │ ├── mod.rs │ │ └── shaders │ │ │ ├── cg │ │ │ ├── text_frag.cg │ │ │ └── text_vert.cg │ │ │ └── glsl │ │ │ ├── text_frag.glsl │ │ │ └── text_vert.glsl │ ├── hle │ │ ├── arm7_hle.rs │ │ ├── bios.rs │ │ ├── bios_lookup_table.rs │ │ ├── cart_hle.rs │ │ ├── firmware_hle.rs │ │ ├── mic_hle.rs │ │ ├── mod.rs │ │ ├── power_manager_hle.rs │ │ ├── rtc_hle.rs │ │ ├── sound_hle.rs │ │ ├── sound_nitro.rs │ │ ├── touchscreen_hle.rs │ │ └── wifi_hle.rs │ ├── input.rs │ ├── ipc.rs │ ├── memory │ │ ├── cartridge.rs │ │ ├── dma.rs │ │ ├── io_arm7.rs │ │ ├── io_arm7_lut.rs │ │ ├── io_arm9.rs │ │ ├── io_arm9_lut.rs │ │ ├── main.rs │ │ ├── mem.rs │ │ ├── mmu.rs │ │ ├── mod.rs │ │ ├── oam.rs │ │ ├── palettes.rs │ │ ├── regions.rs │ │ ├── vram.rs │ │ ├── wifi.rs │ │ └── wram.rs │ ├── mod.rs │ ├── rtc.rs │ ├── spi.rs │ ├── spu.rs │ ├── thread_regs.rs │ ├── timers.rs │ └── wifi.rs ├── fixed_fifo.rs ├── jit │ ├── assembler │ │ ├── arm │ │ │ ├── alu_assembler.rs │ │ │ ├── branch_assembler.rs │ │ │ ├── mod.rs │ │ │ └── transfer_assembler.rs │ │ ├── basic_block.rs │ │ ├── block_asm.rs │ │ ├── block_inst.rs │ │ ├── block_reg_allocator.rs │ │ ├── block_reg_set.rs │ │ └── mod.rs │ ├── disassembler │ │ ├── alu_instructions.rs │ │ ├── branch_instructions.rs │ │ ├── delegations.rs │ │ ├── lookup_table.rs │ │ ├── mod.rs │ │ ├── thumb │ │ │ ├── alu_instructions_thumb.rs │ │ │ ├── branch_instructions_thumb.rs │ │ │ ├── delegations_thumb.rs │ │ │ ├── lookup_table_thumb.rs │ │ │ ├── mod.rs │ │ │ └── transfer_instructions_thumb.rs │ │ └── transfer_instructions.rs │ ├── emitter │ │ ├── emit.rs │ │ ├── emit_branch.rs │ │ ├── emit_cp15.rs │ │ ├── emit_psr.rs │ │ ├── emit_swi.rs │ │ ├── emit_transfer.rs │ │ ├── mod.rs │ │ └── thumb │ │ │ ├── emit_alu_thumb.rs │ │ │ ├── emit_branch_thumb.rs │ │ │ ├── emit_thumb.rs │ │ │ ├── emit_transfer_thumb.rs │ │ │ └── mod.rs │ ├── inst_branch_handler.rs │ ├── inst_cp15_handler.rs │ ├── inst_cpu_regs_handler.rs │ ├── inst_exception_handler.rs │ ├── inst_info.rs │ ├── inst_info_thumb.rs │ ├── inst_mem_handler.rs │ ├── inst_thread_regs_handler.rs │ ├── jit_asm.rs │ ├── jit_asm_common_funs.rs │ ├── jit_memory.rs │ ├── jit_memory_map.rs │ ├── mod.rs │ ├── op.rs │ └── reg.rs ├── linked_list.rs ├── logging.rs ├── main.rs ├── math.rs ├── mmap │ ├── linux.rs │ ├── mod.rs │ └── vita.rs ├── presenter │ ├── linux.rs │ ├── mod.rs │ └── vita.rs ├── settings.rs └── utils.rs └── static └── sce_sys ├── icon0.png └── livearea └── contents ├── bg.png ├── startup.png └── template.xml /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.armv7-unknown-linux-gnueabihf] 2 | linker = "/usr/bin/clang" 3 | rustflags = ["-C", "link-args=-target armv7-unknown-linux-gnueabihf -fuse-ld=lld", "-C", "target-feature=+neon,+thumb-mode,+thumb2,+v5te,+v6,+v6k,+v6t2,+v7,+vfp2,+vfp3", "-Clink-arg=-Wl,--no-rosegment"] 4 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "*" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | submodules: recursive 21 | 22 | - name: Checkout vitasdk 23 | uses: actions/checkout@v4 24 | with: 25 | repository: vitasdk/vdpm 26 | path: vdpm 27 | 28 | - name: Install vitasdk 29 | run: | 30 | cd vdpm 31 | ./bootstrap-vitasdk.sh 32 | ./install-all.sh 33 | env: 34 | VITASDK: /usr/local/vitasdk 35 | 36 | - name: Setup rust 37 | run: | 38 | rustup toolchain install nightly 39 | rustup override set nightly 40 | rustup component add rust-src --toolchain nightly 41 | 42 | - name: Install cargo-vita 43 | run: cargo +nightly install cargo-vita 44 | env: 45 | VITASDK: /usr/local/vitasdk 46 | 47 | - name: Build release 48 | run: cargo vita build vpk --release 49 | env: 50 | VITASDK: /usr/local/vitasdk 51 | RUSTFLAGS: -Zlocation-detail=none -Zfmt-debug=none 52 | 53 | - name: Upload vpk 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: dsvita-vpk 57 | path: target/armv7-sony-vita-newlibeabihf/release/dsvita.vpk 58 | 59 | - name: Upload elf 60 | uses: actions/upload-artifact@v4 61 | with: 62 | name: dsvita-elf 63 | path: target/armv7-sony-vita-newlibeabihf/release/dsvita.elf 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.idea 3 | /.vscode 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vitaGL"] 2 | path = vitaGL 3 | url = https://github.com/Rinnegatamante/vitaGL.git 4 | [submodule "kubridge"] 5 | path = kubridge 6 | url = https://github.com/bythos14/kubridge.git 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | workspace = { members = ["macros"] } 2 | [package] 3 | name = "dsvita" 4 | version = "0.6.1" 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [package.metadata.vita] 10 | title_id = "DSVITA000" 11 | title_name = "DSVita" 12 | build_std = "std,panic_abort" 13 | assets = "./static" 14 | 15 | [dependencies] 16 | bilge = { git = "https://github.com/Grarak/bilge.git" } 17 | chrono = "0.4.38" 18 | dsvita_macros = { path = "macros" } 19 | gl = { git = "https://github.com/bjz/gl-rs" } 20 | glyph_brush = "0.7.9" 21 | rust-ini = "0.21.1" 22 | static_assertions = "1.1.0" 23 | paste = "1.0.15" 24 | enum_dispatch = "0.3.13" 25 | bit-set = "0.8.0" 26 | strum = "0.27.1" 27 | strum_macros = "0.27.1" 28 | 29 | [target.armv7-unknown-linux-gnueabihf.dependencies] 30 | clap = { version = "4.5.17", features = ["cargo"] } 31 | libc = "0.2.153" 32 | sdl2 = "0.35.0" 33 | affinity = "0.1.2" 34 | backtrace = "0.3.74" 35 | 36 | [build-dependencies] 37 | bindgen = "0.70.0" 38 | cc = "1.0.100" 39 | cmake = "0.1" 40 | 41 | [target.armv7-sony-vita-newlibeabihf.dependencies] 42 | vitasdk-sys = { version = "0.3.2", features = [ 43 | "SceAppMgr_stub", 44 | "SceAppUtil_stub", 45 | "SceAudio_stub", 46 | "SceCommonDialog_stub", 47 | "SceCtrl_stub", 48 | "SceDisplay_stub", 49 | "SceGxm_stub", 50 | "SceKernelThreadMgr_stub", 51 | "SceLibKernel_stub", 52 | "SceSysmem_stub", 53 | "SceTouch_stub", 54 | "ScePgf_stub", 55 | "SceKernelDmacMgr_stub", 56 | "SceShaccCg_stub", 57 | "ScePower_stub", 58 | "SceSysrootForKernel_stub" 59 | # "SceRazorCapture_stub", 60 | ] } 61 | 62 | [profile.release-debug] 63 | inherits = "dev" 64 | opt-level = 3 65 | overflow-checks = true 66 | debug-assertions = true 67 | debug = true 68 | 69 | [profile.release.package."*"] 70 | opt-level = 3 71 | 72 | [profile.release] 73 | panic = "abort" 74 | lto = true 75 | opt-level = 3 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DSVita 2 | 3 | [![Rust](https://github.com/Grarak/DSVita/actions/workflows/rust.yml/badge.svg)](https://github.com/Grarak/DSVita/actions/workflows/rust.yml) 4 | 5 | Fast NDS Emulator for ARM32/PSVita 6 | 7 | ## Status 8 | 9 | [![DSVita Mario Kart](http://img.youtube.com/vi/en2EX8GLauk/0.jpg)](https://www.youtube.com/watch?v=en2EX8GLauk "DSVita Mario Kart") 10 | 11 | This runs most games, however consider: 12 | 13 | - 3D rendering 14 | - Polygons and their textures are drawn, however no lighting, any other shading (e.g. toon) nor shadow volumes are implemented 15 | - Games which swap screens every frame for displaying 3D on both screens at the same time, will flicker heavily 16 | - 2D rendering is mostly complete 17 | - Mosaic and some window objects (you will see black screens or silhouettes) are not implemented 18 | - ARM7 HLE will not work with most games 19 | - Disable it if certain games don't boot further, get struck, crash or have any issues 20 | - There are other emulation modes like PartialHle or PartialSoundHle. You can pick them if full HLE breaks anything 21 | - Auto frameskip is always used 22 | - Games will feel choppy, you will most likely hover around 15 fps, even if they run at full game speed 23 | - No scanline rendering, thus games that update VRAM mid frame will not render correctly 24 | - Not many games do this, however games that do use it for scrolling texts 25 | 26 | ## Installation/Setup 27 | 28 | - Grab the latest vpk from [releases](https://github.com/Grarak/DSVita/releases) 29 | - Install `libshacccg.suprx`, follow this [guide](https://cimmerian.gitbook.io/vita-troubleshooting-guide/shader-compiler/extract-libshacccg.suprx) 30 | - Install `kubridge.skprx` from https://github.com/bythos14/kubridge/releases 31 | - It's strongly recommend to overclock your vita to 500MHz 32 | - Create the folder ux0:data/dsvita and put your roms there 33 | - They must have the file extensions `*.nds` 34 | 35 | ## Building 36 | 1. Install [Vitasdk](https://vitasdk.org/) 37 | 2. Install [cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) 38 | 3. Install [cargo vita](https://github.com/vita-rust/cargo-vita) 39 | 4. `RUSTFLAGS="-Zlocation-detail=none -Zfmt-debug=none" cargo vita build vpk -- --release` 40 | 41 | ## Credits 42 | 43 | - [NooDS](https://github.com/Hydr8gon/NooDS) was used as reference. A lot of code was taken from there. 44 | - [melonDS](https://github.com/melonDS-emu/melonDS) for ARM7 HLE implementation and jit optimizations. 45 | - [DesmumePSPExperimental](https://github.com/Xiro28/DesmumePSPExperimental) for ARM7 HLE implementation. 46 | - [pokediamond](https://github.com/pret/pokediamond) for ARM7 HLE implementation. 47 | - [DSHBA](https://github.com/DenSinH/DSHBA) Copied some PPU hardware acceleration implementation (Thanks for xiro28 linking me the repo) 48 | - [vitaGL](https://github.com/Rinnegatamante/vitaGL) 2D/3D hardware acceleration wouldn't be possible without it 49 | - [Tonc](https://www.coranac.com/tonc/text/toc.htm) GBA PPU documentation 50 | - [GBATEK](http://problemkaputt.de/gbatek-index.htm) GBA/NDS documentation 51 | - [kubridge](https://github.com/bythos14/kubridge) For fastmem implementation 52 | - @TheIronUniverse for livearea assets 53 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use bindgen::Formatter; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | use std::process::Command; 6 | use std::{env, fs}; 7 | 8 | fn main() { 9 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 10 | println!("cargo:rerun-if-env-changed=OUT_DIR"); 11 | 12 | let build_profile_name = out_path.to_str().unwrap().split(std::path::MAIN_SEPARATOR).nth_back(3).unwrap(); 13 | let build_profile_name_file = out_path.join("build_profile_name"); 14 | File::create(build_profile_name_file).unwrap().write_all(build_profile_name.as_bytes()).unwrap(); 15 | 16 | let target = env::var("TARGET").unwrap(); 17 | if target != "armv7-sony-vita-newlibeabihf" { 18 | println!("cargo:rerun-if-env-changed=SYSROOT"); 19 | if let Ok(sysroot) = env::var("SYSROOT") { 20 | println!("cargo:rustc-link-arg=--sysroot={sysroot}"); 21 | } 22 | let mut cache_build = cc::Build::new(); 23 | cache_build.compiler("clang"); 24 | // Running IDE on anything other than linux will fail, so ignore compile error 25 | let _ = cache_build.file("builtins/cache.c").try_compile("cache").ok(); 26 | } 27 | 28 | let num_jobs = env::var("NUM_JOBS").unwrap(); 29 | 30 | let vitasdk_path = env::var("VITASDK").map(PathBuf::from); 31 | println!("cargo:rerun-if-env-changed=VITASDK"); 32 | if vitasdk_path.is_err() { 33 | return; 34 | } 35 | let vitasdk_path = vitasdk_path.unwrap(); 36 | let vitasdk_sysroot = vitasdk_path.join("arm-vita-eabi"); 37 | let vitasdk_include_path = vitasdk_sysroot.join("include"); 38 | let vitasdk_lib_path = vitasdk_sysroot.join("lib"); 39 | 40 | let kubridge_path = PathBuf::from("kubridge"); 41 | 42 | { 43 | let bindings_file = out_path.join("imgui_bindings.rs"); 44 | 45 | const IMGUI_HEADERS: [&str; 3] = ["imgui.h", "imgui_internal.h", "imgui_impl_vitagl.h"]; 46 | let mut bindings = bindgen::Builder::default() 47 | .clang_args(["-x", "c++"]) 48 | .clang_args(["-std=c++17"]) 49 | .clang_args(["-target", "armv7-unknown-linux-gnueabihf"]) 50 | .clang_args(["--sysroot", vitasdk_sysroot.to_str().unwrap()]) 51 | .formatter(Formatter::Prettyplease); 52 | for header in IMGUI_HEADERS { 53 | let header_path = vitasdk_include_path.join(header); 54 | println!("cargo:rerun-if-changed={header_path:?}"); 55 | bindings = bindings.header(header_path.to_str().unwrap()); 56 | } 57 | bindings.rust_target(bindgen::RustTarget::Nightly).generate().unwrap().write_to_file(bindings_file).unwrap(); 58 | 59 | println!("cargo:rustc-link-search=native={vitasdk_lib_path:?}"); 60 | if target == "armv7-sony-vita-newlibeabihf" { 61 | println!("cargo:rustc-link-lib=static=imgui"); 62 | } 63 | } 64 | 65 | { 66 | let bindings_file = out_path.join("kubridge_bindings.rs"); 67 | 68 | const KUBRIDGE_HEADERS: [&str; 1] = ["kubridge.h"]; 69 | let mut bindings = bindgen::Builder::default() 70 | .clang_args(["-I", kubridge_path.to_str().unwrap()]) 71 | .clang_args(["-x", "c++"]) 72 | .clang_args(["-std=c++17"]) 73 | .clang_args(["-target", "armv7-unknown-linux-gnueabihf"]) 74 | .clang_args(["--sysroot", vitasdk_sysroot.to_str().unwrap()]) 75 | .formatter(Formatter::Prettyplease); 76 | for header in KUBRIDGE_HEADERS { 77 | let header_path = kubridge_path.join(header); 78 | println!("cargo:rerun-if-changed={header_path:?}"); 79 | bindings = bindings.header(header_path.to_str().unwrap()); 80 | } 81 | bindings.rust_target(bindgen::RustTarget::Nightly).generate().unwrap().write_to_file(bindings_file).unwrap(); 82 | } 83 | 84 | if target != "armv7-sony-vita-newlibeabihf" { 85 | return; 86 | } 87 | 88 | { 89 | let vita_gl_path = PathBuf::from("vitaGL"); 90 | let vita_gl_lib_path = vita_gl_path.join("libvitaGL.a"); 91 | let vita_gl_lib_new_path = vita_gl_path.join("libvitaGL_dsvita.a"); 92 | 93 | Command::new("make") 94 | .current_dir("vitaGL") 95 | .args(["-j", &num_jobs]) 96 | .envs([ 97 | ("HAVE_UNFLIPPED_FBOS", "1"), 98 | ("NO_TEX_COMBINER", "1"), 99 | ("NO_DEBUG", "1"), 100 | ("SHADER_COMPILER_SPEEDHACK", "1"), 101 | ("MATH_SPEEDHACK", "1"), 102 | ("HAVE_SHADER_CACHE", "1"), 103 | // ("HAVE_SHARK_LOG", "1"), 104 | // ("LOG_ERRORS", "1"), 105 | // ("HAVE_RAZOR", "1"), 106 | ]) 107 | .status() 108 | .unwrap(); 109 | 110 | fs::rename(vita_gl_lib_path, vita_gl_lib_new_path).unwrap(); 111 | println!("cargo:rustc-link-search=native={}", fs::canonicalize(vita_gl_path).unwrap().to_str().unwrap()); 112 | println!("cargo:rustc-link-lib=static=vitaGL_dsvita"); 113 | } 114 | 115 | { 116 | let kubridge_dst_path = cmake::Config::new(&kubridge_path).build_target("libkubridge_stub.a").build().join("build"); 117 | let kubridge_lib_path = kubridge_dst_path.join("libkubridge_stub.a"); 118 | let kubridge_lib_new_path = kubridge_dst_path.join("libkubridge_stub_dsvita.a"); 119 | fs::rename(kubridge_lib_path, kubridge_lib_new_path).unwrap(); 120 | 121 | println!("cargo:rustc-link-search=native={}", fs::canonicalize(kubridge_dst_path).unwrap().to_str().unwrap()); 122 | println!("cargo:rustc-link-lib=static=kubridge_stub_dsvita"); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /builtins/cache.c: -------------------------------------------------------------------------------- 1 | void built_in_clear_cache(void *start, void *end) { 2 | __builtin___clear_cache(start, end); 3 | } 4 | -------------------------------------------------------------------------------- /font/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | https://openfontlicense.org 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /font/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grarak/DSVita/e9f965806f9cd01f2c780d3db79b25bc464be547/font/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsvita_macros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | syn = { version = "2.0.58", features = ["extra-traits", "full"] } 13 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 200 2 | -------------------------------------------------------------------------------- /src/bitset.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, AddAssign, BitAnd, BitXor, BitXorAssign, Not, Sub, SubAssign}; 2 | 3 | #[derive(Copy, Clone, Eq, PartialEq)] 4 | pub struct Bitset(pub [u32; SIZE]); 5 | 6 | impl Bitset { 7 | pub const fn new() -> Self { 8 | Bitset([0; SIZE]) 9 | } 10 | 11 | fn _add(&mut self, bit: impl Into) { 12 | let bit = bit.into(); 13 | let array_index = bit >> 5; 14 | let pos_index = bit & 31; 15 | unsafe { *self.0.get_unchecked_mut(array_index) |= 1 << pos_index }; 16 | } 17 | 18 | fn _sub(&mut self, bit: impl Into) { 19 | let bit = bit.into(); 20 | let array_index = bit >> 5; 21 | let pos_index = bit & 31; 22 | unsafe { *self.0.get_unchecked_mut(array_index) &= !(1 << pos_index) }; 23 | } 24 | 25 | pub fn contains(&self, bit: impl Into) -> bool { 26 | let bit = bit.into(); 27 | let array_index = bit >> 5; 28 | let pos_index = bit & 31; 29 | unsafe { *self.0.get_unchecked(array_index) & (1 << pos_index) != 0 } 30 | } 31 | 32 | pub const fn is_empty(&self) -> bool { 33 | let mut i = 0; 34 | let mut sum = 0; 35 | while i < self.0.len() { 36 | sum |= self.0[i]; 37 | i += 1; 38 | } 39 | sum == 0 40 | } 41 | 42 | pub const fn clear(&mut self) { 43 | self.0 = [0; SIZE]; 44 | } 45 | } 46 | 47 | impl Default for Bitset { 48 | fn default() -> Self { 49 | Bitset([0; SIZE]) 50 | } 51 | } 52 | 53 | impl> Add for Bitset { 54 | type Output = Bitset; 55 | 56 | fn add(mut self, rhs: T) -> Self::Output { 57 | self._add(rhs); 58 | self 59 | } 60 | } 61 | 62 | impl> AddAssign for Bitset { 63 | fn add_assign(&mut self, rhs: T) { 64 | self._add(rhs) 65 | } 66 | } 67 | 68 | impl> Sub for Bitset { 69 | type Output = Bitset; 70 | 71 | fn sub(mut self, rhs: T) -> Self::Output { 72 | self._sub(rhs); 73 | self 74 | } 75 | } 76 | 77 | impl> SubAssign for Bitset { 78 | fn sub_assign(&mut self, rhs: T) { 79 | self._sub(rhs); 80 | } 81 | } 82 | 83 | impl Add> for Bitset { 84 | type Output = Bitset; 85 | 86 | fn add(mut self, rhs: Bitset) -> Self::Output { 87 | for i in 0..self.0.len() { 88 | self.0[i] |= rhs.0[i]; 89 | } 90 | self 91 | } 92 | } 93 | 94 | impl AddAssign> for Bitset { 95 | fn add_assign(&mut self, rhs: Bitset) { 96 | for i in 0..self.0.len() { 97 | self.0[i] |= rhs.0[i]; 98 | } 99 | } 100 | } 101 | 102 | impl Sub> for Bitset { 103 | type Output = Bitset; 104 | 105 | fn sub(mut self, rhs: Bitset) -> Self::Output { 106 | for i in 0..self.0.len() { 107 | self.0[i] &= !rhs.0[i]; 108 | } 109 | self 110 | } 111 | } 112 | 113 | impl SubAssign> for Bitset { 114 | fn sub_assign(&mut self, rhs: Bitset) { 115 | for i in 0..self.0.len() { 116 | self.0[i] &= !rhs.0[i]; 117 | } 118 | } 119 | } 120 | 121 | impl BitAnd> for Bitset { 122 | type Output = Bitset; 123 | 124 | fn bitand(mut self, rhs: Bitset) -> Self::Output { 125 | for i in 0..self.0.len() { 126 | self.0[i] &= rhs.0[i]; 127 | } 128 | self 129 | } 130 | } 131 | 132 | impl BitXor> for Bitset { 133 | type Output = Bitset; 134 | 135 | fn bitxor(mut self, rhs: Bitset) -> Self::Output { 136 | for i in 0..self.0.len() { 137 | self.0[i] ^= rhs.0[i]; 138 | } 139 | self 140 | } 141 | } 142 | 143 | impl BitXorAssign for Bitset { 144 | fn bitxor_assign(&mut self, rhs: Self) { 145 | for i in 0..self.0.len() { 146 | self.0[i] ^= rhs.0[i]; 147 | } 148 | } 149 | } 150 | 151 | impl Not for Bitset { 152 | type Output = Bitset; 153 | 154 | fn not(mut self) -> Self::Output { 155 | for i in 0..self.0.len() { 156 | self.0[i] = !self.0[i]; 157 | } 158 | self 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/core/cp15.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::CpuType::ARM9; 3 | use crate::logging::debug_println; 4 | use bilge::prelude::*; 5 | use std::{cmp, mem}; 6 | 7 | #[bitsize(32)] 8 | #[derive(FromBits)] 9 | struct Cp15ControlReg { 10 | mmu_pu_enable: u1, 11 | alignment_fault_check: u1, 12 | data_unified_cache: u1, 13 | write_buffer: u1, 14 | exception_handling: u1, 15 | address26bit_faults: u1, 16 | abort_model: u1, 17 | big_endian: u1, 18 | system_protection_bit: u1, 19 | rom_protection_bit: u1, 20 | implementation_defined: u1, 21 | branch_prediction: u1, 22 | instruction_cache: u1, 23 | exception_vectors: u1, 24 | cache_replacement: u1, 25 | pre_armv5_mode: u1, 26 | dtcm_enable: u1, 27 | dtcm_load_mode: u1, 28 | itcm_enable: u1, 29 | itcm_load_mode: u1, 30 | reserved: u2, 31 | unaligned_access: u1, 32 | extended_page_table: u1, 33 | reserved1: u1, 34 | cpsr_e_on_exceptions: u1, 35 | reserved2: u1, 36 | fiq_behaviour: u1, 37 | tex_remap_bit: u1, 38 | force_ap: u1, 39 | reserved3: u2, 40 | } 41 | 42 | #[bitsize(32)] 43 | #[derive(FromBits)] 44 | struct TcmReg { 45 | reserved: u1, 46 | virtual_size: u5, 47 | reserved1: u6, 48 | region_base: u20, 49 | } 50 | 51 | const CONTROL_RW_BITS_MASK: u32 = 0x000FF085; 52 | const TCM_MIN_SIZE: u32 = 4 * 1024; 53 | 54 | pub struct Cp15 { 55 | control: u32, 56 | pub exception_addr: u32, 57 | dtcm: u32, 58 | pub dtcm_state: TcmState, 59 | pub dtcm_addr: u32, 60 | pub dtcm_size: u32, 61 | itcm: u32, 62 | pub itcm_state: TcmState, 63 | pub itcm_size: u32, 64 | proc_id: u32, 65 | } 66 | 67 | #[derive(Eq, PartialEq)] 68 | #[repr(u8)] 69 | pub enum TcmState { 70 | Disabled = 0, 71 | RW = 1, 72 | W = 2, 73 | } 74 | 75 | impl From for TcmState { 76 | fn from(value: u8) -> Self { 77 | debug_assert!(value <= TcmState::W as u8); 78 | unsafe { mem::transmute(value) } 79 | } 80 | } 81 | 82 | impl Cp15 { 83 | pub fn new() -> Self { 84 | let mut control_default = Cp15ControlReg::from(0); 85 | control_default.set_write_buffer(u1::new(1)); 86 | control_default.set_exception_handling(u1::new(1)); 87 | control_default.set_address26bit_faults(u1::new(1)); 88 | control_default.set_abort_model(u1::new(1)); 89 | 90 | Cp15 { 91 | control: u32::from(control_default), 92 | exception_addr: 0, 93 | dtcm: 0, 94 | dtcm_state: TcmState::Disabled, 95 | dtcm_addr: 0, 96 | dtcm_size: 0, 97 | itcm: 0, 98 | itcm_state: TcmState::Disabled, 99 | itcm_size: 0, 100 | proc_id: 0, 101 | } 102 | } 103 | } 104 | 105 | impl Emu { 106 | fn cp15_set_control_reg(&mut self, value: u32) { 107 | self.cp15.control = (self.cp15.control & (!CONTROL_RW_BITS_MASK)) | (value & CONTROL_RW_BITS_MASK); 108 | let control_reg = Cp15ControlReg::from(self.cp15.control); 109 | 110 | self.cp15.exception_addr = if bool::from(control_reg.exception_vectors()) { 0xFFFF0000 } else { 0x00000000 }; 111 | self.cp15.dtcm_state = TcmState::from(u8::from(control_reg.dtcm_enable()) + u8::from(control_reg.dtcm_load_mode())); 112 | self.cp15.itcm_state = TcmState::from(u8::from(control_reg.itcm_enable()) + u8::from(control_reg.itcm_load_mode())); 113 | 114 | self.mmu_update_itcm::<{ ARM9 }>(); 115 | self.mmu_update_dtcm::<{ ARM9 }>(); 116 | } 117 | 118 | fn cp15_set_dtcm(&mut self, value: u32) { 119 | let tcm_reg = TcmReg::from(value); 120 | 121 | self.cp15.dtcm = value; 122 | self.cp15.dtcm_addr = u32::from(tcm_reg.region_base()) << 12; 123 | self.cp15.dtcm_size = cmp::max(512 << u8::from(tcm_reg.virtual_size()), TCM_MIN_SIZE); 124 | 125 | self.mmu_update_dtcm::<{ ARM9 }>(); 126 | 127 | debug_println!("{:?} Set dtcm to addr {:x} with size {:x}", ARM9, self.cp15.dtcm_addr, self.cp15.dtcm_size); 128 | } 129 | 130 | fn cp15_set_itcm(&mut self, value: u32) { 131 | let tcm_reg = TcmReg::from(value); 132 | 133 | self.cp15.itcm = value; 134 | self.cp15.itcm_size = cmp::max(512 << u8::from(tcm_reg.virtual_size()), TCM_MIN_SIZE); 135 | 136 | self.mmu_update_itcm::<{ ARM9 }>(); 137 | 138 | debug_println!("Set itcm with size {:x}", self.cp15.itcm_size); 139 | } 140 | 141 | pub fn cp15_write(&mut self, reg: u32, value: u32) { 142 | debug_println!("Writing to cp15 reg {:x} {:x}", reg, value); 143 | 144 | match reg { 145 | 0x010000 => self.cp15_set_control_reg(value), 146 | 0x090100 => self.cp15_set_dtcm(value), 147 | 0x090101 => self.cp15_set_itcm(value), 148 | 0x0D0001 | 0x0D0101 => self.cp15.proc_id = value, 149 | _ => debug_println!("Unknown cp15 reg write {:x}", reg), 150 | } 151 | } 152 | 153 | pub fn cp15_read(&self, reg: u32) -> u32 { 154 | debug_println!("Reading from cp15 reg {:x}", reg); 155 | 156 | match reg { 157 | 0x000000 => 0x41059461, // Main ID 158 | 0x000001 => 0x0F0D2112, // Cache type 159 | 0x010000 => self.cp15.control, 160 | 0x090100 => self.cp15.dtcm, 161 | 0x090101 => self.cp15.itcm, 162 | 0x0D0001 | 0x0D0101 => self.cp15.proc_id, 163 | _ => { 164 | debug_println!("Unknown cp15 reg read {:x}", reg); 165 | 0 166 | } 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/core/cpu_regs.rs: -------------------------------------------------------------------------------- 1 | use crate::core::cycle_manager::EventType; 2 | use crate::core::emu::Emu; 3 | use crate::core::exception_handler::ExceptionVector; 4 | use crate::core::thread_regs::Cpsr; 5 | use crate::core::CpuType::ARM7; 6 | use crate::core::{exception_handler, CpuType}; 7 | use crate::logging::debug_println; 8 | use std::fmt::{Debug, Formatter}; 9 | use std::mem; 10 | use CpuType::ARM9; 11 | 12 | #[repr(u8)] 13 | #[derive(Copy, Clone, Debug)] 14 | pub enum InterruptFlag { 15 | LcdVBlank = 0, 16 | LcdHBlank = 1, 17 | LcdVCounterMatch = 2, 18 | Timer0Overflow = 3, 19 | Timer1Overflow = 4, 20 | Timer2Overflow = 5, 21 | Timer3Overflow = 6, 22 | Rtc = 7, 23 | Dma0 = 8, 24 | Dma1 = 9, 25 | Dma2 = 10, 26 | Dma3 = 11, 27 | Keypad = 12, 28 | GbaSlot = 13, 29 | IpcSync = 16, 30 | IpcSendFifoEmpty = 17, 31 | IpcRecvFifoNotEmpty = 18, 32 | NdsSlotTransferCompletion = 19, 33 | NdsSlotIreqMc = 20, 34 | GeometryCmdFifo = 21, 35 | ScreensUnfolding = 22, 36 | SpiBus = 23, 37 | Wifi = 24, 38 | } 39 | 40 | impl From for InterruptFlag { 41 | fn from(value: u8) -> Self { 42 | debug_assert!(value <= InterruptFlag::Wifi as u8); 43 | unsafe { mem::transmute(value) } 44 | } 45 | } 46 | 47 | pub struct InterruptFlags(pub u32); 48 | 49 | impl Debug for InterruptFlags { 50 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 51 | let mut debug_set = f.debug_set(); 52 | for i in 0..=InterruptFlag::Wifi as u8 { 53 | if self.0 & (1 << i) != 0 { 54 | let flag = InterruptFlag::from(i); 55 | debug_set.entry(&flag); 56 | } 57 | } 58 | debug_set.finish() 59 | } 60 | } 61 | 62 | #[repr(C)] 63 | pub struct CpuRegs { 64 | pub ie: u32, 65 | pub irf: u32, 66 | pub ime: u8, 67 | pub post_flg: u8, 68 | pub halt_cnt: u8, 69 | halt: u8, 70 | pub bios_wait_flags: u32, 71 | } 72 | 73 | impl CpuRegs { 74 | pub fn new() -> Self { 75 | CpuRegs { 76 | ime: 0, 77 | ie: 0, 78 | irf: 0, 79 | post_flg: 0, 80 | halt_cnt: 0, 81 | halt: 0, 82 | bios_wait_flags: 0, 83 | } 84 | } 85 | } 86 | 87 | impl Emu { 88 | pub fn cpu_set_ime(&mut self, cpu: CpuType, value: u8) { 89 | self.cpu[cpu].ime = value & 0x1; 90 | self.cpu_check_for_interrupt(cpu); 91 | } 92 | 93 | pub fn cpu_set_ie(&mut self, cpu: CpuType, mut mask: u32, value: u32) { 94 | let CpuRegs { ie, .. } = &mut self.cpu[cpu]; 95 | mask &= match cpu { 96 | ARM9 => 0x003F3F7F, 97 | ARM7 => 0x01FF3FFF, 98 | }; 99 | *ie = (*ie & !mask) | (value & mask); 100 | debug_println!("{cpu:?} set ie {ie:x} {:?}", InterruptFlags(*ie)); 101 | self.cpu_check_for_interrupt(cpu); 102 | } 103 | 104 | pub fn cpu_check_for_interrupt(&mut self, cpu: CpuType) { 105 | let cpu_regs = &self.cpu[cpu]; 106 | if cpu_regs.ime != 0 && (cpu_regs.ie & cpu_regs.irf) != 0 && !Cpsr::from(self.thread[cpu].cpsr).irq_disable() { 107 | self.cpu_schedule_interrupt(cpu); 108 | } 109 | } 110 | 111 | fn cpu_schedule_interrupt(&mut self, cpu: CpuType) { 112 | self.cm.schedule_imm( 113 | match cpu { 114 | ARM9 => EventType::CpuInterruptArm9, 115 | ARM7 => EventType::CpuInterruptArm7, 116 | }, 117 | 0, 118 | ) 119 | } 120 | 121 | pub fn cpu_set_irf(&mut self, cpu: CpuType, mask: u32, value: u32) { 122 | // debug_println!("{:?} set irf {:?}", self.cpu_type, InterruptFlags(value & mask)); 123 | self.cpu[cpu].irf &= !(value & mask); 124 | } 125 | 126 | pub fn cpu_set_post_flg(&mut self, cpu: CpuType, value: u8) { 127 | let cpu_regs = &mut self.cpu[cpu]; 128 | cpu_regs.post_flg |= value & 0x1; 129 | if cpu == ARM9 { 130 | cpu_regs.post_flg = (cpu_regs.post_flg & !0x2) | (value & 0x2); 131 | } 132 | } 133 | 134 | pub fn cpu_halt(&mut self, cpu: CpuType, bit: u8) { 135 | debug_println!("{cpu:?} halt with bit {bit}"); 136 | self.cpu[cpu].halt |= 1 << bit; 137 | } 138 | 139 | pub fn cpu_unhalt(&mut self, cpu: CpuType, bit: u8) { 140 | debug_println!("{cpu:?} unhalt with bit {bit}"); 141 | self.cpu[cpu].halt &= !(1 << bit); 142 | } 143 | 144 | pub fn cpu_is_halted(&self, cpu: CpuType) -> bool { 145 | self.cpu[cpu].halt != 0 146 | } 147 | 148 | pub fn cpu_send_interrupt(&mut self, cpu: CpuType, flag: InterruptFlag) { 149 | let cpu_regs = &mut self.cpu[cpu]; 150 | cpu_regs.irf |= 1 << flag as u8; 151 | debug_println!( 152 | "{cpu:?} send interrupt {flag:?} {:?} {:?} {:x} {}", 153 | InterruptFlags(cpu_regs.ie), 154 | InterruptFlags(cpu_regs.irf), 155 | cpu_regs.ime, 156 | !Cpsr::from(self.thread[cpu].cpsr).irq_disable() 157 | ); 158 | if (cpu_regs.ie & cpu_regs.irf) != 0 { 159 | if cpu_regs.ime != 0 && !Cpsr::from(self.thread[cpu].cpsr).irq_disable() { 160 | debug_println!("{cpu:?} schedule send interrupt {flag:?}"); 161 | self.cpu_schedule_interrupt(cpu); 162 | } else if cpu == ARM7 || cpu_regs.ime != 0 { 163 | debug_println!("{cpu:?} unhalt send interrupt {flag:?}"); 164 | self.cpu_unhalt(cpu, 0); 165 | } 166 | } 167 | } 168 | 169 | pub fn cpu_set_halt_cnt(&mut self, cpu: CpuType, value: u8) { 170 | self.cpu[cpu].halt_cnt = value & 0xC0; 171 | 172 | match self.cpu[cpu].halt_cnt { 173 | 1 => { 174 | todo!("gba mode") 175 | } 176 | 2 => { 177 | todo!("halt") 178 | } 179 | _ => {} 180 | } 181 | } 182 | 183 | pub fn cpu_on_interrupt_event(&mut self, _: u16) { 184 | let cpu_regs = &self.cpu[CPU]; 185 | let interrupted = { 186 | let interrupt = cpu_regs.ime != 0 && (cpu_regs.ie & cpu_regs.irf) != 0 && !Cpsr::from(self.thread[CPU].cpsr).irq_disable(); 187 | if interrupt { 188 | debug_println!("{CPU:?} interrupt {:?}", InterruptFlags(cpu_regs.ie & cpu_regs.irf)); 189 | } 190 | interrupt 191 | }; 192 | if interrupted { 193 | exception_handler::handle::(self, 0, ExceptionVector::NormalInterrupt); 194 | self.cpu_unhalt(CPU, 0); 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/core/cycle_manager.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::CpuType::{ARM7, ARM9}; 3 | use crate::linked_list::{LinkedList, LinkedListAllocator, LinkedListEntry}; 4 | use bilge::prelude::*; 5 | use std::alloc::{GlobalAlloc, Layout, System}; 6 | use std::cmp::max; 7 | use std::intrinsics::unlikely; 8 | use std::{mem, ptr}; 9 | 10 | #[bitsize(16)] 11 | #[derive(FromBits)] 12 | pub struct EventTypeEntry { 13 | event_type: u4, 14 | arg: u12, 15 | } 16 | 17 | impl EventTypeEntry { 18 | fn create(event_type: EventType, arg: u16) -> Self { 19 | EventTypeEntry::new(u4::new(event_type as u8), u12::new(arg)) 20 | } 21 | } 22 | 23 | struct CycleEventEntry { 24 | event_type_entry: EventTypeEntry, 25 | cycle_count: u64, 26 | } 27 | 28 | impl CycleEventEntry { 29 | fn new(event_type: EventType, arg: u16, cycle_count: u64) -> Self { 30 | CycleEventEntry { 31 | event_type_entry: EventTypeEntry::create(event_type, arg), 32 | cycle_count, 33 | } 34 | } 35 | } 36 | 37 | #[derive(Default)] 38 | struct CycleEventsListAllocator(Vec<*mut LinkedListEntry>); 39 | 40 | impl LinkedListAllocator for CycleEventsListAllocator { 41 | fn allocate(&mut self, value: CycleEventEntry) -> *mut LinkedListEntry { 42 | let entry = if self.0.is_empty() { 43 | unsafe { System.alloc(Layout::new::>()) as *mut LinkedListEntry } 44 | } else { 45 | unsafe { self.0.pop().unwrap_unchecked() } 46 | }; 47 | unsafe { 48 | (*entry).value = value; 49 | (*entry).previous = ptr::null_mut(); 50 | (*entry).next = ptr::null_mut(); 51 | } 52 | entry 53 | } 54 | 55 | fn deallocate(&mut self, entry: *mut LinkedListEntry) { 56 | self.0.push(entry); 57 | } 58 | } 59 | 60 | #[repr(u8)] 61 | #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] 62 | pub enum EventType { 63 | CpuInterruptArm9 = 0, 64 | CpuInterruptArm7 = 1, 65 | GpuScanline256 = 2, 66 | GpuScanline355 = 3, 67 | SoundCmdHle = 4, 68 | SoundAlarmHle = 5, 69 | CartridgeWordReadArm9 = 6, 70 | CartridgeWordReadArm7 = 7, 71 | DmaArm9 = 8, 72 | DmaArm7 = 9, 73 | SpuSample = 10, 74 | TimerArm9 = 11, 75 | TimerArm7 = 12, 76 | WifiScanHle = 13, 77 | } 78 | 79 | pub struct CycleManager { 80 | cycle_count: u64, 81 | events: LinkedList, 82 | imm_events: Vec, 83 | imm_events_swap: Vec, 84 | } 85 | 86 | impl CycleManager { 87 | pub fn new() -> Self { 88 | CycleManager { 89 | cycle_count: 0, 90 | events: LinkedList::new(), 91 | imm_events: Vec::new(), 92 | imm_events_swap: Vec::new(), 93 | } 94 | } 95 | 96 | pub fn add_cycles(&mut self, cycle_count: u16) { 97 | self.cycle_count += cycle_count as u64; 98 | } 99 | 100 | pub fn get_cycles(&self) -> u64 { 101 | self.cycle_count 102 | } 103 | 104 | pub fn schedule_imm(&mut self, event_type: EventType, arg: u16) { 105 | self.imm_events.push(EventTypeEntry::create(event_type, arg)) 106 | } 107 | 108 | pub fn schedule(&mut self, in_cycles: u32, event_type: EventType, arg: u16) { 109 | let event_cycle = self.cycle_count + max(in_cycles, 1) as u64; 110 | 111 | let mut current_node = self.events.root; 112 | while !current_node.is_null() { 113 | let entry = LinkedList::<_, CycleEventsListAllocator>::deref(current_node); 114 | if entry.value.cycle_count > event_cycle { 115 | self.events.insert_entry_begin(current_node, CycleEventEntry::new(event_type, arg, event_cycle)); 116 | return; 117 | } 118 | current_node = entry.next; 119 | } 120 | self.events.insert_end(CycleEventEntry::new(event_type, arg, event_cycle)); 121 | } 122 | 123 | pub fn jump_to_next_event(&mut self) { 124 | self.cycle_count = LinkedList::<_, CycleEventsListAllocator>::deref(self.events.root).value.cycle_count 125 | } 126 | } 127 | 128 | impl Emu { 129 | pub fn cm_check_events(&mut self) -> bool { 130 | static LUT: [fn(&mut Emu, u16); EventType::WifiScanHle as usize + 1] = [ 131 | Emu::cpu_on_interrupt_event::<{ ARM9 }>, 132 | Emu::cpu_on_interrupt_event::<{ ARM7 }>, 133 | Emu::gpu_on_scanline256_event, 134 | Emu::gpu_on_scanline355_event, 135 | Emu::sound_nitro_on_cmd_event, 136 | Emu::sound_nitro_on_alarm_event, 137 | Emu::cartridge_on_word_read_event::<{ ARM9 }>, 138 | Emu::cartridge_on_word_read_event::<{ ARM7 }>, 139 | Emu::dma_on_event::<{ ARM9 }>, 140 | Emu::dma_on_event::<{ ARM7 }>, 141 | Emu::spu_on_sample_event, 142 | Emu::timers_on_overflow_event::<{ ARM9 }>, 143 | Emu::timers_on_overflow_event::<{ ARM7 }>, 144 | Emu::wifi_hle_on_scan_event, 145 | ]; 146 | 147 | self.cm.imm_events_swap.clear(); 148 | mem::swap(&mut self.cm.imm_events, &mut self.cm.imm_events_swap); 149 | for i in 0..self.cm.imm_events_swap.len() { 150 | let event_type_entry = &self.cm.imm_events_swap[i]; 151 | let func = unsafe { LUT.get_unchecked(u8::from(event_type_entry.event_type()) as usize) }; 152 | func(self, u16::from(event_type_entry.arg())); 153 | } 154 | 155 | let cycle_count = self.cm.cycle_count; 156 | let mut event_triggered = false; 157 | while { 158 | let entry = &LinkedList::<_, CycleEventsListAllocator>::deref(self.cm.events.root).value; 159 | unlikely(entry.cycle_count <= cycle_count) 160 | } { 161 | event_triggered = true; 162 | let entry = self.cm.events.remove_begin(); 163 | let func = unsafe { LUT.get_unchecked(u8::from(entry.event_type_entry.event_type()) as usize) }; 164 | func(self, u16::from(entry.event_type_entry.arg())); 165 | } 166 | event_triggered 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/core/emu.rs: -------------------------------------------------------------------------------- 1 | use crate::cartridge_io::CartridgeIo; 2 | use crate::core::cp15::Cp15; 3 | use crate::core::cpu_regs::CpuRegs; 4 | use crate::core::cycle_manager::CycleManager; 5 | use crate::core::div_sqrt::DivSqrt; 6 | use crate::core::graphics::gpu::Gpu; 7 | use crate::core::hle::arm7_hle::Arm7Hle; 8 | use crate::core::input::Input; 9 | use crate::core::ipc::Ipc; 10 | use crate::core::memory::cartridge::Cartridge; 11 | use crate::core::memory::dma::Dma; 12 | use crate::core::memory::mem::Memory; 13 | use crate::core::rtc::Rtc; 14 | use crate::core::spi::Spi; 15 | use crate::core::spu::{SoundSampler, Spu}; 16 | use crate::core::thread_regs::ThreadRegs; 17 | use crate::core::timers::Timers; 18 | use crate::core::wifi::Wifi; 19 | use crate::jit::jit_memory::JitMemory; 20 | use crate::settings::Settings; 21 | use std::sync::atomic::{AtomicU16, AtomicU32}; 22 | use std::sync::Arc; 23 | 24 | pub struct Emu { 25 | pub ipc: Ipc, 26 | pub cartridge: Cartridge, 27 | pub gpu: Gpu, 28 | pub cm: CycleManager, 29 | pub thread: [ThreadRegs; 2], 30 | pub cpu: [CpuRegs; 2], 31 | pub cp15: Cp15, 32 | pub input: Input, 33 | pub mem: Memory, 34 | pub hle: Arm7Hle, 35 | pub div_sqrt: DivSqrt, 36 | pub spi: Spi, 37 | pub rtc: Rtc, 38 | pub spu: Spu, 39 | pub dma: [Dma; 2], 40 | pub timers: [Timers; 2], 41 | pub wifi: Wifi, 42 | pub jit: JitMemory, 43 | pub breakout_imm: bool, 44 | pub settings: Settings, 45 | } 46 | 47 | impl Emu { 48 | pub fn new(cartridge_io: CartridgeIo, fps: Arc, key_map: Arc, touch_points: Arc, sound_sampler: Arc, jit: JitMemory, settings: Settings) -> Self { 49 | Emu { 50 | ipc: Ipc::new(&settings), 51 | cartridge: Cartridge::new(cartridge_io), 52 | gpu: Gpu::new(fps), 53 | cm: CycleManager::new(), 54 | thread: [ThreadRegs::new(), ThreadRegs::new()], 55 | cpu: [CpuRegs::new(), CpuRegs::new()], 56 | cp15: Cp15::new(), 57 | input: Input::new(key_map), 58 | mem: Memory::new(), 59 | hle: Arm7Hle::new(), 60 | div_sqrt: DivSqrt::new(), 61 | spi: Spi::new(touch_points), 62 | rtc: Rtc::new(), 63 | spu: Spu::new(sound_sampler), 64 | dma: [Dma::new(), Dma::new()], 65 | timers: [Timers::new(), Timers::new()], 66 | wifi: Wifi::new(), 67 | jit, 68 | breakout_imm: false, 69 | settings, 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/core/exception_handler.rs: -------------------------------------------------------------------------------- 1 | #[repr(u8)] 2 | #[derive(Copy, Clone, Eq, PartialEq)] 3 | pub enum ExceptionVector { 4 | Reset = 0x0, 5 | UndefinedInstruction = 0x4, 6 | SoftwareInterrupt = 0x8, 7 | PrefetchAbort = 0xC, 8 | DataAbort = 0x10, 9 | AddressExceeds26Bit = 0x14, 10 | NormalInterrupt = 0x18, 11 | FastInterrupt = 0x1C, 12 | } 13 | 14 | mod handler { 15 | use crate::core::emu::Emu; 16 | use crate::core::exception_handler::ExceptionVector; 17 | use crate::core::hle::bios; 18 | use crate::core::thread_regs::Cpsr; 19 | use crate::core::CpuType; 20 | use crate::logging::debug_println; 21 | use bilge::prelude::u5; 22 | 23 | pub fn handle(emu: &mut Emu, opcode: u32, vector: ExceptionVector) { 24 | if CPU == CpuType::ARM7 || emu.cp15.exception_addr != 0 { 25 | match vector { 26 | ExceptionVector::SoftwareInterrupt => bios::swi::(((opcode >> if THUMB { 0 } else { 16 }) & 0xFF) as u8, emu), 27 | ExceptionVector::NormalInterrupt => bios::interrupt::(emu), 28 | _ => todo!(), 29 | } 30 | } else { 31 | debug_println!("{CPU:?} handle exception"); 32 | debug_assert!(vector != ExceptionVector::SoftwareInterrupt); 33 | 34 | const MODES: [u8; 8] = [0x13, 0x1B, 0x13, 0x17, 0x17, 0x13, 0x12, 0x11]; 35 | let regs = &mut emu.thread[CPU]; 36 | 37 | let mut new_cpsr = Cpsr::from(regs.cpsr); 38 | new_cpsr.set_mode(u5::new(MODES[(vector as usize) >> 2])); 39 | new_cpsr.set_thumb(false); 40 | new_cpsr.set_fiq_disable(true); 41 | new_cpsr.set_irq_disable(true); 42 | emu.thread_set_cpsr::(CPU, new_cpsr.into()); 43 | 44 | let regs = &mut emu.thread[CPU]; 45 | // Interrupt handler will subtract 4 from lr, offset this 46 | regs.lr = regs.pc + 4; 47 | regs.pc = vector as u32; 48 | } 49 | } 50 | } 51 | 52 | pub use handler::handle; 53 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/mod.rs: -------------------------------------------------------------------------------- 1 | use std::marker::ConstParamTy; 2 | 3 | pub mod registers_2d; 4 | pub mod renderer_2d; 5 | pub mod renderer_soft_2d; 6 | 7 | #[derive(ConstParamTy, Debug, Default, Eq, PartialEq)] 8 | #[repr(u8)] 9 | pub enum Gpu2DEngine { 10 | #[default] 11 | A, 12 | B, 13 | } 14 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/renderer_soft_2d.rs: -------------------------------------------------------------------------------- 1 | use crate::core::graphics::gpu_2d::renderer_2d::{Gpu2DCommon, Gpu2DMem, Gpu2DRenderRegs}; 2 | use crate::core::graphics::gpu_2d::Gpu2DEngine; 3 | 4 | pub struct Gpu2DSoftRenderer {} 5 | 6 | impl Gpu2DSoftRenderer { 7 | pub fn new() -> Gpu2DSoftRenderer { 8 | Gpu2DSoftRenderer {} 9 | } 10 | 11 | pub fn start_render(&mut self, common: &Gpu2DCommon, regs: &Gpu2DRenderRegs, mem: Gpu2DMem) {} 12 | 13 | pub fn wait_for_render() {} 14 | } 15 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/bg_frag_affine_extended.cg: -------------------------------------------------------------------------------- 1 | float4 drawAffine(int x, int y, int bgNum) { 2 | short size = short(affineDims.x); 3 | 4 | int2 coords = int2(calculateAffineCoords(x, y, bgNum)); 5 | 6 | bool wrap = (bgCnt & (1 << 13)) != 0; 7 | if (wrap) { 8 | coords.x &= size - 1; 9 | coords.y &= size - 1; 10 | } else if (coords.x < 0 || coords.x >= size || coords.y < 0 || coords.y >= size) { 11 | discard; 12 | } 13 | 14 | int screenAddr = ((dispCnt >> 11) & 0x70000) + ((bgCnt << 3) & 0x0F800); 15 | int charAddr = ((dispCnt >> 8) & 0x70000) + ((bgCnt << 12) & 0x3C000); 16 | 17 | int xBlockNum = coords.x >> 3; 18 | int xInBlock = coords.x & 7; 19 | int yBlockNum = coords.y >> 3; 20 | int yInBlock = coords.y & 7; 21 | 22 | screenAddr += (yBlockNum * (size >> 3) + xBlockNum) * 2; 23 | int screenEntry = readBg16Aligned(screenAddr); 24 | 25 | bool isHFlip = (screenEntry & (1 << 10)) != 0; 26 | bool isVFlip = (screenEntry & (1 << 11)) != 0; 27 | 28 | if (isHFlip) { 29 | xInBlock = 7 - xInBlock; 30 | } 31 | if (isVFlip) { 32 | yInBlock = 7 - yInBlock; 33 | } 34 | 35 | charAddr += (screenEntry & 0x3FF) * 64 + yInBlock * 8 + xInBlock; 36 | 37 | int palIndex = readBg8(charAddr); 38 | if (palIndex == 0) { 39 | discard; 40 | } 41 | 42 | bool useExtPal = (dispCnt & (1 << 30)) != 0; 43 | if (useExtPal) { 44 | palIndex += bgNum * 4096 + ((screenEntry & 0xF000) >> 4); 45 | return float4(readExtPal(palIndex).rgb, 1.0); 46 | } else { 47 | return float4(readPal(palIndex).rgb, 1.0); 48 | } 49 | } 50 | 51 | void main(out float4 color : COLOR) { 52 | short bgNum = short(screenPos.z); 53 | 54 | short winEnabled = short(tex2D(winTex, screenPosF).x * 255.0); 55 | if ((winEnabled & (1 << bgNum)) == 0) { 56 | discard; 57 | } 58 | 59 | short x = short(screenPos.x); 60 | short y = short(screenPos.y); 61 | 62 | color = drawAffine(x, y, bgNum); 63 | 64 | short priority = short(bgCnt & 3); 65 | color.a = float(priority) / 4.0; 66 | } 67 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/bg_frag_bitmap.cg: -------------------------------------------------------------------------------- 1 | float4 drawBitmap(short x, short y, short bgNum) { 2 | short width = short(affineDims.x); 3 | short height = short(affineDims.y); 4 | 5 | int2 coords = int2(calculateAffineCoords(x, y, bgNum)); 6 | 7 | bool wrap = (bgCnt & (1 << 13)) != 0; 8 | if (wrap) { 9 | coords.x &= width - 1; 10 | coords.y &= height - 1; 11 | } else if (coords.x < 0 || coords.x >= width || coords.y < 0 || coords.y >= height) { 12 | discard; 13 | } 14 | 15 | int dataBase = (bgCnt << 6) & 0x7C000; 16 | bool usePal = (bgCnt & (1 << 2)) == 0; 17 | if (usePal) { 18 | int palIndex = readBg8(dataBase + coords.y * width + coords.x); 19 | if (palIndex == 0) { 20 | discard; 21 | } 22 | return float4(readPal(palIndex).rgb, 1.0); 23 | } else { 24 | short color = readBg16Aligned(dataBase + (coords.y * width + coords.x) * 2); 25 | if ((color & (1 << 15)) == 0) { 26 | discard; 27 | } 28 | return float4(normRgb5(color), 1.0); 29 | } 30 | } 31 | 32 | void main(out float4 color : COLOR) { 33 | short bgNum = short(screenPos.z); 34 | 35 | short winEnabled = short(tex2D(winTex, screenPosF).x * 255.0); 36 | if ((winEnabled & (1 << bgNum)) == 0) { 37 | discard; 38 | } 39 | 40 | short x = short(screenPos.x); 41 | short y = short(screenPos.y); 42 | 43 | color = drawBitmap(x, y, bgNum); 44 | 45 | short priority = short(bgCnt & 3); 46 | color.a = float(priority) / 4.0; 47 | } 48 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/bg_frag_common.cg: -------------------------------------------------------------------------------- 1 | float3 in screenPos : TEXCOORD0; 2 | float2 in screenPosF : TEXCOORD1; 3 | float2 in affineDims : TEXCOORD2; 4 | 5 | uniform int dispCnt; 6 | uniform int bgCnt; 7 | 8 | uniform sampler2D bgTex : TEXUNIT0; 9 | uniform sampler2D palTex : TEXUNIT1; 10 | uniform sampler2D extPalTex : TEXUNIT2; 11 | uniform sampler2D winTex : TEXUNIT3; 12 | uniform sampler2D display3dTex : TEXUNIT4; 13 | 14 | uniform BgUbo { 15 | int bgOfs[192 * 4]; 16 | int bgX[192 * 2]; 17 | int bgY[192 * 2]; 18 | int bgPas[192 * 2]; 19 | int bgPbs[192 * 2]; 20 | int bgPcs[192 * 2]; 21 | int bgPds[192 * 2]; 22 | } BgUbo : BUFFER[0]; 23 | 24 | short readBg8(int addr) { 25 | short addrX = (addr >> 2) & 0x1FF; 26 | short addrY = addr >> 11; 27 | float x = float(addrX) / 511.0; 28 | float y = float(addrY) / (BG_TEX_HEIGHT - 1.0); 29 | return short(tex2D(bgTex, float2(x, y))[addr & 3] * 255.0); 30 | } 31 | 32 | short readBg16Aligned(int addr) { 33 | short addrX = (addr >> 2) & 0x1FF; 34 | short addrY = addr >> 11; 35 | float x = float(addrX) / 511.0; 36 | float y = float(addrY) / (BG_TEX_HEIGHT - 1.0); 37 | float4 value = tex2D(bgTex, float2(x, y)); 38 | short entry = short(addr & 2); 39 | return short(value[entry] * 255.0) | (short(value[entry + 1] * 255.0) << 8); 40 | } 41 | 42 | float4 readPal(short index) { 43 | return tex2D(palTex, float2(float(index) / 511.0, 1.0)); 44 | } 45 | 46 | float4 readExtPal(short index) { 47 | short indexX = index & 0x1FF; 48 | short indexY = index >> 9; 49 | float x = float(indexX) / 511.0; 50 | float y = float(indexY) / 31.0; 51 | return tex2D(extPalTex, float2(x, y)); 52 | } 53 | 54 | float3 normRgb5(short color) { 55 | return float3(float(color & 0x1F), float((color >> 5) & 0x1F), float((color >> 10) & 0x1F)) / 31.0; 56 | } 57 | 58 | short2 calculateAffineCoords(short x, short y, short bgNum) { 59 | short index = (bgNum - 2) * 192 + y; 60 | float bgX = float(BgUbo.bgX[index]) / 256.0; 61 | float bgY = float(BgUbo.bgY[index]) / 256.0; 62 | float bgPa = float(BgUbo.bgPas[index]) / 256.0; 63 | float bgPb = float(BgUbo.bgPbs[index]) / 256.0; 64 | float bgPc = float(BgUbo.bgPcs[index]) / 256.0; 65 | float bgPd = float(BgUbo.bgPds[index]) / 256.0; 66 | return short2(short(bgX + bgPb + float(x) * bgPa), short(bgY + bgPd + float(x) * bgPc)); 67 | } 68 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/bg_frag_display_3d.cg: -------------------------------------------------------------------------------- 1 | void main(out float4 color : COLOR) { 2 | short bgNum = short(screenPos.z); 3 | 4 | short winEnabled = short(tex2D(winTex, screenPosF).x * 255.0); 5 | if ((winEnabled & (1 << bgNum)) == 0) { 6 | discard; 7 | } 8 | 9 | color = tex2D(display3dTex, screenPosF); 10 | if (color.a == 0.0) { 11 | discard; 12 | } 13 | 14 | short priority = short(bgCnt & 3); 15 | color.a = float(priority) / 4.0; 16 | } 17 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/bg_frag_text_4bpp.cg: -------------------------------------------------------------------------------- 1 | float4 drawText(short x, short y, short bgNum) { 2 | int screenAddr = ((dispCnt >> 11) & 0x70000) + ((bgCnt << 3) & 0x0F800); 3 | int charAddr = ((dispCnt >> 8u) & 0x70000) + ((bgCnt << 12) & 0x3C000); 4 | 5 | int of = BgUbo.bgOfs[bgNum * 192 + y]; 6 | x += short(of & 0xFFFF); 7 | x &= 0x1FF; 8 | y += short(of >> 16); 9 | y &= 0x1FF; 10 | 11 | // 512 Width 12 | if (x > 255 && (bgCnt & (1 << 14)) != 0) { 13 | screenAddr += 0x800; 14 | } 15 | 16 | // 512 Height 17 | if (y > 255 && (bgCnt & (1 << 15)) != 0) { 18 | screenAddr += (bgCnt & (1 << 14)) != 0 ? 0x1000 : 0x800; 19 | } 20 | 21 | short xBlock = x & 0xF8; 22 | short xInBlock = x & 7; 23 | short yBlock = y & 0xF8; 24 | short yInBlock = y & 7; 25 | 26 | screenAddr += yBlock << 3; 27 | screenAddr += xBlock >> 2; 28 | int screenEntry = readBg16Aligned(screenAddr); 29 | 30 | bool isHFlip = (screenEntry & (1 << 10)) != 0; 31 | bool isVFlip = (screenEntry & (1 << 11)) != 0; 32 | 33 | if (isHFlip) { 34 | xInBlock = 7 - xInBlock; 35 | } 36 | if (isVFlip) { 37 | yInBlock = 7 - yInBlock; 38 | } 39 | 40 | charAddr += ((screenEntry & 0x3FF) << 5) + (yInBlock << 2); 41 | charAddr += xInBlock >> 1; 42 | 43 | short palIndex = readBg8(charAddr); 44 | palIndex >>= 4 * (xInBlock & 1); 45 | palIndex &= 0xF; 46 | if (palIndex == 0) { 47 | discard; 48 | } 49 | 50 | palIndex += (screenEntry & 0xF000) >> 8; 51 | return float4(readPal(palIndex).rgb, 1.0); 52 | } 53 | 54 | void main(out float4 color : COLOR) { 55 | short bgNum = short(screenPos.z); 56 | 57 | short winEnabled = short(tex2D(winTex, screenPosF).x * 255.0); 58 | if ((winEnabled & (1 << bgNum)) == 0) { 59 | discard; 60 | } 61 | 62 | short x = short(screenPos.x); 63 | short y = short(screenPos.y); 64 | 65 | color = drawText(x, y, bgNum); 66 | 67 | short priority = short(bgCnt & 3); 68 | color.a = float(priority) / 4.0; 69 | } 70 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/bg_frag_text_8bpp.cg: -------------------------------------------------------------------------------- 1 | float4 drawText(short x, short y, short bgNum) { 2 | int screenAddr = ((dispCnt >> 11) & 0x70000) + ((bgCnt << 3) & 0x0F800); 3 | int charAddr = ((dispCnt >> 8u) & 0x70000) + ((bgCnt << 12) & 0x3C000); 4 | 5 | int of = BgUbo.bgOfs[bgNum * 192 + y]; 6 | x += short(of & 0xFFFF); 7 | x &= 0x1FF; 8 | y += short(of >> 16); 9 | y &= 0x1FF; 10 | 11 | // 512 Width 12 | if (x > 255 && (bgCnt & (1 << 14)) != 0) { 13 | screenAddr += 0x800; 14 | } 15 | 16 | // 512 Height 17 | if (y > 255 && (bgCnt & (1 << 15)) != 0) { 18 | screenAddr += (bgCnt & (1 << 14)) != 0 ? 0x1000 : 0x800; 19 | } 20 | 21 | short xBlock = x & 0xF8; 22 | short xInBlock = x & 7; 23 | short yBlock = y & 0xF8; 24 | short yInBlock = y & 7; 25 | 26 | screenAddr += yBlock << 3; 27 | screenAddr += xBlock >> 2; 28 | int screenEntry = readBg16Aligned(screenAddr); 29 | 30 | bool isHFlip = (screenEntry & (1 << 10)) != 0; 31 | bool isVFlip = (screenEntry & (1 << 11)) != 0; 32 | 33 | if (isHFlip) { 34 | xInBlock = 7 - xInBlock; 35 | } 36 | if (isVFlip) { 37 | yInBlock = 7 - yInBlock; 38 | } 39 | 40 | charAddr += ((screenEntry & 0x3FF) << 6) + (yInBlock << 3); 41 | charAddr += xInBlock; 42 | 43 | short palIndex = readBg8(charAddr); 44 | if (palIndex == 0) { 45 | discard; 46 | } 47 | 48 | bool useExtPal = (dispCnt & (1 << 30)) != 0; 49 | if (useExtPal) { 50 | short slot = bgNum < 2 && (bgCnt & (1 << 13)) != 0 ? bgNum + 2 : bgNum; 51 | palIndex += slot * 4096 + ((screenEntry & 0xF000) >> 4); 52 | return float4(readExtPal(palIndex).rgb, 1.0); 53 | } else { 54 | return float4(readPal(palIndex).rgb, 1.0); 55 | } 56 | } 57 | 58 | void main(out float4 color : COLOR) { 59 | short bgNum = short(screenPos.z); 60 | 61 | short winEnabled = short(tex2D(winTex, screenPosF).x * 255.0); 62 | if ((winEnabled & (1 << bgNum)) == 0) { 63 | discard; 64 | } 65 | 66 | short x = short(screenPos.x); 67 | short y = short(screenPos.y); 68 | 69 | color = drawText(x, y, bgNum); 70 | 71 | short priority = short(bgCnt & 3); 72 | color.a = float(priority) / 4.0; 73 | } 74 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/bg_vert.cg: -------------------------------------------------------------------------------- 1 | float4 out gl_Position : POSITION; 2 | float3 out screenPos : TEXCOORD0; 3 | float2 out screenPosF : TEXCOORD1; 4 | float2 out affineDims : TEXCOORD2; 5 | 6 | void main(float3 position) { 7 | float normX = position.x * 0.5 + 0.5; 8 | screenPos = float3(max(normX * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0), position.z); 9 | screenPosF = float2(normX, position.y / 192.0); 10 | affineDims = float2(0.0, 0.0); 11 | gl_Position = float4(position.x, 1.0 - screenPosF.y * 2.0, 0.0, 1.0); 12 | } 13 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/bg_vert_affine_extended.cg: -------------------------------------------------------------------------------- 1 | float4 out gl_Position : POSITION; 2 | float3 out screenPos : TEXCOORD0; 3 | float2 out screenPosF : TEXCOORD1; 4 | float2 out affineDims : TEXCOORD2; 5 | 6 | uniform int bgCnt; 7 | 8 | void main(float3 position) { 9 | float size = float(short(128 << ((bgCnt >> 14) & 0x3))); 10 | affineDims = float2(size, size); 11 | 12 | float normX = position.x * 0.5 + 0.5; 13 | screenPos = float3(max(normX * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0), position.z); 14 | screenPosF = float2(normX, position.y / 192.0); 15 | gl_Position = float4(position.x, 1.0 - screenPosF.y * 2.0, 0.0, 1.0); 16 | } 17 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/bg_vert_bitmap.cg: -------------------------------------------------------------------------------- 1 | float4 out gl_Position : POSITION; 2 | float3 out screenPos : TEXCOORD0; 3 | float2 out screenPosF : TEXCOORD1; 4 | float2 out affineDims : TEXCOORD2; 5 | 6 | uniform int bgCnt; 7 | 8 | static const float2 BitMapSizeLookup[4] = {float2(128.0, 128.0), float2(256.0, 256.0), float2(512.0, 256.0), float2(512.0, 512.0)}; 9 | 10 | void main(float3 position) { 11 | short size = (bgCnt >> 14) & 0x3; 12 | affineDims = BitMapSizeLookup[size]; 13 | 14 | float normX = position.x * 0.5 + 0.5; 15 | screenPos = float3(max(normX * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0), position.z); 16 | screenPosF = float2(normX, position.y / 192.0); 17 | gl_Position = float4(position.x, 1.0 - screenPosF.y * 2.0, 0.0, 1.0); 18 | } 19 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/blend_frag.cg: -------------------------------------------------------------------------------- 1 | float2 in screenPos : TEXCOORD0; 2 | 3 | uniform sampler2D bg0Tex : TEXUNIT0; 4 | uniform sampler2D bg1Tex : TEXUNIT1; 5 | uniform sampler2D bg2Tex : TEXUNIT2; 6 | uniform sampler2D bg3Tex : TEXUNIT3; 7 | uniform sampler2D objTex : TEXUNIT4; 8 | uniform sampler2D objDepthTex : TEXUNIT5; 9 | uniform sampler2D winTex : TEXUNIT6; 10 | 11 | uniform BlendUbo { 12 | int bldCnts[192]; 13 | int bldAlphas[192]; 14 | int bldYs[192]; 15 | } BlendUbo : BUFFER[0]; 16 | 17 | static short topNum = 5; 18 | static short bottomNum = 5; 19 | // Priority of 1.0 indicates a discarded fragment 20 | // Due to float imprecision priority must be < 0.9 21 | // Lowest priority is 1.0 / 4.0 22 | static float topPrio = 0.9; 23 | static float bottomPrio = 0.9; 24 | static float4 topColor = float4(0.0, 0.0, 0.0, 1.0); 25 | static float4 bottomColor = float4(0.0, 0.0, 0.0, 1.0); 26 | 27 | void sortObjPrio() { 28 | float prio = tex2D(objDepthTex, screenPos) * 2.0 - 1.0; 29 | if (prio < topPrio) { 30 | topNum = 4; 31 | // Give obj a priority boost due to float imprecision 32 | topPrio = prio - 0.1; 33 | topColor = tex2D(objTex, screenPos); 34 | } 35 | } 36 | 37 | void sortBgPrio(short num, sampler2D bgTex) { 38 | float4 texColor = tex2D(bgTex, screenPos); 39 | if (texColor.a < topPrio) { 40 | bottomNum = topNum; 41 | bottomPrio = topPrio; 42 | bottomColor = topColor; 43 | 44 | topNum = num; 45 | topPrio = texColor.a; 46 | topColor = texColor; 47 | } else if (texColor.a < bottomPrio) { 48 | bottomNum = num; 49 | bottomPrio = texColor.a; 50 | bottomColor = texColor; 51 | } 52 | } 53 | 54 | float4 alphaBlend(int bldAlpha) { 55 | short eva = short(bldAlpha & 0x1F); 56 | short evb = short((bldAlpha >> 8) & 0x1F); 57 | float evaF = min(float(eva) / 16.0, 1.0); 58 | float evbF = min(float(evb) / 16.0, 1.0); 59 | float3 blendedColor = topColor.rgb * evaF + bottomColor.rgb * evbF; 60 | return float4(blendedColor.rgb, 1.0); 61 | } 62 | 63 | void main(out float4 color : COLOR) { 64 | sortObjPrio(); 65 | sortBgPrio(0, bg0Tex); 66 | sortBgPrio(1, bg1Tex); 67 | sortBgPrio(2, bg2Tex); 68 | sortBgPrio(3, bg3Tex); 69 | 70 | if (topNum == 5) { 71 | discard; 72 | } 73 | 74 | if (topNum == 4 && topColor.a == 0.0) { 75 | // Semi transparent object 76 | short y = short(screenPos.y * 191.0); 77 | int bldCnt = BlendUbo.bldCnts[y]; 78 | bool blendBottom = ((bldCnt >> 8) & (1 << bottomNum)) != 0; 79 | if (blendBottom) { 80 | color = alphaBlend(BlendUbo.bldAlphas[y]); 81 | return; 82 | } 83 | 84 | int bldMode = (bldCnt >> 6) & 3; 85 | if (bldMode < 2) { 86 | color = float4(topColor.rgb, 1.0); 87 | return; 88 | } 89 | } 90 | 91 | short winEnabled = short(tex2D(winTex, screenPos).x * 255.0); 92 | if ((winEnabled & (1 << 5)) == 0) { 93 | color = float4(topColor.rgb, 1.0); 94 | return; 95 | } 96 | 97 | short y = short(screenPos.y * 191.0); 98 | 99 | int bldCnt = BlendUbo.bldCnts[y]; 100 | int bldMode = (bldCnt >> 6) & 3; 101 | 102 | if (bldMode == 0) { 103 | color = float4(topColor.rgb, 1.0); 104 | return; 105 | } 106 | 107 | bool blendTop = (bldCnt & (1 << topNum)) != 0; 108 | if (!blendTop) { 109 | color = float4(topColor.rgb, 1.0); 110 | return; 111 | } 112 | 113 | if (bldMode == 1) { 114 | bool blendBottom = ((bldCnt >> 8) & (1 << bottomNum)) != 0; 115 | if (!blendBottom) { 116 | color = float4(topColor.rgb, 1.0); 117 | return; 118 | } 119 | color = alphaBlend(BlendUbo.bldAlphas[y]); 120 | } else if (bldMode == 2) { 121 | short bldY = short(BlendUbo.bldYs[y]); 122 | float bldYF = float(bldY) / 16.0; 123 | float3 increaseColor = (1.0 - topColor.rgb) * bldYF; 124 | color = float4((topColor.rgb + increaseColor), 1.0); 125 | } else if (bldMode == 3) { 126 | short bldY = short(BlendUbo.bldYs[y]); 127 | float bldYF = float(bldY) / 16.0; 128 | float3 decreaseColor = topColor.rgb * bldYF; 129 | color = float4((topColor.rgb - decreaseColor), 1.0); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/blend_vert.cg: -------------------------------------------------------------------------------- 1 | float4 out gl_Position : POSITION; 2 | float2 out screenPos : TEXCOORD0; 3 | 4 | void main(float3 position) { 5 | screenPos = float2(position.x * 0.5 + 0.5, 1.0 - position.y * 0.5 - 0.5); 6 | gl_Position = float4(position.xy, 0.0, 1.0); 7 | } 8 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/obj_frag.cg: -------------------------------------------------------------------------------- 1 | float3 in objPos : TEXCOORD0; 2 | float2 in objDims : TEXCOORD1; 3 | float2 in screenPosF : TEXCOORD2; 4 | 5 | uniform sampler2D oamTex : TEXUNIT0; 6 | uniform sampler2D objTex : TEXUNIT1; 7 | uniform sampler2D palTex : TEXUNIT2; 8 | uniform sampler2D extPalTex : TEXUNIT3; 9 | uniform sampler2D winTex : TEXUNIT4; 10 | 11 | uniform int dispCnt; 12 | uniform ObjUbo { 13 | int mapWidths[128]; 14 | int objBounds[128]; 15 | } ObjUbo : BUFFER[0]; 16 | 17 | short readOam16Aligned(short addr) { 18 | float x = float(addr >> 2) / 255.0f; 19 | float4 value = tex2D(oamTex, float2(x, 1.0)); 20 | short entry = addr & 2; 21 | return short(value[entry] * 255.0) | (short(value[entry + 1] * 255.0) << 8); 22 | } 23 | 24 | short readObj8(int addr) { 25 | short addrX = (addr >> 2) & 0x1FF; 26 | short addrY = addr >> 11; 27 | float x = float(addrX) / 511.0f; 28 | float y = float(addrY) / (OBJ_TEX_HEIGHT - 1.0); 29 | return int(tex2D(objTex, float2(x, y))[addr & 3] * 255.0); 30 | } 31 | 32 | short readObj16Aligned(int addr) { 33 | short addrX = (addr >> 2) & 0x1FF; 34 | short addrY = addr >> 11; 35 | float x = float(addrX) / 511.0f; 36 | float y = float(addrY) / (OBJ_TEX_HEIGHT - 1.0); 37 | float4 value = tex2D(objTex, float2(x, y)); 38 | short entry = addr & 2; 39 | return short(value[entry] * 255.0) | (short(value[entry + 1] * 255.0) << 8); 40 | } 41 | 42 | float4 readPal(short index) { 43 | return tex2D(palTex, float2(float(index) / 511.0, 1.0)); 44 | } 45 | 46 | float4 readExtPal(short index) { 47 | short indexX = index & 0x1FF; 48 | short indexY = index >> 9; 49 | float x = float(indexX) / 511.0; 50 | float y = float(indexY) / 7.0; 51 | return tex2D(extPalTex, float2(x, y)); 52 | } 53 | 54 | float3 normRgb5(short color) { 55 | return float3(float(color & 0x1F), float((color >> 5) & 0x1F), float((color >> 10) & 0x1F)) / 31.0; 56 | } 57 | 58 | float4 drawSprite(short objX, short objY, short attrib0, short oamIndex) { 59 | short mapWidth = short(ObjUbo.mapWidths[oamIndex]); 60 | short objBound = short(ObjUbo.objBounds[oamIndex]); 61 | 62 | short attrib2 = readOam16Aligned(oamIndex * 8 + 4); 63 | 64 | int tileIndex = attrib2 & 0x3FF; 65 | int tileAddr = tileIndex * objBound; 66 | 67 | bool is8bpp = (attrib0 & (1 << 13)) != 0; 68 | if (is8bpp) { 69 | tileAddr += ((objY & 7) + (objY >> 3) * mapWidth) * 8; 70 | tileAddr += (objX >> 3) * 64 + (objX & 7); 71 | 72 | int palIndex = readObj8(tileAddr); 73 | if (palIndex == 0) { 74 | discard; 75 | } 76 | 77 | bool useExtPal = (dispCnt & (1 << 31)) != 0; 78 | if (useExtPal) { 79 | int palBaseIndex = (attrib2 & 0xF000) >> 4; 80 | return float4(readExtPal(palBaseIndex + palIndex).rgb, 1.0); 81 | } else { 82 | return float4(readPal(0x100 + palIndex).rgb, 1.0); 83 | } 84 | } else { 85 | tileAddr += ((objY & 7) + (objY >> 3) * mapWidth) * 4; 86 | tileAddr += (objX >> 3) * 32 + (objX & 7) / 2; 87 | 88 | short palIndex = readObj8(tileAddr); 89 | palIndex >>= 4 * (objX & 1); 90 | palIndex &= 0xF; 91 | if (palIndex == 0) { 92 | discard; 93 | } 94 | 95 | short palBank = (attrib2 >> 12) & 0xF; 96 | short palBase = 0x100 + palBank * 16; 97 | return float4(readPal(palBase + palIndex).rgb, 1.0); 98 | } 99 | } 100 | 101 | float4 drawBitmap(short objX, short objY, short oamIndex) { 102 | int bitmapWidth = ObjUbo.mapWidths[oamIndex]; 103 | int dataBase = ObjUbo.objBounds[oamIndex]; 104 | 105 | int objColor = readObj16Aligned(dataBase + (objY * bitmapWidth + objX) * 2); 106 | if ((objColor & (1 << 15)) == 0) { 107 | discard; 108 | } 109 | return float4(normRgb5(objColor), 1.0); 110 | } 111 | 112 | void main(out float4 color : COLOR) { 113 | short objWidth = short(objDims.x); 114 | short objHeight = short(objDims.y); 115 | short objY = short(objPos.y); 116 | short objX = short(objPos.x); 117 | 118 | if (objX < 0 || objX >= objWidth || objY < 0 || objY >= objHeight) { 119 | discard; 120 | } 121 | 122 | short winEnabled = short(tex2D(winTex, screenPosF).x * 255.0); 123 | if ((winEnabled & (1 << 4)) == 0) { 124 | discard; 125 | } 126 | 127 | short oamIndex = short(objPos.z); 128 | 129 | short attrib0 = readOam16Aligned(oamIndex * 8); 130 | 131 | bool isBitmap = (attrib0 & 0xC00) == 0xC00; 132 | if (isBitmap) { 133 | color = drawBitmap(objX, objY, oamIndex); 134 | } else { 135 | color = drawSprite(objX, objY, attrib0, oamIndex); 136 | } 137 | 138 | bool semiTransparent = ((attrib0 >> 10) & 3) == 1; 139 | if (semiTransparent) { 140 | color.a = 0.0; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/obj_vert.cg: -------------------------------------------------------------------------------- 1 | float4 out gl_Position : POSITION; 2 | float3 out objPos : TEXCOORD0; 3 | float2 out objDims : TEXCOORD1; 4 | float2 out screenPosF : TEXCOORD2; 5 | 6 | uniform sampler2D oamTex : TEXUNIT0; 7 | 8 | uniform int dispCnt; 9 | 10 | short readOam16Aligned(short addr) { 11 | float x = float(addr >> 2) / 255.0f; 12 | float4 value = tex2D(oamTex, float2(x, 1.0)); 13 | short entry = addr & 2; 14 | return short(value[entry] * 255.0) | (short(value[entry + 1] * 255.0) << 8); 15 | } 16 | 17 | static const float2 SizeLookup[12] = { 18 | float2(8.0, 8.0), float2(16.0, 16.0), float2(32.0, 32.0), float2(64.0, 64.0), float2(16.0, 8.0), float2(32.0, 8.0), 19 | float2(32.0, 16.0), float2(64.0, 32.0), float2(8.0, 16.0), float2(8.0, 32.0), float2(16.0, 32.0), float2(32.0, 64.0), 20 | }; 21 | 22 | void main(float2 position, float oamIndex) { 23 | short index = short(oamIndex); 24 | 25 | short attrib0 = readOam16Aligned(index * 8); 26 | short attrib1 = readOam16Aligned(index * 8 + 2); 27 | short attrib2 = readOam16Aligned(index * 8 + 4); 28 | 29 | short oamX = short(attrib1 & 0x1FF); 30 | short oamY = short(attrib0 & 0xFF); 31 | 32 | int shape = (attrib0 >> 12) & 0xC; 33 | int size = (attrib1 >> 14) & 0x3; 34 | 35 | float2 oamDims = SizeLookup[shape | size]; 36 | float oamWidth = oamDims.x; 37 | float oamHeight = oamDims.y; 38 | objDims = short2(oamWidth, oamHeight); 39 | 40 | if (oamX >= 256) { 41 | oamX -= 512; 42 | } 43 | 44 | if (oamY >= 192) { 45 | oamY -= 256; 46 | } 47 | 48 | float2 pos = position; 49 | 50 | bool affine = (attrib0 & (1 << 8)) != 0; 51 | if (affine) { 52 | short affineIndex = (attrib1 >> 9) & 0x1F; 53 | short affineOffset = affineIndex * 0x20; 54 | short pa = readOam16Aligned(affineOffset + 6); 55 | short pb = readOam16Aligned(affineOffset + 14); 56 | short pc = readOam16Aligned(affineOffset + 22); 57 | short pd = readOam16Aligned(affineOffset + 30); 58 | float2x2 m = float2x2(float(pa), float(pb), float(pc), float(pd)) / 256.0; 59 | 60 | bool doubleAffine = (attrib0 & (1 << 9)) != 0; 61 | if (doubleAffine) { 62 | float2 normPos = mul(m, float2(max(oamWidth * 2.0 * pos.x - 0.9, 0.0) - oamWidth, max(oamHeight * 2.0 * pos.y - 0.9, 0.0) - oamHeight)); 63 | objPos = float3(normPos.x + oamWidth / 2.0, normPos.y + oamHeight / 2.0, oamIndex); 64 | oamWidth *= 2.0; 65 | oamHeight *= 2.0; 66 | } else { 67 | float2 normPos = mul(m, float2(max(oamWidth * pos.x - 0.5, 0.0) - oamWidth / 2.0, max(oamHeight * pos.y - 0.5, 0.0) - oamHeight / 2.0)); 68 | objPos = float3(normPos.x + oamWidth / 2.0, normPos.y + oamHeight / 2.0, oamIndex); 69 | } 70 | } else { 71 | bool isVFlip = (attrib1 & (1 << 13)) != 0; 72 | bool isHFlip = (attrib1 & (1 << 12)) != 0; 73 | 74 | if (isVFlip) { 75 | pos.y -= 1.0; 76 | pos.y = abs(pos.y); 77 | } 78 | 79 | if (isHFlip) { 80 | pos.x -= 1.0; 81 | pos.x = abs(pos.x); 82 | } 83 | 84 | objPos = float3((oamWidth - 0.1) * pos.x, (oamHeight - 0.1) * pos.y, oamIndex); 85 | } 86 | 87 | float x = float(oamX) + oamWidth * position.x; 88 | float y = float(oamY) + oamHeight * position.y; 89 | 90 | screenPosF = float2(x / 256.0, y / 192.0); 91 | 92 | short priority = short((attrib2 >> 10) & 3); 93 | gl_Position = float4(screenPosF.x * 2.0 - 1.0, 1.0 - screenPosF.y * 2.0, float(priority) / 4.0, 1.0); 94 | } 95 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/rotate_frag.cg: -------------------------------------------------------------------------------- 1 | float2 in texCoords : TEXCOORD0; 2 | 3 | uniform sampler2D tex : TEXUNIT0; 4 | 5 | void main(out float4 color : COLOR) { 6 | float2 flippedTexCoord = float2(texCoords.x, 1.0 - texCoords.y); 7 | color = tex2D(tex, flippedTexCoord); 8 | } -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/rotate_vert.cg: -------------------------------------------------------------------------------- 1 | float4 in in_position : POSITION; 2 | float2 in in_texcoord : TEXCOORD0; 3 | 4 | float4 out gl_Position : POSITION; 5 | float2 out texCoords : TEXCOORD0; 6 | 7 | void main(float4 position) { 8 | texCoords = in_texcoord; 9 | gl_Position = float4(in_position.xy, 0.0, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/vram_display_frag.cg: -------------------------------------------------------------------------------- 1 | float3 in screenPos : TEXCOORD0; 2 | 3 | uniform sampler2D lcdcPalTex : TEXUNIT0; 4 | 5 | uniform int dispCnt; 6 | 7 | float4 readLcdcPal(int index) { 8 | short indexX = index & 0x1FF; 9 | short indexY = index >> 9; 10 | float x = float(indexX) / 511.0; 11 | float y = float(indexY) / 655.0; 12 | return tex2D(lcdcPalTex, float2(x, y)); 13 | } 14 | 15 | void main(out float4 color : COLOR) { 16 | short x = short(screenPos.x); 17 | short y = short(screenPos.y); 18 | 19 | int addr = ((dispCnt >> 18) & 0x3) * 0x10000 + int(y) * 256 + int(x); 20 | color = float4(readLcdcPal(addr).rgb, 0.0); 21 | } 22 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/vram_display_vert.cg: -------------------------------------------------------------------------------- 1 | float2 out screenPos : TEXCOORD0; 2 | float4 out gl_Position : POSITION; 3 | 4 | void main(float2 position) { 5 | screenPos = float2(max((position.x * 0.5 + 0.5) * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0)); 6 | gl_Position = float4(position.x, 1.0 - position.y / 192.0 * 2.0, 0.0, 1.0); 7 | } 8 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/win_bg_frag.cg: -------------------------------------------------------------------------------- 1 | float3 in screenPos : TEXCOORD0; 2 | 3 | uniform int dispCnt; 4 | 5 | uniform WinBgUbo { 6 | int winH[192 * 2]; 7 | int winV[192 * 2]; 8 | int winIn[192]; 9 | int winOut[192]; 10 | } WinBgUbo : BUFFER[0]; 11 | 12 | static float4 oColor; 13 | 14 | bool checkBounds(short x, short y, short winNum) { 15 | bool winEnabled = (dispCnt & (1 << (13 + winNum))) != 0; 16 | if (!winEnabled) { 17 | return false; 18 | } 19 | 20 | int h = WinBgUbo.winH[winNum * 192 + y]; 21 | int v = WinBgUbo.winV[winNum * 192 + y]; 22 | 23 | int winX1 = (h >> 8) & 0xFF; 24 | int winX2 = h & 0xFF; 25 | 26 | int winY1 = (v >> 8) & 0xFF; 27 | int winY2 = v & 0xFF; 28 | 29 | if (winX1 <= winX2) { 30 | if (x < winX1 || x > winX2) { 31 | return false; 32 | } 33 | } else { 34 | if (x >= winX2 && x < winX1) { 35 | return false; 36 | } 37 | } 38 | 39 | if (winY1 <= winY2) { 40 | if (y < winY1 || y > winY2) { 41 | return false; 42 | } 43 | } else { 44 | if (y >= winY2 && y < winY1) { 45 | return false; 46 | } 47 | } 48 | 49 | short enabled = (WinBgUbo.winIn[y] >> (winNum * 8)) & 0xFF; 50 | oColor = float4(float(enabled) / 255.0, 0.0, 0.0, 0.0); 51 | return true; 52 | } 53 | 54 | void main(out float4 color : COLOR) { 55 | short x = short(screenPos.x); 56 | short y = short(screenPos.y); 57 | 58 | if (!checkBounds(x, y, 0) && !checkBounds(x, y, 1)) { 59 | short enabled = WinBgUbo.winOut[y] & 0xFF; 60 | oColor = float4(float(enabled) / 255.0, 0.0, 0.0, 0.0); 61 | } 62 | 63 | color = oColor; 64 | } 65 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/cg/win_bg_vert.cg: -------------------------------------------------------------------------------- 1 | float2 out screenPos : TEXCOORD0; 2 | float4 out gl_Position : POSITION; 3 | 4 | void main(float2 position) { 5 | screenPos = float2(max((position.x * 0.5 + 0.5) * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0)); 6 | gl_Position = float4(position.x, 1.0 - position.y / 192.0 * 2.0, 0.0, 1.0); 7 | } 8 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/bg_frag_affine_extended.glsl: -------------------------------------------------------------------------------- 1 | vec4 drawAffine(int x, int y, int bgNum) { 2 | int size = int(affineDims.x); 3 | 4 | ivec2 coords = calculateAffineCoords(x, y, bgNum); 5 | 6 | bool wrap = (bgCnt & (1 << 13)) != 0; 7 | if (wrap) { 8 | coords.x &= size - 1; 9 | coords.y &= size - 1; 10 | } else if (coords.x < 0 || coords.x >= size || coords.y < 0 || coords.y >= size) { 11 | discard; 12 | } 13 | 14 | int screenAddr = ((dispCnt >> 11) & 0x70000) + ((bgCnt << 3) & 0x0F800); 15 | int charAddr = ((dispCnt >> 8) & 0x70000) + ((bgCnt << 12) & 0x3C000); 16 | 17 | int xBlockNum = coords.x >> 3; 18 | int xInBlock = coords.x & 7; 19 | int yBlockNum = coords.y >> 3; 20 | int yInBlock = coords.y & 7; 21 | 22 | screenAddr += (yBlockNum * (size / 8) + xBlockNum) * 2; 23 | int screenEntry = readBg16Aligned(screenAddr); 24 | 25 | int isHFlip = (screenEntry >> 10) & 1; 26 | int isVFlip = (screenEntry >> 11) & 1; 27 | 28 | xInBlock = abs(isHFlip * 7 - xInBlock); 29 | yInBlock = abs(isVFlip * 7 - yInBlock); 30 | 31 | charAddr += (screenEntry & 0x3FF) * 64 + yInBlock * 8 + xInBlock; 32 | 33 | int palAddr = readBg8(charAddr); 34 | if (palAddr == 0) { 35 | discard; 36 | } 37 | palAddr *= 2; 38 | 39 | bool useExtPal = (dispCnt & (1 << 30)) != 0; 40 | if (useExtPal) { 41 | palAddr += bgNum * 8192 + ((screenEntry & 0xF000) >> 3); 42 | int color = readExtPal16Aligned(palAddr); 43 | return vec4(normRgb5(color), 1.0); 44 | } else { 45 | int color = readPal16Aligned(palAddr); 46 | return vec4(normRgb5(color), 1.0); 47 | } 48 | } 49 | 50 | void main() { 51 | int bgNum = int(screenPos.z); 52 | 53 | int winEnabled = int(texture(winTex, screenPosF).x * 255.0); 54 | if ((winEnabled & (1 << bgNum)) == 0) { 55 | discard; 56 | } 57 | 58 | int x = int(screenPos.x); 59 | int y = int(screenPos.y); 60 | 61 | color = drawAffine(x, y, bgNum); 62 | 63 | int priority = bgCnt & 3; 64 | color.a = float(priority) / 4.0; 65 | } 66 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/bg_frag_bitmap.glsl: -------------------------------------------------------------------------------- 1 | vec4 drawBitmap(int x, int y, int bgNum) { 2 | int width = int(affineDims.x); 3 | int height = int(affineDims.y); 4 | 5 | ivec2 coords = calculateAffineCoords(x, y, bgNum); 6 | 7 | bool wrap = (bgCnt & (1 << 13)) != 0; 8 | if (wrap) { 9 | coords.x &= width - 1; 10 | coords.y &= height - 1; 11 | } else if (coords.x < 0 || coords.x >= width || coords.y < 0 || coords.y >= height) { 12 | discard; 13 | } 14 | 15 | int dataBase = (bgCnt << 6) & 0x7C000; 16 | bool usePal = (bgCnt & (1 << 2)) == 0; 17 | if (usePal) { 18 | int palAddr = readBg8(dataBase + coords.y * width + coords.x); 19 | if (palAddr == 0) { 20 | discard; 21 | } 22 | palAddr *= 2; 23 | 24 | int color = readPal16Aligned(palAddr); 25 | return vec4(normRgb5(color), 1.0); 26 | } else { 27 | int color = readBg16Aligned(dataBase + (coords.y * width + coords.x) * 2); 28 | if ((color & (1 << 15)) == 0) { 29 | discard; 30 | } 31 | return vec4(normRgb5(color), 1.0); 32 | } 33 | } 34 | 35 | void main() { 36 | int bgNum = int(screenPos.z); 37 | 38 | int winEnabled = int(texture(winTex, screenPosF).x * 255.0); 39 | if ((winEnabled & (1 << bgNum)) == 0) { 40 | discard; 41 | } 42 | 43 | int x = int(screenPos.x); 44 | int y = int(screenPos.y); 45 | 46 | color = drawBitmap(x, y, bgNum); 47 | 48 | int priority = bgCnt & 3; 49 | color.a = float(priority) / 4.0; 50 | } 51 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/bg_frag_common.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | precision highp int; 5 | 6 | layout(location = 0) out vec4 color; 7 | in vec3 screenPos; 8 | in vec2 screenPosF; 9 | in vec2 affineDims; 10 | 11 | uniform int dispCnt; 12 | uniform int bgCnt; 13 | 14 | uniform BgUbo { 15 | int bgOfs[192 * 4]; 16 | int bgX[192 * 2]; 17 | int bgY[192 * 2]; 18 | int bgPas[192 * 2]; 19 | int bgPbs[192 * 2]; 20 | int bgPcs[192 * 2]; 21 | int bgPds[192 * 2]; 22 | }; 23 | 24 | uniform sampler2D bgTex; 25 | uniform sampler2D palTex; 26 | uniform sampler2D extPalTex; 27 | uniform sampler2D winTex; 28 | uniform sampler2D display3dTex; 29 | 30 | int readBg8(int addr) { 31 | float x = float((addr >> 2) & 0x1FF) / 511.0; 32 | float y = float(addr >> 11) / (BG_TEX_HEIGHT - 1.0); 33 | return int(texture(bgTex, vec2(x, y))[addr & 3] * 255.0); 34 | } 35 | 36 | int readBg16Aligned(int addr) { 37 | int addrX = (addr >> 2) & 0x1FF; 38 | int addrY = addr >> 11; 39 | float x = float(addrX) / 511.0; 40 | float y = float(addrY) / (BG_TEX_HEIGHT - 1.0); 41 | vec4 value = texture(bgTex, vec2(x, y)); 42 | int entry = addr & 2; 43 | return int(value[entry] * 255.0) | (int(value[entry + 1] * 255.0) << 8); 44 | } 45 | 46 | int readPal16Aligned(int addr) { 47 | float x = float(addr >> 2) / 255.0; 48 | vec4 value = texture(palTex, vec2(x, 1.0)); 49 | int entry = addr & 2; 50 | return int(value[entry] * 255.0) | (int(value[entry + 1] * 255.0) << 8); 51 | } 52 | 53 | int readExtPal16Aligned(int addr) { 54 | int addrX = (addr >> 2) & 0x1FF; 55 | int addrY = addr >> 11; 56 | float x = float(addrX) / 511.0; 57 | float y = float(addrY) / 15.0; 58 | vec4 value = texture(extPalTex, vec2(x, y)); 59 | int entry = addr & 2; 60 | return int(value[entry] * 255.0) | (int(value[entry + 1] * 255.0) << 8); 61 | } 62 | 63 | vec3 normRgb5(int color) { 64 | return vec3(float(color & 0x1F), float((color >> 5) & 0x1F), float((color >> 10) & 0x1F)) / 31.0; 65 | } 66 | 67 | ivec2 calculateAffineCoords(int x, int y, int bgNum) { 68 | int index = (bgNum - 2) * 192 + y; 69 | float bgX = float(bgX[index]) / 256.0; 70 | float bgY = float(bgY[index]) / 256.0; 71 | float bgPa = float(bgPas[index]) / 256.0; 72 | float bgPb = float(bgPbs[index]) / 256.0; 73 | float bgPc = float(bgPcs[index]) / 256.0; 74 | float bgPd = float(bgPds[index]) / 256.0; 75 | return ivec2(int(bgX + bgPb + float(x) * bgPa), int(bgY + bgPd + float(x) * bgPc)); 76 | } 77 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/bg_frag_display_3d.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | int bgNum = int(screenPos.z); 3 | 4 | int winEnabled = int(texture(winTex, screenPosF).x * 255.0); 5 | if ((winEnabled & (1 << bgNum)) == 0) { 6 | discard; 7 | } 8 | 9 | color = texture(display3dTex, vec2(screenPosF.x, 1.0 - screenPosF.y)); 10 | if (color.a == 0.0) { 11 | discard; 12 | } 13 | 14 | int priority = bgCnt & 3; 15 | color.a = float(priority) / 4.0; 16 | } 17 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/bg_frag_text_4bpp.glsl: -------------------------------------------------------------------------------- 1 | vec4 drawText(int x, int y, int bgNum) { 2 | int screenAddr = ((dispCnt >> 11) & 0x70000) + ((bgCnt << 3) & 0x0F800); 3 | int charAddr = ((dispCnt >> 8) & 0x70000) + ((bgCnt << 12) & 0x3C000); 4 | 5 | int of = bgOfs[bgNum * 192 + y]; 6 | x += of & 0xFFFF; 7 | x &= 0x1FF; 8 | y += of >> 16; 9 | y &= 0x1FF; 10 | 11 | // 512 Width 12 | if (x > 255 && (bgCnt & (1 << 14)) != 0) { 13 | screenAddr += 0x800; 14 | } 15 | 16 | // 512 Height 17 | if (y > 255 && (bgCnt & (1 << 15)) != 0) { 18 | screenAddr += (bgCnt & (1 << 14)) != 0 ? 0x1000 : 0x800; 19 | } 20 | 21 | int xBlock = x & 0xF8; 22 | int xInBlock = x & 7; 23 | int yBlock = y & 0xF8; 24 | int yInBlock = y & 7; 25 | 26 | screenAddr += yBlock << 3; 27 | screenAddr += xBlock >> 2; 28 | int screenEntry = readBg16Aligned(screenAddr); 29 | 30 | int isHFlip = (screenEntry >> 10) & 1; 31 | int isVFlip = (screenEntry >> 11) & 1; 32 | 33 | xInBlock = abs(isHFlip * 7 - xInBlock); 34 | yInBlock = abs(isVFlip * 7 - yInBlock); 35 | 36 | charAddr += ((screenEntry & 0x3FF) << 5) + (yInBlock << 2); 37 | charAddr += xInBlock >> 1; 38 | 39 | int palAddr = readBg8(charAddr); 40 | palAddr >>= 4 * (xInBlock & 1); 41 | palAddr &= 0xF; 42 | if (palAddr == 0) { 43 | discard; 44 | } 45 | palAddr *= 2; 46 | palAddr += (screenEntry & 0xF000) >> 7; 47 | 48 | int color = readPal16Aligned(palAddr); 49 | return vec4(normRgb5(color), 1.0); 50 | } 51 | 52 | void main() { 53 | int bgNum = int(screenPos.z); 54 | 55 | int winEnabled = int(texture(winTex, screenPosF).x * 255.0); 56 | if ((winEnabled & (1 << bgNum)) == 0) { 57 | discard; 58 | } 59 | 60 | int x = int(screenPos.x); 61 | int y = int(screenPos.y); 62 | 63 | color = drawText(x, y, bgNum); 64 | 65 | int priority = bgCnt & 3; 66 | color.a = float(priority) / 4.0; 67 | } 68 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/bg_frag_text_8bpp.glsl: -------------------------------------------------------------------------------- 1 | vec4 drawText(int x, int y, int bgNum) { 2 | int screenAddr = ((dispCnt >> 11) & 0x70000) + ((bgCnt << 3) & 0x0F800); 3 | int charAddr = ((dispCnt >> 8) & 0x70000) + ((bgCnt << 12) & 0x3C000); 4 | 5 | int of = bgOfs[bgNum * 192 + y]; 6 | x += of & 0xFFFF; 7 | x &= 0x1FF; 8 | y += of >> 16; 9 | y &= 0x1FF; 10 | 11 | // 512 Width 12 | if (x > 255 && (bgCnt & (1 << 14)) != 0) { 13 | screenAddr += 0x800; 14 | } 15 | 16 | // 512 Height 17 | if (y > 255 && (bgCnt & (1 << 15)) != 0) { 18 | screenAddr += (bgCnt & (1 << 14)) != 0 ? 0x1000 : 0x800; 19 | } 20 | 21 | int xBlock = x & 0xF8; 22 | int xInBlock = x & 7; 23 | int yBlock = y & 0xF8; 24 | int yInBlock = y & 7; 25 | 26 | screenAddr += yBlock << 3; 27 | screenAddr += xBlock >> 2; 28 | int screenEntry = readBg16Aligned(screenAddr); 29 | 30 | int isHFlip = (screenEntry >> 10) & 1; 31 | int isVFlip = (screenEntry >> 11) & 1; 32 | 33 | xInBlock = abs(isHFlip * 7 - xInBlock); 34 | yInBlock = abs(isVFlip * 7 - yInBlock); 35 | 36 | charAddr += ((screenEntry & 0x3FF) << 6) + (yInBlock << 3); 37 | charAddr += xInBlock; 38 | 39 | int palAddr = readBg8(charAddr); 40 | if (palAddr == 0) { 41 | discard; 42 | } 43 | palAddr *= 2; 44 | 45 | bool useExtPal = (dispCnt & (1 << 30)) != 0; 46 | if (useExtPal) { 47 | int slot = bgNum < 2 && (bgCnt & (1 << 13)) != 0 ? bgNum + 2 : bgNum; 48 | palAddr += slot * 8192 + ((screenEntry & 0xF000) >> 3); 49 | int color = readExtPal16Aligned(palAddr); 50 | return vec4(normRgb5(color), 1.0); 51 | } else { 52 | int color = readPal16Aligned(palAddr); 53 | return vec4(normRgb5(color), 1.0); 54 | } 55 | } 56 | 57 | void main() { 58 | int bgNum = int(screenPos.z); 59 | 60 | int winEnabled = int(texture(winTex, screenPosF).x * 255.0); 61 | if ((winEnabled & (1 << bgNum)) == 0) { 62 | discard; 63 | } 64 | 65 | int x = int(screenPos.x); 66 | int y = int(screenPos.y); 67 | 68 | color = drawText(x, y, bgNum); 69 | 70 | int priority = bgCnt & 3; 71 | color.a = float(priority) / 4.0; 72 | } 73 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/bg_vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | in vec4 position; 4 | out vec3 screenPos; 5 | out vec2 screenPosF; 6 | out vec2 affineDims; 7 | 8 | void main() { 9 | float normX = position.x * 0.5 + 0.5; 10 | screenPos = vec3(max(normX * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0), position.z); 11 | screenPosF = vec2(normX, position.y / 192.0); 12 | affineDims = vec2(0.0, 0.0); 13 | gl_Position = vec4(position.x, 1.0 - position.y / 192.0 * 2.0, 0.0, 1.0); 14 | } 15 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/bg_vert_affine_extended.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | in vec4 position; 4 | out vec3 screenPos; 5 | out vec2 screenPosF; 6 | out vec2 affineDims; 7 | 8 | uniform int bgCnt; 9 | 10 | void main() { 11 | float size = float(128 << ((bgCnt >> 14) & 0x3)); 12 | affineDims = vec2(size, size); 13 | 14 | float normX = position.x * 0.5 + 0.5; 15 | screenPos = vec3(max(normX * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0), position.z); 16 | screenPosF = vec2(normX, position.y / 192.0); 17 | gl_Position = vec4(position.x, 1.0 - position.y / 192.0 * 2.0, 0.0, 1.0); 18 | } 19 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/bg_vert_bitmap.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | in vec4 position; 4 | out vec3 screenPos; 5 | out vec2 screenPosF; 6 | out vec2 affineDims; 7 | 8 | uniform int bgCnt; 9 | 10 | const vec2 BitMapSizeLookup[4] = vec2[4](vec2(128.0, 128.0), vec2(256.0, 256.0), vec2(512.0, 256.0), vec2(512.0, 512.0)); 11 | 12 | void main() { 13 | int size = (bgCnt >> 14) & 0x3; 14 | affineDims = BitMapSizeLookup[size]; 15 | 16 | float normX = position.x * 0.5 + 0.5; 17 | screenPos = vec3(max(normX * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0), position.z); 18 | screenPosF = vec2(normX, position.y / 192.0); 19 | gl_Position = vec4(position.x, 1.0 - position.y / 192.0 * 2.0, 0.0, 1.0); 20 | } 21 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/blend_frag.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision mediump float; 4 | 5 | layout(location = 0) out vec4 color; 6 | 7 | in vec2 screenPos; 8 | 9 | uniform sampler2D bg0Tex; 10 | uniform sampler2D bg1Tex; 11 | uniform sampler2D bg2Tex; 12 | uniform sampler2D bg3Tex; 13 | uniform sampler2D objTex; 14 | uniform sampler2D objDepthTex; 15 | uniform sampler2D winTex; 16 | 17 | uniform BlendUbo { 18 | int bldCnts[192]; 19 | int bldAlphas[192]; 20 | int bldYs[192]; 21 | }; 22 | 23 | int topNum = 5; 24 | int bottomNum = 5; 25 | // Priority of 1.0 indicates a discarded fragment 26 | // Due to float imprecision priority must be < 0.9 27 | // Lowest priority is 1.0 / 4.0 28 | float topPrio = 0.9; 29 | float bottomPrio = 0.9; 30 | vec4 topColor = vec4(0.0, 0.0, 0.0, 0.0); 31 | vec4 bottomColor = vec4(0.0, 0.0, 0.0, 0.0); 32 | 33 | void sortObjPrio() { 34 | float prio = texture(objDepthTex, screenPos).r * 2.0 - 1.0; 35 | if (prio < topPrio) { 36 | topNum = 4; 37 | // Give obj a priority boost due to float imprecision 38 | topPrio = prio - 0.1; 39 | topColor = texture(objTex, screenPos); 40 | } 41 | } 42 | 43 | void sortBgPrio(int num, sampler2D bgTex) { 44 | vec4 texColor = texture(bgTex, screenPos); 45 | if (texColor.a < topPrio) { 46 | bottomNum = topNum; 47 | bottomPrio = topPrio; 48 | bottomColor = topColor; 49 | 50 | topNum = num; 51 | topPrio = texColor.a; 52 | topColor = texColor; 53 | } else if (texColor.a < bottomPrio) { 54 | bottomNum = num; 55 | bottomPrio = texColor.a; 56 | bottomColor = texColor; 57 | } 58 | } 59 | 60 | vec4 alphaBlend(int bldAlpha) { 61 | int eva = bldAlpha & 0x1F; 62 | int evb = (bldAlpha >> 8) & 0x1F; 63 | float evaF = min(float(eva) / 16.0, 1.0); 64 | float evbF = min(float(evb) / 16.0, 1.0); 65 | vec3 blendedColor = topColor.rgb * evaF + bottomColor.rgb * evbF; 66 | return vec4(blendedColor.rgb, 1.0); 67 | } 68 | 69 | void main() { 70 | sortObjPrio(); 71 | sortBgPrio(0, bg0Tex); 72 | sortBgPrio(1, bg1Tex); 73 | sortBgPrio(2, bg2Tex); 74 | sortBgPrio(3, bg3Tex); 75 | 76 | if (topNum == 5) { 77 | discard; 78 | } 79 | 80 | if (topNum == 4 && topColor.a == 0.0) { 81 | color = vec4(topColor.rgb, 1.0); 82 | // Semi transparent object 83 | int y = int(screenPos.y * 191.0); 84 | int bldCnt = bldCnts[y]; 85 | bool blendBottom = ((bldCnt >> 8) & (1 << bottomNum)) != 0; 86 | if (blendBottom) { 87 | color = alphaBlend(bldAlphas[y]); 88 | return; 89 | } 90 | 91 | int bldMode = (bldCnt >> 6) & 3; 92 | if (bldMode < 2) { 93 | color = vec4(topColor.rgb, 1.0); 94 | return; 95 | } 96 | } 97 | 98 | int winEnabled = int(texture(winTex, screenPos).x * 255.0); 99 | if ((winEnabled & (1 << 5)) == 0) { 100 | color = vec4(topColor.rgb, 1.0); 101 | return; 102 | } 103 | 104 | int y = int(screenPos.y * 191.0); 105 | 106 | int bldCnt = bldCnts[y]; 107 | int bldMode = (bldCnt >> 6) & 3; 108 | 109 | if (bldMode == 0) { 110 | color = vec4(topColor.rgb, 1.0); 111 | return; 112 | } 113 | 114 | bool blendTop = (bldCnt & (1 << topNum)) != 0; 115 | if (!blendTop) { 116 | color = vec4(topColor.rgb, 1.0); 117 | return; 118 | } 119 | 120 | switch (bldMode) { 121 | case 1: { 122 | bool blendBottom = ((bldCnt >> 8) & (1 << bottomNum)) != 0; 123 | if (!blendBottom) { 124 | color = vec4(topColor.rgb, 1.0); 125 | return; 126 | } 127 | color = alphaBlend(bldAlphas[y]); 128 | break; 129 | } 130 | case 2: { 131 | int bldY = bldYs[y]; 132 | float bldYF = float(bldY) / 16.0; 133 | vec3 increaseColor = (1.0 - topColor.rgb) * bldYF; 134 | color = vec4((topColor.rgb + increaseColor), 1.0); 135 | break; 136 | } 137 | case 3: { 138 | int bldY = bldYs[y]; 139 | float bldYF = float(bldY) / 16.0; 140 | vec3 decreaseColor = topColor.rgb * bldYF; 141 | color = vec4((topColor.rgb - decreaseColor), 1.0); 142 | break; 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/blend_vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | in vec4 position; 4 | out vec2 screenPos; 5 | 6 | void main() { 7 | screenPos = vec2(position.x * 0.5 + 0.5, position.y * 0.5 + 0.5); 8 | gl_Position = vec4(position.xy, 0.0, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/obj_frag.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | precision highp int; 5 | 6 | layout(location = 0) out vec4 color; 7 | 8 | in vec3 objPos; 9 | flat in ivec2 objDims; 10 | in vec2 screenPosF; 11 | 12 | uniform int dispCnt; 13 | uniform ObjUbo { 14 | int mapWidths[128]; 15 | int objBounds[128]; 16 | }; 17 | 18 | uniform sampler2D oamTex; 19 | uniform sampler2D objTex; 20 | uniform sampler2D palTex; 21 | uniform sampler2D extPalTex; 22 | uniform sampler2D winTex; 23 | 24 | int readOam16Aligned(int addr) { 25 | float x = float(addr >> 2) / 255.0f; 26 | vec4 value = texture(oamTex, vec2(x, 1.0)); 27 | int entry = addr & 2; 28 | return int(value[entry] * 255.0) | (int(value[entry + 1] * 255.0) << 8); 29 | } 30 | 31 | int readObj8(int addr) { 32 | int addrX = (addr >> 2) & 0x1FF; 33 | int addrY = addr >> 11; 34 | float x = float(addrX) / 511.0; 35 | float y = float(addrY) / (OBJ_TEX_HEIGHT - 1.0); 36 | return int(texture(objTex, vec2(x, y))[addr & 3] * 255.0); 37 | } 38 | 39 | int readObj16Aligned(int addr) { 40 | int addrX = (addr >> 2) & 0x1FF; 41 | int addrY = addr >> 11; 42 | float x = float(addrX) / 511.0; 43 | float y = float(addrY) / (OBJ_TEX_HEIGHT - 1.0); 44 | vec4 value = texture(objTex, vec2(x, y)); 45 | int entry = addr & 2; 46 | return int(value[entry] * 255.0) | (int(value[entry + 1] * 255.0) << 8); 47 | } 48 | 49 | int readPal16Aligned(int addr) { 50 | float x = float(addr >> 2) / 255.0; 51 | vec4 value = texture(palTex, vec2(x, 1.0)); 52 | int entry = addr & 2; 53 | return int(value[entry] * 255.0) | (int(value[entry + 1] * 255.0) << 8); 54 | } 55 | 56 | int readExtPal16Aligned(int addr) { 57 | int addrX = (addr >> 2) & 0x1FF; 58 | int addrY = addr >> 11; 59 | float x = float(addrX) / 511.0; 60 | float y = float(addrY) / 3.0; 61 | vec4 value = texture(extPalTex, vec2(x, y)); 62 | int entry = addr & 2; 63 | return int(value[entry] * 255.0) | (int(value[entry + 1] * 255.0) << 8); 64 | } 65 | 66 | vec3 normRgb5(int color) { 67 | return vec3(float(color & 0x1F), float((color >> 5) & 0x1F), float((color >> 10) & 0x1F)) / 31.0; 68 | } 69 | 70 | vec4 drawSprite(int objX, int objY, int attrib0, int oamIndex) { 71 | int mapWidth = mapWidths[oamIndex]; 72 | int objBound = objBounds[oamIndex]; 73 | 74 | int attrib2 = readOam16Aligned(oamIndex * 8 + 4); 75 | 76 | int tileIndex = attrib2 & 0x3FF; 77 | int tileAddr = tileIndex * objBound; 78 | 79 | bool is8bpp = (attrib0 & (1 << 13)) != 0; 80 | if (is8bpp) { 81 | tileAddr += ((objY & 7) + (objY >> 3) * mapWidth) * 8; 82 | tileAddr += (objX >> 3) * 64 + (objX & 7); 83 | 84 | int palIndex = readObj8(tileAddr); 85 | if (palIndex == 0) { 86 | discard; 87 | } 88 | 89 | bool useExtPal = (dispCnt & (1 << 31)) != 0; 90 | if (useExtPal) { 91 | int palBaseAddr = (attrib2 & 0xF000) >> 3; 92 | int palColor = readExtPal16Aligned(palBaseAddr + palIndex * 2); 93 | return vec4(normRgb5(palColor), 1.0); 94 | } else { 95 | int palColor = readPal16Aligned(0x200 + palIndex * 2); 96 | return vec4(normRgb5(palColor), 1.0); 97 | } 98 | } else { 99 | tileAddr += ((objY & 7) + (objY >> 3) * mapWidth) * 4; 100 | tileAddr += (objX >> 3) * 32 + (objX & 7) / 2; 101 | 102 | int palIndex = readObj8(tileAddr); 103 | palIndex >>= 4 * (objX & 1); 104 | palIndex &= 0xF; 105 | if (palIndex == 0) { 106 | discard; 107 | } 108 | 109 | int palBank = (attrib2 >> 12) & 0xF; 110 | int palBaseAddr = 0x200 + palBank * 32; 111 | int palColor = readPal16Aligned(palBaseAddr + palIndex * 2); 112 | return vec4(normRgb5(palColor), 1.0); 113 | } 114 | } 115 | 116 | vec4 drawBitmap(int objX, int objY, int oamIndex) { 117 | int bitmapWidth = mapWidths[oamIndex]; 118 | int dataBase = objBounds[oamIndex]; 119 | 120 | int objColor = readObj16Aligned(dataBase + (objY * bitmapWidth + objX) * 2); 121 | if ((objColor & (1 << 15)) == 0) { 122 | discard; 123 | } 124 | return vec4(normRgb5(objColor), 1.0); 125 | } 126 | 127 | void main() { 128 | int objWidth = objDims.x; 129 | int objHeight = objDims.y; 130 | int objY = int(objPos.y); 131 | int objX = int(objPos.x); 132 | 133 | if (objX < 0 || objX >= objWidth || objY < 0 || objY >= objHeight) { 134 | discard; 135 | } 136 | 137 | int winEnabled = int(texture(winTex, screenPosF).x * 255.0); 138 | if ((winEnabled & (1 << 4)) == 0) { 139 | discard; 140 | } 141 | 142 | int oamIndex = int(objPos.z); 143 | 144 | int attrib0 = readOam16Aligned(oamIndex * 8); 145 | 146 | bool isBitmap = (attrib0 & 0xC00) == 0xC00; 147 | if (isBitmap) { 148 | color = drawBitmap(objX, objY, oamIndex); 149 | } else { 150 | color = drawSprite(objX, objY, attrib0, oamIndex); 151 | } 152 | 153 | bool semiTransparent = ((attrib0 >> 10) & 3) == 1; 154 | if (semiTransparent) { 155 | color.a = 0.0; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/obj_vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | in vec4 position; 4 | in float oamIndex; 5 | 6 | uniform sampler2D oamTex; 7 | 8 | out vec3 objPos; 9 | flat out ivec2 objDims; 10 | out vec2 screenPosF; 11 | 12 | uniform int dispCnt; 13 | 14 | int readOam16Aligned(int addr) { 15 | float x = float(addr >> 2) / 255.0f; 16 | vec4 value = texture(oamTex, vec2(x, 1.0)); 17 | int entry = addr & 2; 18 | return int(value[entry] * 255.0) | (int(value[entry + 1] * 255.0) << 8); 19 | } 20 | 21 | const vec2 SizeLookup[12] = vec2[12]( 22 | vec2(8.0, 8.0), vec2(16.0, 16.0), vec2(32.0, 32.0), vec2(64.0, 64.0), vec2(16.0, 8.0), vec2(32.0, 8.0), 23 | vec2(32.0, 16.0), vec2(64.0, 32.0), vec2(8.0, 16.0), vec2(8.0, 32.0), vec2(16.0, 32.0), vec2(32.0, 64.0) 24 | ); 25 | 26 | float fixedToFloat(int n) { 27 | bool sign = (n & (1 << 15)) != 0; 28 | if (sign) { 29 | n -= 1; 30 | n = ~n; 31 | n &= 0xFFFF; 32 | float f = float(n) / 256.0; 33 | return f * -1.0; 34 | } else { 35 | float f = float(n) / 256.0; 36 | return f; 37 | } 38 | } 39 | 40 | void main() { 41 | int index = int(oamIndex); 42 | 43 | int attrib0 = readOam16Aligned(index * 8); 44 | int attrib1 = readOam16Aligned(index * 8 + 2); 45 | int attrib2 = readOam16Aligned(index * 8 + 4); 46 | 47 | int oamX = attrib1 & 0x1FF; 48 | int oamY = attrib0 & 0xFF; 49 | 50 | int shape = (attrib0 >> 12) & 0xC; 51 | int size = (attrib1 >> 14) & 0x3; 52 | 53 | vec2 oamDims = SizeLookup[shape | size]; 54 | float oamWidth = oamDims.x; 55 | float oamHeight = oamDims.y; 56 | objDims = ivec2(int(oamWidth), int(oamHeight)); 57 | 58 | if (oamX >= 256) { 59 | oamX -= 512; 60 | } 61 | 62 | if (oamY >= 192) { 63 | oamY -= 256; 64 | } 65 | 66 | vec2 pos = position.xy; 67 | 68 | bool affine = (attrib0 & (1 << 8)) != 0; 69 | if (affine) { 70 | int affineIndex = (attrib1 >> 9) & 0x1F; 71 | int affineOffset = affineIndex * 0x20; 72 | int pa = readOam16Aligned(affineOffset + 6); 73 | int pb = readOam16Aligned(affineOffset + 14); 74 | int pc = readOam16Aligned(affineOffset + 22); 75 | int pd = readOam16Aligned(affineOffset + 30); 76 | mat2 m = mat2(fixedToFloat(pa), fixedToFloat(pb), fixedToFloat(pc), fixedToFloat(pd)); 77 | 78 | bool doubleAffine = (attrib0 & (1 << 9)) != 0; 79 | if (doubleAffine) { 80 | vec2 normPos = vec2(max(oamWidth * 2.0 * pos.x - 0.9, 0.0) - oamWidth, max(oamHeight * 2.0 * pos.y - 0.9, 0.0) - oamHeight) * m; 81 | objPos = vec3(normPos.x + oamWidth / 2.0, normPos.y + oamHeight / 2.0, oamIndex); 82 | oamWidth *= 2.0; 83 | oamHeight *= 2.0; 84 | } else { 85 | vec2 normPos = vec2(max(oamWidth * pos.x - 0.5, 0.0) - oamWidth / 2.0, max(oamHeight * pos.y - 0.5, 0.0) - oamHeight / 2.0) * m; 86 | objPos = vec3(normPos.x + oamWidth / 2.0 - 0.5, normPos.y + oamHeight / 2.0, oamIndex); 87 | } 88 | } else { 89 | bool isVFlip = (attrib1 & (1 << 13)) != 0; 90 | bool isHFlip = (attrib1 & (1 << 12)) != 0; 91 | 92 | if (isVFlip) { 93 | pos.y -= 1.0; 94 | pos.y = abs(pos.y); 95 | } 96 | 97 | if (isHFlip) { 98 | pos.x -= 1.0; 99 | pos.x = abs(pos.x); 100 | } 101 | 102 | objPos = vec3((oamWidth - 0.1) * pos.x, (oamHeight - 0.1) * pos.y, oamIndex); 103 | } 104 | 105 | float x = float(oamX) + oamWidth * position.x; 106 | float y = float(oamY) + oamHeight * position.y; 107 | 108 | screenPosF = vec2(x / 256.0, y / 192.0); 109 | 110 | int priority = (attrib2 >> 10) & 3; 111 | gl_Position = vec4(screenPosF.x * 2.0 - 1.0, 1.0 - screenPosF.y * 2.0, float(priority) / 4.0, 1.0); 112 | } 113 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/rotate_frag.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision mediump float; 4 | 5 | layout(location = 0) out vec4 color; 6 | 7 | uniform sampler2D tex; 8 | in vec2 texCoords; 9 | 10 | void main() { 11 | color = texture(tex, texCoords); 12 | } 13 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/rotate_vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | 5 | layout(location = 0) in vec2 aPos; 6 | layout(location = 1) in vec2 aTexCoord; 7 | 8 | out vec2 texCoords; 9 | 10 | void main() 11 | { 12 | gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); 13 | texCoords = aTexCoord; 14 | } 15 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/vram_display_frag.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | precision highp int; 5 | 6 | layout(location = 0) out vec4 color; 7 | 8 | in vec2 screenPos; 9 | uniform int dispCnt; 10 | 11 | uniform sampler2D lcdcPalTex; 12 | 13 | int readLcdcPal16Aligned(int addr) { 14 | int addrX = (addr >> 2) & 0x1FF; 15 | int addrY = addr >> 11; 16 | float x = float(addrX) / 511.0; 17 | float y = float(addrY) / 327.0; 18 | vec4 value = texture(lcdcPalTex, vec2(x, y)); 19 | int entry = addr & 2; 20 | return int(value[entry] * 255.0) | (int(value[entry + 1] * 255.0) << 8); 21 | } 22 | 23 | vec3 normRgb5(int color) { 24 | return vec3(float(color & 0x1F), float((color >> 5) & 0x1F), float((color >> 10) & 0x1F)) / 31.0; 25 | } 26 | 27 | void main() { 28 | int x = int(screenPos.x); 29 | int y = int(screenPos.y); 30 | 31 | int addr = ((dispCnt >> 18) & 0x3) * 0x10000 + y * 256 + x; 32 | color = vec4(normRgb5(readLcdcPal16Aligned(addr * 2)), 0.0); 33 | } 34 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/vram_display_vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | in vec4 position; 4 | out vec2 screenPos; 5 | 6 | void main() { 7 | screenPos = vec2(max((position.x * 0.5 + 0.5) * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0)); 8 | // Draw upside down, since opengl starts fbo texture bottom left 9 | gl_Position = vec4(position.x, 1.0 - position.y / 192.0 * 2.0, 0.0, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/win_bg_frag.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | precision highp int; 5 | 6 | layout(location = 0) out vec4 color; 7 | 8 | in vec2 screenPos; 9 | uniform int dispCnt; 10 | 11 | uniform WinBgUbo { 12 | int winH[192 * 2]; 13 | int winV[192 * 2]; 14 | int winIn[192]; 15 | int winOut[192]; 16 | }; 17 | 18 | bool checkBounds(int x, int y, int winNum) { 19 | bool winEnabled = (dispCnt & (1 << (13 + winNum))) != 0; 20 | if (!winEnabled) { 21 | return false; 22 | } 23 | 24 | int h = winH[winNum * 192 + y]; 25 | int v = winV[winNum * 192 + y]; 26 | 27 | int winX1 = (h >> 8) & 0xFF; 28 | int winX2 = h & 0xFF; 29 | 30 | int winY1 = (v >> 8) & 0xFF; 31 | int winY2 = v & 0xFF; 32 | 33 | if (winX1 <= winX2) { 34 | if (x < winX1 || x > winX2) { 35 | return false; 36 | } 37 | } else { 38 | if (x >= winX2 && x < winX1) { 39 | return false; 40 | } 41 | } 42 | 43 | if (winY1 <= winY2) { 44 | if (y < winY1 || y > winY2) { 45 | return false; 46 | } 47 | } else { 48 | if (y >= winY2 && y < winY1) { 49 | return false; 50 | } 51 | } 52 | 53 | int enabled = (winIn[y] >> (winNum * 8)) & 0xFF; 54 | color = vec4(float(enabled) / 255.0, 0.0, 0.0, 0.0); 55 | return true; 56 | } 57 | 58 | void main() { 59 | int x = int(screenPos.x); 60 | int y = int(screenPos.y); 61 | 62 | if (!checkBounds(x, y, 0) && !checkBounds(x, y, 1)) { 63 | int enabled = winOut[y] & 0xFF; 64 | color = vec4(float(enabled) / 255.0, 0.0, 0.0, 0.0); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_2d/shaders/glsl/win_bg_vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | in vec4 position; 4 | out vec2 screenPos; 5 | 6 | void main() { 7 | screenPos = vec2(max((position.x * 0.5 + 0.5) * 256.0 - 0.1, 0.0), max(position.y - 0.1, 0.0)); 8 | // Draw upside down, since opengl starts fbo texture bottom left 9 | gl_Position = vec4(position.x, position.y / 192.0 * 2.0 - 1.0, 0.0, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_3d/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod registers_3d; 2 | pub mod renderer_3d; 3 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_3d/shaders/cg/render_vert.cg: -------------------------------------------------------------------------------- 1 | float4 out gl_Position : POSITION; 2 | float3 out oColor : TEXCOORD0; 3 | float2 out oTexCoords : TEXCOORD1; 4 | float out oPolygonIndex : TEXCOORD2; 5 | 6 | void main(float4 position, float4 color, float2 texCoords) { 7 | oColor = color.rgb; 8 | oTexCoords = texCoords; 9 | oPolygonIndex = color.a; 10 | gl_Position = position; 11 | } 12 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_3d/shaders/glsl/render_vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | in vec4 position; 4 | in vec4 color; 5 | in vec2 texCoords; 6 | 7 | out vec3 oColor; 8 | out vec2 oTexCoords; 9 | out float oPolygonIndex; 10 | 11 | void main() { 12 | oColor = color.rgb; 13 | oTexCoords = texCoords; 14 | oPolygonIndex = color.a; 15 | gl_Position = position; 16 | } 17 | -------------------------------------------------------------------------------- /src/core/graphics/gpu_mem_buf.rs: -------------------------------------------------------------------------------- 1 | use crate::core::memory::mem::Memory; 2 | use crate::core::memory::vram::Vram; 3 | use crate::core::memory::{regions, vram}; 4 | use crate::utils::HeapMemU8; 5 | use std::ops::Deref; 6 | 7 | #[derive(Default)] 8 | pub struct GpuMemBuf { 9 | vram: Vram, 10 | 11 | pub lcdc: HeapMemU8<{ vram::TOTAL_SIZE }>, 12 | 13 | pub bg_a: HeapMemU8<{ vram::BG_A_SIZE as usize }>, 14 | pub obj_a: HeapMemU8<{ vram::OBJ_A_SIZE as usize }>, 15 | pub bg_a_ext_palette: HeapMemU8<{ vram::BG_EXT_PAL_SIZE as usize }>, 16 | pub obj_a_ext_palette: HeapMemU8<{ vram::OBJ_EXT_PAL_SIZE as usize }>, 17 | 18 | pub bg_b: HeapMemU8<{ vram::BG_B_SIZE as usize }>, 19 | pub obj_b: HeapMemU8<{ vram::OBJ_B_SIZE as usize }>, 20 | pub bg_b_ext_palette: HeapMemU8<{ vram::BG_EXT_PAL_SIZE as usize }>, 21 | pub obj_b_ext_palette: HeapMemU8<{ vram::OBJ_EXT_PAL_SIZE as usize }>, 22 | 23 | pub pal_a: HeapMemU8<{ regions::STANDARD_PALETTES_SIZE as usize / 2 }>, 24 | pub pal_b: HeapMemU8<{ regions::STANDARD_PALETTES_SIZE as usize / 2 }>, 25 | pub oam_a: HeapMemU8<{ regions::OAM_SIZE as usize / 2 }>, 26 | pub oam_b: HeapMemU8<{ regions::OAM_SIZE as usize / 2 }>, 27 | 28 | pub tex_rear_plane_image: HeapMemU8<{ vram::TEX_REAR_PLANE_IMAGE_SIZE as usize }>, 29 | pub tex_pal: HeapMemU8<{ vram::TEX_PAL_SIZE as usize }>, 30 | } 31 | 32 | impl GpuMemBuf { 33 | pub fn read_vram(&mut self, vram: &mut Vram) { 34 | self.vram.cnt = vram.cnt; 35 | vram.banks.copy_dirty_sections(&mut self.vram.banks); 36 | vram.banks.reset_dirty_sections(); 37 | } 38 | 39 | pub fn read_palettes_oam(&mut self, mem: &mut Memory) { 40 | if mem.palettes.dirty { 41 | mem.palettes.dirty = false; 42 | self.pal_a.copy_from_slice(&mem.palettes.mem[..mem.palettes.mem.len() / 2]); 43 | self.pal_b.copy_from_slice(&mem.palettes.mem[mem.palettes.mem.len() / 2..]); 44 | } 45 | if mem.oam.dirty { 46 | mem.oam.dirty = false; 47 | self.oam_a.copy_from_slice(&mem.oam.mem[..mem.oam.mem.len() / 2]); 48 | self.oam_b.copy_from_slice(&mem.oam.mem[mem.oam.mem.len() / 2..]); 49 | } 50 | } 51 | 52 | pub fn rebuild_vram_maps(&mut self) { 53 | self.vram.rebuild_maps(); 54 | } 55 | 56 | pub fn read_2d(&mut self, read_lcdc: bool) { 57 | if read_lcdc { 58 | self.vram.maps.read_all_lcdc(&mut self.lcdc, self.vram.banks.mem.deref()); 59 | } 60 | 61 | self.vram.maps.read_all_bg_a(&mut self.bg_a, self.vram.banks.mem.deref()); 62 | self.vram.maps.read_all_obj_a(&mut self.obj_a, self.vram.banks.mem.deref()); 63 | self.vram.maps.read_all_bg_a_ext_palette(&mut self.bg_a_ext_palette, self.vram.banks.mem.deref()); 64 | self.vram.maps.read_all_obj_a_ext_palette(&mut self.obj_a_ext_palette, self.vram.banks.mem.deref()); 65 | 66 | self.vram.maps.read_bg_b(&mut self.bg_b, self.vram.banks.mem.deref()); 67 | self.vram.maps.read_all_obj_b(&mut self.obj_b, self.vram.banks.mem.deref()); 68 | self.vram.maps.read_all_bg_b_ext_palette(&mut self.bg_b_ext_palette, self.vram.banks.mem.deref()); 69 | self.vram.maps.read_all_obj_b_ext_palette(&mut self.obj_b_ext_palette, self.vram.banks.mem.deref()); 70 | } 71 | 72 | pub fn read_3d(&mut self) { 73 | self.vram.maps.read_all_tex_rear_plane_img(&mut self.tex_rear_plane_image, self.vram.banks.mem.deref()); 74 | self.vram.maps.read_all_tex_palette(&mut self.tex_pal, self.vram.banks.mem.deref()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/core/graphics/mod.rs: -------------------------------------------------------------------------------- 1 | mod gl_glyph; 2 | mod gl_utils; 3 | pub mod gpu; 4 | pub mod gpu_2d; 5 | pub mod gpu_3d; 6 | mod gpu_mem_buf; 7 | pub mod gpu_renderer; 8 | -------------------------------------------------------------------------------- /src/core/graphics/shaders/cg/text_frag.cg: -------------------------------------------------------------------------------- 1 | float2 in texCoords : TEXCOORD0; 2 | 3 | uniform sampler2D tex : TEXUNIT0; 4 | 5 | void main(out float4 color : COLOR) { 6 | float alpha = tex2D(tex, texCoords).r; 7 | if (alpha <= 0.0) { 8 | discard; 9 | } 10 | color = float4(1.0, 1.0, 1.0, alpha); 11 | } 12 | -------------------------------------------------------------------------------- /src/core/graphics/shaders/cg/text_vert.cg: -------------------------------------------------------------------------------- 1 | float4 out gl_Position : POSITION; 2 | float2 out texCoords : TEXCOORD0; 3 | 4 | void main(float4 position) { 5 | texCoords = position.zw; 6 | gl_Position = float4(position.x / 550.0 + 0.6, -position.y / 300.0 + 0.925, 0.0, 1.0); 7 | } 8 | -------------------------------------------------------------------------------- /src/core/graphics/shaders/glsl/text_frag.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision mediump float; 4 | 5 | layout(location = 0) out vec4 color; 6 | 7 | uniform sampler2D tex; 8 | in vec2 texCoords; 9 | 10 | void main() { 11 | float alpha = texture(tex, texCoords).r; 12 | if (alpha <= 0.0) { 13 | discard; 14 | } 15 | color = vec4(1.0, 1.0, 1.0, alpha); 16 | } 17 | -------------------------------------------------------------------------------- /src/core/graphics/shaders/glsl/text_vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | in vec4 position; 4 | out vec2 texCoords; 5 | 6 | void main() { 7 | texCoords = position.zw; 8 | gl_Position = vec4(position.x / 550.0 + 0.6, -position.y / 300.0 + 0.925, 0.0, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /src/core/hle/bios_lookup_table.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::hle::bios::*; 3 | use crate::core::CpuType::{ARM7, ARM9}; 4 | 5 | pub const ARM9_SWI_LOOKUP_TABLE: [(&str, fn(&mut Emu)); 33] = [ 6 | ("unknown", unknown::<{ ARM9 }>), 7 | ("unknown", unknown::<{ ARM9 }>), 8 | ("unknown", unknown::<{ ARM9 }>), 9 | ("wait_by_loop", wait_by_loop::<{ ARM9 }>), 10 | ("interrupt_wait", interrupt_wait::<{ ARM9 }>), 11 | ("v_blank_intr_wait", v_blank_intr_wait::<{ ARM9 }>), 12 | ("halt", halt::<{ ARM9 }>), 13 | ("unknown", unknown::<{ ARM9 }>), 14 | ("unknown", unknown::<{ ARM9 }>), 15 | ("divide", divide::<{ ARM9 }>), 16 | ("unknown", unknown::<{ ARM9 }>), 17 | ("cpu_set", cpu_set::<{ ARM9 }>), 18 | ("cpu_fast_set", cpu_fast_set::<{ ARM9 }>), 19 | ("square_root", square_root::<{ ARM9 }>), 20 | ("get_crc16", get_crc16::<{ ARM9 }>), 21 | ("is_debugger", is_debugger::<{ ARM9 }>), 22 | ("bit_unpack", bit_unpack::<{ ARM9 }>), 23 | ("lz77_uncomp", lz77_uncomp::<{ ARM9 }>), 24 | ("lz77_uncomp", lz77_uncomp::<{ ARM9 }>), 25 | ("huff_uncomp", huff_uncomp::<{ ARM9 }>), 26 | ("runlen_uncomp", runlen_uncomp::<{ ARM9 }>), 27 | ("runlen_uncomp", runlen_uncomp::<{ ARM9 }>), 28 | ("diff_unfilt8", diff_unfilt8::<{ ARM9 }>), 29 | ("unknown", unknown::<{ ARM9 }>), 30 | ("diff_unfilt16", diff_unfilt16::<{ ARM9 }>), 31 | ("unknown", unknown::<{ ARM9 }>), 32 | ("unknown", unknown::<{ ARM9 }>), 33 | ("unknown", unknown::<{ ARM9 }>), 34 | ("unknown", unknown::<{ ARM9 }>), 35 | ("unknown", unknown::<{ ARM9 }>), 36 | ("unknown", unknown::<{ ARM9 }>), 37 | ("unknown", unknown::<{ ARM9 }>), 38 | ("unknown", unknown::<{ ARM9 }>), 39 | ]; 40 | 41 | pub const ARM7_SWI_LOOKUP_TABLE: [(&str, fn(&mut Emu)); 33] = [ 42 | ("unknown", unknown::<{ ARM7 }>), 43 | ("unknown", unknown::<{ ARM7 }>), 44 | ("unknown", unknown::<{ ARM7 }>), 45 | ("wait_by_loop", wait_by_loop::<{ ARM7 }>), 46 | ("interrupt_wait", interrupt_wait::<{ ARM7 }>), 47 | ("v_blank_intr_wait", v_blank_intr_wait::<{ ARM7 }>), 48 | ("halt", halt::<{ ARM7 }>), 49 | ("sleep", sleep::<{ ARM7 }>), 50 | ("sound_bias", sound_bias::<{ ARM7 }>), 51 | ("divide", divide::<{ ARM7 }>), 52 | ("unknown", unknown::<{ ARM7 }>), 53 | ("cpu_set", cpu_set::<{ ARM7 }>), 54 | ("cpu_fast_set", cpu_fast_set::<{ ARM7 }>), 55 | ("square_root", square_root::<{ ARM7 }>), 56 | ("get_crc16", get_crc16::<{ ARM7 }>), 57 | ("is_debugger", is_debugger::<{ ARM7 }>), 58 | ("bit_unpack", bit_unpack::<{ ARM7 }>), 59 | ("lz77_uncomp", lz77_uncomp::<{ ARM7 }>), 60 | ("lz77_uncomp", lz77_uncomp::<{ ARM7 }>), 61 | ("huff_uncomp", huff_uncomp::<{ ARM7 }>), 62 | ("runlen_uncomp", runlen_uncomp::<{ ARM7 }>), 63 | ("runlen_uncomp", runlen_uncomp::<{ ARM7 }>), 64 | ("unknown", unknown::<{ ARM7 }>), 65 | ("unknown", unknown::<{ ARM7 }>), 66 | ("unknown", unknown::<{ ARM7 }>), 67 | ("unknown", unknown::<{ ARM7 }>), 68 | ("get_sine_table", get_sine_table::<{ ARM7 }>), 69 | ("get_pitch_table", get_pitch_table::<{ ARM7 }>), 70 | ("get_volume_table", get_volume_table::<{ ARM7 }>), 71 | ("unknown", unknown::<{ ARM7 }>), 72 | ("unknown", unknown::<{ ARM7 }>), 73 | ("unknown", unknown::<{ ARM7 }>), 74 | ("unknown", unknown::<{ ARM7 }>), 75 | ]; 76 | -------------------------------------------------------------------------------- /src/core/hle/cart_hle.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::hle::arm7_hle::IpcFifoTag; 3 | use crate::core::CpuType::ARM7; 4 | use crate::logging::debug_println; 5 | 6 | pub(super) struct CartHle { 7 | cmd: u32, 8 | data_pos: u32, 9 | buffer: u32, 10 | } 11 | 12 | impl CartHle { 13 | pub(super) fn new() -> Self { 14 | CartHle { cmd: 0, data_pos: 0, buffer: 0 } 15 | } 16 | } 17 | 18 | impl Emu { 19 | pub(super) fn cart_hle_ipc_recv(&mut self, data: u32) { 20 | if self.hle.cart.data_pos == 0 { 21 | self.hle.cart.cmd = data; 22 | } 23 | 24 | match self.hle.cart.cmd { 25 | 0 => { 26 | if self.hle.cart.data_pos == 1 { 27 | self.hle.cart.buffer = data; 28 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Filesystem, 0x1, true); 29 | self.hle.cart.data_pos = 0; 30 | return; 31 | } 32 | } 33 | 2 => { 34 | if self.cartridge.io.save_file_size == 0 { 35 | let save_param = self.mem_read::<{ ARM7 }, u32>(self.hle.cart.buffer + 0x4); 36 | let save_size_shift = (save_param >> 8) & 0xFF; 37 | self.cartridge.io.resize_save_file(1 << save_size_shift); 38 | } 39 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Filesystem, 0x1, true); 40 | self.hle.cart.data_pos = 0; 41 | return; 42 | } 43 | 6 => { 44 | let offset = self.mem_read::<{ ARM7 }, u32>(self.hle.cart.buffer + 0x0C); 45 | let dst = self.mem_read::<{ ARM7 }, u32>(self.hle.cart.buffer + 0x10); 46 | let len = self.mem_read::<{ ARM7 }, u32>(self.hle.cart.buffer + 0x14); 47 | 48 | for i in 0..len { 49 | self.mem_write::<{ ARM7 }, u8>(dst + i, self.cartridge.io.read_save_buf((offset + i) & (self.cartridge.io.save_file_size - 1))); 50 | } 51 | 52 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Filesystem, 0x1, true); 53 | self.hle.cart.data_pos = 0; 54 | return; 55 | } 56 | 7 | 8 => { 57 | let src = self.mem_read::<{ ARM7 }, u32>(self.hle.cart.buffer + 0x0C); 58 | let offset = self.mem_read::<{ ARM7 }, u32>(self.hle.cart.buffer + 0x10); 59 | let len = self.mem_read::<{ ARM7 }, u32>(self.hle.cart.buffer + 0x14); 60 | 61 | let mut buf = vec![0xFF; len as usize]; 62 | for i in 0..len { 63 | buf[i as usize] = self.mem_read::<{ ARM7 }, u8>(src + i); 64 | } 65 | let cartridge_io = &mut self.cartridge.io; 66 | cartridge_io.write_save_buf_slice(offset & (cartridge_io.save_file_size - 1), &buf); 67 | 68 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Filesystem, 0x1, true); 69 | self.hle.cart.data_pos = 0; 70 | return; 71 | } 72 | 9 => { 73 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Filesystem, 0x1, true); 74 | self.hle.cart.data_pos = 0; 75 | return; 76 | } 77 | _ => debug_println!("cart save: unknown cmd {:x}", self.hle.cart.cmd), 78 | } 79 | 80 | self.hle.cart.data_pos += 1; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/core/hle/firmware_hle.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::hle::arm7_hle::IpcFifoTag; 3 | use crate::core::spi; 4 | use crate::core::CpuType::ARM7; 5 | 6 | pub(super) struct FirmwareHle { 7 | data: [u16; 16], 8 | } 9 | 10 | impl FirmwareHle { 11 | pub(super) fn new() -> Self { 12 | FirmwareHle { data: [0; 16] } 13 | } 14 | } 15 | 16 | impl Emu { 17 | pub(super) fn firmware_hle_ipc_recv(&mut self, data: u32) { 18 | let firmware = &mut self.hle.firmware; 19 | 20 | if data & (1 << 25) != 0 { 21 | firmware.data.fill(0); 22 | } 23 | 24 | firmware.data[((data >> 16) & 0xF) as usize] = data as u16; 25 | 26 | if data & (1 << 24) == 0 { 27 | return; 28 | } 29 | 30 | let cmd = (firmware.data[0] >> 8) - 0x20; 31 | match cmd { 32 | 0 => self.arm7_hle_send_ipc_fifo(IpcFifoTag::Nvram, 0x0300A000, false), 33 | 1 => self.arm7_hle_send_ipc_fifo(IpcFifoTag::Nvram, 0x0300A100, false), 34 | 2 => { 35 | let addr = (((firmware.data[0] as u32) & 0xFF) << 24) | ((firmware.data[1] as u32) << 8) | (((firmware.data[2] as u32) >> 8) & 0xFF); 36 | if (0x02000000..0x02800000).contains(&addr) { 37 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Nvram, 0x0300A200, false); 38 | } else { 39 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Nvram, 0x0300A202, false); 40 | } 41 | } 42 | 3 => { 43 | let addr = ((firmware.data[4] as u32) << 16) | firmware.data[5] as u32; 44 | if (0x02000000..0x02800000).contains(&addr) { 45 | let src = (((firmware.data[0] as u32) & 0xFF) << 16) | firmware.data[1] as u32; 46 | let len = ((firmware.data[2] as u32) << 16) | firmware.data[3] as u32; 47 | 48 | for i in 0..len { 49 | let val = spi::SPI_FIRMWARE[(src + i) as usize % spi::SPI_FIRMWARE.len()]; 50 | self.mem_write::<{ ARM7 }, _>(addr + i, val); 51 | } 52 | 53 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Nvram, 0x0300A300, false); 54 | } else { 55 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Nvram, 0x0300A302, false); 56 | } 57 | } 58 | 5 => { 59 | let addr = ((firmware.data[3] as u32) << 16) | firmware.data[4] as u32; 60 | if (0x02000000..0x02800000).contains(&addr) { 61 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Nvram, 0x0300A500, false); 62 | } else { 63 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Nvram, 0x0300A502, false); 64 | } 65 | } 66 | _ => {} 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/core/hle/mic_hle.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::hle::arm7_hle::IpcFifoTag; 3 | use crate::logging::debug_println; 4 | 5 | pub(super) struct MicHle { 6 | data: [u16; 16], 7 | } 8 | 9 | impl MicHle { 10 | pub(super) fn new() -> Self { 11 | MicHle { data: [0; 16] } 12 | } 13 | } 14 | 15 | impl Emu { 16 | pub(super) fn mic_hle_ipc_recv(&mut self, data: u32) { 17 | let mic = &mut self.hle.mic; 18 | 19 | if data & (1 << 25) != 0 { 20 | mic.data.fill(0); 21 | } 22 | 23 | mic.data[((data >> 16) & 0xF) as usize] = data as u16; 24 | 25 | if (data & (1 << 24)) == 0 { 26 | return; 27 | } 28 | 29 | let cmd = (mic.data[0] >> 8) - 0x40; 30 | match cmd { 31 | 0 => self.arm7_hle_send_ipc_fifo(IpcFifoTag::Mic, 0x0300C000, false), 32 | _ => { 33 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Mic, 0x0300C000, false); // Just send same reply, seems to work 34 | debug_println!("unknown mic request {data:x}"); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/core/hle/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod arm7_hle; 2 | pub mod bios; 3 | mod bios_lookup_table; 4 | mod cart_hle; 5 | mod firmware_hle; 6 | mod mic_hle; 7 | mod power_manager_hle; 8 | mod rtc_hle; 9 | mod sound_hle; 10 | pub mod sound_nitro; 11 | mod touchscreen_hle; 12 | pub mod wifi_hle; 13 | -------------------------------------------------------------------------------- /src/core/hle/power_manager_hle.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::hle::arm7_hle::IpcFifoTag; 3 | 4 | pub(super) struct PowerManagerHle { 5 | data: [u16; 16], 6 | } 7 | 8 | impl PowerManagerHle { 9 | pub(super) fn new() -> Self { 10 | PowerManagerHle { data: [0; 16] } 11 | } 12 | } 13 | 14 | impl Emu { 15 | pub(super) fn power_manager_hle_ipc_recv(&mut self, data: u32) { 16 | if data & (1 << 25) != 0 { 17 | self.hle.power_manager.data.fill(0); 18 | } 19 | 20 | self.hle.power_manager.data[((data >> 16) & 0xF) as usize] = data as u16; 21 | 22 | if data & (1 << 24) == 0 { 23 | return; 24 | } 25 | 26 | let cmd = (self.hle.power_manager.data[0] >> 8) - 0x60; 27 | match cmd { 28 | 1 => self.arm7_hle_send_ipc_fifo(IpcFifoTag::PowerManager, 0x0300E300, false), 29 | 3 => self.arm7_hle_send_ipc_fifo(IpcFifoTag::PowerManager, 0x0300E300, false), 30 | 4 => self.arm7_hle_send_ipc_fifo(IpcFifoTag::PowerManager, 0x03008000 | (((self.hle.power_manager.data[1] as u32 + 0x70) & 0xFF) << 8), false), 31 | 5 => self.arm7_hle_send_ipc_fifo(IpcFifoTag::PowerManager, 0x03008000 | (((self.hle.power_manager.data[1] as u32 + 0x70) & 0xFF) << 8), false), 32 | 6 => self.arm7_hle_send_ipc_fifo(IpcFifoTag::PowerManager, 0x03008000 | (((self.hle.power_manager.data[1] as u32 + 0x70) & 0xFF) << 8), false), 33 | _ => {} 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/core/hle/rtc_hle.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::hle::arm7_hle::IpcFifoTag; 3 | use crate::core::CpuType::ARM7; 4 | 5 | pub struct RtcHle; 6 | 7 | impl RtcHle { 8 | pub(super) fn new() -> Self { 9 | RtcHle 10 | } 11 | } 12 | 13 | impl Emu { 14 | pub fn rtc_hle_ipc_recv(&mut self, data: u32) { 15 | let cmd = (data >> 8) & 0x7F; 16 | 17 | if (2..=15).contains(&cmd) || (26..=34).contains(&cmd) || (cmd >= 42) { 18 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Rtc, 0x8001 | (cmd << 8), false); 19 | return; 20 | } 21 | 22 | match cmd { 23 | 0x10 => { 24 | // read date and time 25 | self.rtc.update_date_time(); 26 | for i in 0..7 { 27 | self.mem_write::<{ ARM7 }, _>(0x027FFDE8 + i, self.rtc.date_time[i as usize]); 28 | } 29 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Rtc, 0x9000, false); 30 | } 31 | 0x11 => { 32 | // read date 33 | self.rtc.update_date_time(); 34 | for i in 0..4 { 35 | self.mem_write::<{ ARM7 }, _>(0x027FFDE8 + i, self.rtc.date_time[i as usize]); 36 | } 37 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Rtc, 0x9100, false); 38 | } 39 | 0x12 => { 40 | // read time 41 | self.rtc.update_date_time(); 42 | for i in 4..7 { 43 | self.mem_write::<{ ARM7 }, _>(0x027FFDE8 + i, self.rtc.date_time[i as usize]); 44 | } 45 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Rtc, 0x9200, false); 46 | } 47 | _ => {} 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/core/hle/sound_hle.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::hle::sound_nitro::SoundNitro; 3 | 4 | pub struct SoundHle { 5 | engine: i8, 6 | pub(super) nitro: SoundNitro, 7 | } 8 | 9 | impl SoundHle { 10 | pub(super) fn new() -> Self { 11 | SoundHle { 12 | engine: -1, 13 | nitro: SoundNitro::default(), 14 | } 15 | } 16 | } 17 | 18 | impl Emu { 19 | pub fn sound_hle_ipc_recv(&mut self, data: u32) { 20 | if self.hle.sound.engine == -1 { 21 | if data >= 0x02000000 { 22 | self.hle.sound.engine = 0; 23 | self.sound_nitro_reset(); 24 | } else { 25 | self.hle.sound.engine = 1; 26 | } 27 | } 28 | 29 | if self.hle.sound.engine == 0 { 30 | self.sound_nitro_ipc_recv(data); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/core/hle/touchscreen_hle.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::hle::arm7_hle::IpcFifoTag; 3 | use crate::core::CpuType::ARM7; 4 | 5 | pub struct TouchscreenHle { 6 | status: i32, 7 | data: [u16; 16], 8 | num_samples: u16, 9 | sample_pos: [u16; 4], 10 | } 11 | 12 | impl TouchscreenHle { 13 | pub(super) fn new() -> Self { 14 | TouchscreenHle { 15 | status: 0, 16 | data: [0; 16], 17 | num_samples: 0, 18 | sample_pos: [0; 4], 19 | } 20 | } 21 | } 22 | 23 | impl Emu { 24 | fn touchscreen_hle_sample(&mut self) { 25 | let mut ts = (self.mem_read::<{ ARM7 }, u16>(0x027FFFAA) as u32) | ((self.mem_read::<{ ARM7 }, u16>(0x027FFFAC) as u32) << 16); 26 | 27 | let is_pressed = self.input.get_ext_key_in() & 0x40 == 0; 28 | if is_pressed { 29 | let (x, y) = self.spi.get_touch_coordinates(); 30 | ts &= 0xF9000000; 31 | ts |= (x & 0xFFF) as u32; 32 | ts |= ((y & 0xFFF) as u32) << 12; 33 | ts |= 0x01000000; 34 | } else { 35 | ts &= 0xFE000000; 36 | ts |= 0x06000000; 37 | } 38 | 39 | self.mem_write::<{ ARM7 }, _>(0x027FFFAA, ts as u16); 40 | self.mem_write::<{ ARM7 }, _>(0x027FFFAC, (ts >> 16) as u16); 41 | } 42 | 43 | pub fn touchscreen_hle_ipc_recv(&mut self, data: u32) { 44 | let touchscreen = &mut self.hle.touchscreen; 45 | 46 | if data & (1 << 25) != 0 { 47 | touchscreen.data.fill(0); 48 | } 49 | 50 | touchscreen.data[((data >> 16) & 0xF) as usize] = data as u16; 51 | 52 | if data & (1 << 24) == 0 { 53 | return; 54 | } 55 | 56 | match touchscreen.data[0] >> 8 { 57 | 0 => { 58 | self.touchscreen_hle_sample(); 59 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Touchpanel, 0x03008000, false); 60 | } 61 | 1 => { 62 | if touchscreen.status != 0 { 63 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Touchpanel, 0x03008103, false); 64 | return; 65 | } 66 | 67 | let num = touchscreen.data[0] & 0xFF; 68 | if num == 0 || num > 4 { 69 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Touchpanel, 0x03008102, false); 70 | return; 71 | } 72 | 73 | let offset = touchscreen.data[1]; 74 | if offset >= 263 { 75 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Touchpanel, 0x03008102, false); 76 | return; 77 | } 78 | 79 | touchscreen.status = 1; 80 | 81 | touchscreen.num_samples = num; 82 | for i in 0..num { 83 | let y_pos = offset + ((i * 263 / num) % 263); 84 | touchscreen.sample_pos[i as usize] = y_pos; 85 | } 86 | 87 | touchscreen.status = 2; 88 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Touchpanel, 0x03008100, false); 89 | } 90 | 2 => { 91 | if touchscreen.status != 2 { 92 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Touchpanel, 0x03008103, false); 93 | return; 94 | } 95 | 96 | touchscreen.status = 3; 97 | touchscreen.num_samples = 0; 98 | touchscreen.status = 0; 99 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Touchpanel, 0x03008200, false); 100 | } 101 | 3 => { 102 | self.touchscreen_hle_sample(); 103 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Touchpanel, 0x03008300, false); 104 | } 105 | _ => {} 106 | } 107 | } 108 | 109 | pub(super) fn touchscreen_hle_on_scanline(&mut self, v_count: u16) { 110 | for i in 0..self.hle.touchscreen.num_samples as usize { 111 | if v_count == self.hle.touchscreen.sample_pos[i] { 112 | self.touchscreen_hle_sample(); 113 | self.arm7_hle_send_ipc_fifo(IpcFifoTag::Touchpanel, 0x03009000 | i as u32, false); 114 | break; 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/core/input.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicU32, Ordering}; 2 | use std::sync::Arc; 3 | 4 | #[repr(u8)] 5 | #[derive(Copy, Clone)] 6 | pub enum Keycode { 7 | A = 0, 8 | B = 1, 9 | Select = 2, 10 | Start = 3, 11 | Right = 4, 12 | Left = 5, 13 | Up = 6, 14 | Down = 7, 15 | TriggerR = 8, 16 | TriggerL = 9, 17 | X = 10, 18 | Y = 11, 19 | } 20 | 21 | pub struct Input { 22 | key_input: u16, 23 | ext_key_in: u16, 24 | key_map: Arc, 25 | } 26 | 27 | impl Input { 28 | pub fn new(key_map: Arc) -> Self { 29 | Input { 30 | key_input: 0x3FF, 31 | ext_key_in: 0x007F, 32 | key_map, 33 | } 34 | } 35 | 36 | pub fn get_key_input(&self) -> u16 { 37 | let key_map = self.key_map.load(Ordering::Relaxed); 38 | (self.key_input & !0x3FF) | (key_map & 0x3FF) as u16 39 | } 40 | 41 | pub fn get_ext_key_in(&self) -> u16 { 42 | let key_map = self.key_map.load(Ordering::Relaxed); 43 | (self.ext_key_in & !0x43) | ((key_map >> 10) & 0x43) as u16 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/core/memory/io_arm7.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::memory::io_arm7_lut::{IoArm7ReadLut, IoArm7ReadLutUpper, IoArm7ReadLutWifi, IoArm7WriteLut, IoArm7WriteLutWifi}; 3 | use crate::utils::Convert; 4 | 5 | impl Emu { 6 | pub fn io_arm7_read(&mut self, addr_offset: u32) -> T { 7 | match addr_offset & 0xF00000 { 8 | 0x0 if IoArm7ReadLut::is_in_range(addr_offset) => T::from(IoArm7ReadLut::read(addr_offset, size_of::() as u8, self)), 9 | 0x100000 if IoArm7ReadLutUpper::is_in_range(addr_offset) => T::from(IoArm7ReadLutUpper::read(addr_offset, size_of::() as u8, self)), 10 | 0x800000 if IoArm7ReadLutWifi::is_in_range(addr_offset) => T::from(IoArm7ReadLutWifi::read(addr_offset, size_of::() as u8, self)), 11 | _ => T::from(0), 12 | } 13 | } 14 | 15 | pub fn io_arm7_write(&mut self, addr_offset: u32, value: T) { 16 | match addr_offset & 0xF00000 { 17 | 0x0 if IoArm7WriteLut::is_in_range(addr_offset) => IoArm7WriteLut::write(value.into(), addr_offset, size_of::() as u8, self), 18 | 0x800000 if IoArm7WriteLutWifi::is_in_range(addr_offset) => IoArm7WriteLutWifi::write(value.into(), addr_offset, size_of::() as u8, self), 19 | _ => {} 20 | } 21 | } 22 | 23 | pub fn io_arm7_write_fixed_slice(&mut self, addr_offset: u32, slice: &[T]) { 24 | match addr_offset & 0xF00000 { 25 | 0x0 if IoArm7WriteLut::is_in_range(addr_offset) => IoArm7WriteLut::write_fixed_slice(addr_offset, slice, self), 26 | 0x800000 if IoArm7WriteLutWifi::is_in_range(addr_offset) => IoArm7WriteLutWifi::write_fixed_slice(addr_offset, slice, self), 27 | _ => {} 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/core/memory/io_arm9.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::memory::io_arm9_lut::{IoArm9ReadLut, IoArm9ReadLutUpper, IoArm9WriteLut}; 3 | use crate::utils::Convert; 4 | use std::intrinsics::likely; 5 | 6 | impl Emu { 7 | pub fn io_arm9_read(&mut self, addr_offset: u32) -> T { 8 | match addr_offset & 0xF00000 { 9 | 0x0 if IoArm9ReadLut::is_in_range(addr_offset) => T::from(IoArm9ReadLut::read(addr_offset, size_of::() as u8, self)), 10 | 0x100000 if IoArm9ReadLutUpper::is_in_range(addr_offset) => T::from(IoArm9ReadLutUpper::read(addr_offset, size_of::() as u8, self)), 11 | _ => T::from(0), 12 | } 13 | } 14 | 15 | pub fn io_arm9_write(&mut self, addr_offset: u32, value: T) { 16 | if likely(IoArm9WriteLut::is_in_range(addr_offset)) { 17 | IoArm9WriteLut::write(value.into(), addr_offset, size_of::() as u8, self); 18 | } 19 | } 20 | 21 | pub fn io_arm9_write_fixed_slice(&mut self, addr_offset: u32, slice: &[T]) { 22 | if likely(IoArm9WriteLut::is_in_range(addr_offset)) { 23 | IoArm9WriteLut::write_fixed_slice(addr_offset, slice, self); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/core/memory/main.rs: -------------------------------------------------------------------------------- 1 | use crate::core::memory::regions; 2 | use crate::utils; 3 | use crate::utils::{Convert, HeapMemU8}; 4 | use std::ops::{Deref, DerefMut}; 5 | 6 | pub struct Main { 7 | main: HeapMemU8<{ regions::MAIN_SIZE as usize }>, 8 | } 9 | 10 | impl Main { 11 | pub fn new() -> Self { 12 | Main { main: HeapMemU8::new() } 13 | } 14 | 15 | pub fn get_ptr(&self, addr: u32) -> *const u8 { 16 | unsafe { self.main.as_ptr().add((addr & (regions::MAIN_SIZE - 1)) as usize) } 17 | } 18 | 19 | pub fn read(&self, addr_offset: u32) -> T { 20 | utils::read_from_mem(self.main.deref(), addr_offset & (regions::MAIN_SIZE - 1)) 21 | } 22 | 23 | pub fn write(&mut self, addr_offset: u32, value: T) { 24 | utils::write_to_mem(self.main.deref_mut(), addr_offset & (regions::MAIN_SIZE - 1), value); 25 | } 26 | 27 | pub fn write_slice(&mut self, addr_offset: u32, slice: &[T]) { 28 | utils::write_to_mem_slice(self.main.deref_mut(), (addr_offset & (regions::MAIN_SIZE - 1)) as usize, slice); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/core/memory/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cartridge; 2 | pub mod dma; 3 | pub mod io_arm7; 4 | mod io_arm7_lut; 5 | pub mod io_arm9; 6 | mod io_arm9_lut; 7 | pub mod main; 8 | pub mod mem; 9 | pub mod mmu; 10 | pub mod oam; 11 | pub mod palettes; 12 | pub mod regions; 13 | pub mod vram; 14 | mod wifi; 15 | pub mod wram; 16 | -------------------------------------------------------------------------------- /src/core/memory/oam.rs: -------------------------------------------------------------------------------- 1 | use crate::core::memory::regions; 2 | use crate::utils; 3 | use crate::utils::HeapMemU8; 4 | use bilge::prelude::*; 5 | use std::mem; 6 | 7 | pub struct Oam { 8 | pub mem: HeapMemU8<{ regions::OAM_SIZE as usize }>, 9 | pub dirty: bool, 10 | } 11 | 12 | impl Oam { 13 | pub fn new() -> Self { 14 | Oam { mem: HeapMemU8::new(), dirty: false } 15 | } 16 | 17 | pub fn read(&self, addr_offset: u32) -> T { 18 | utils::read_from_mem(self.mem.as_slice(), addr_offset & (regions::OAM_SIZE - 1)) 19 | } 20 | 21 | pub fn read_slice(&self, addr_offset: u32, slice: &mut [T]) { 22 | utils::read_from_mem_slice(self.mem.as_slice(), addr_offset & (regions::OAM_SIZE - 1), slice); 23 | } 24 | 25 | pub fn write(&mut self, addr_offset: u32, value: T) { 26 | self.dirty = true; 27 | utils::write_to_mem(self.mem.as_mut_slice(), addr_offset & (regions::OAM_SIZE - 1), value) 28 | } 29 | 30 | pub fn write_slice(&mut self, addr_offset: u32, slice: &[T]) { 31 | self.dirty = true; 32 | utils::write_to_mem_slice(self.mem.as_mut_slice(), (addr_offset & (regions::OAM_SIZE - 1)) as usize, slice); 33 | } 34 | 35 | pub fn write_memset(&mut self, addr_offset: u32, value: T, size: usize) { 36 | self.dirty = true; 37 | utils::write_memset(self.mem.as_mut_slice(), (addr_offset & (regions::OAM_SIZE - 1)) as usize, value, size) 38 | } 39 | } 40 | 41 | #[repr(u8)] 42 | #[derive(Debug, Eq, PartialEq)] 43 | pub enum OamObjMode { 44 | Normal = 0, 45 | Affine = 1, 46 | Disabled = 2, 47 | AffineDouble = 3, 48 | } 49 | 50 | impl From for OamObjMode { 51 | fn from(value: u8) -> Self { 52 | debug_assert!(value <= OamObjMode::AffineDouble as u8); 53 | unsafe { mem::transmute(value) } 54 | } 55 | } 56 | 57 | #[repr(u8)] 58 | #[derive(Debug, Eq, PartialEq)] 59 | pub enum OamGfxMode { 60 | Normal = 0, 61 | AlphaBlending = 1, 62 | Window = 2, 63 | Bitmap = 3, 64 | } 65 | 66 | impl From for OamGfxMode { 67 | fn from(value: u8) -> Self { 68 | debug_assert!(value <= OamGfxMode::Bitmap as u8); 69 | unsafe { mem::transmute(value) } 70 | } 71 | } 72 | 73 | #[bitsize(16)] 74 | #[derive(FromBits)] 75 | pub struct OamAttrib0 { 76 | pub y: u8, 77 | obj_mode: u2, 78 | gfx_mode: u2, 79 | pub is_mosaic: bool, 80 | pub is_8bit: bool, 81 | pub shape: u2, 82 | } 83 | 84 | impl OamAttrib0 { 85 | pub fn get_obj_mode(&self) -> OamObjMode { 86 | OamObjMode::from(u8::from(self.obj_mode())) 87 | } 88 | 89 | pub fn get_gfx_mode(&self) -> OamGfxMode { 90 | OamGfxMode::from(u8::from(self.gfx_mode())) 91 | } 92 | } 93 | 94 | #[bitsize(16)] 95 | #[derive(FromBits)] 96 | pub struct OamAttrib1 { 97 | pub x: u9, 98 | pub affine_index: u3, 99 | pub flip: u2, 100 | pub size: u2, 101 | } 102 | 103 | #[bitsize(16)] 104 | #[derive(FromBits)] 105 | pub struct OamAttrib2 { 106 | pub tile_index: u10, 107 | pub priority: u2, 108 | pub pal_bank: u4, 109 | } 110 | 111 | #[repr(C)] 112 | pub struct OamAttribs { 113 | pub attr0: u16, 114 | pub attr1: u16, 115 | pub attr2: u16, 116 | fill: i16, 117 | } 118 | -------------------------------------------------------------------------------- /src/core/memory/palettes.rs: -------------------------------------------------------------------------------- 1 | use crate::core::memory::regions; 2 | use crate::utils; 3 | use crate::utils::{Convert, HeapMemU8}; 4 | 5 | pub struct Palettes { 6 | pub mem: HeapMemU8<{ regions::STANDARD_PALETTES_SIZE as usize }>, 7 | pub dirty: bool, 8 | } 9 | 10 | impl Palettes { 11 | pub fn new() -> Self { 12 | Palettes { mem: HeapMemU8::new(), dirty: false } 13 | } 14 | 15 | pub fn read(&self, addr_offset: u32) -> T { 16 | utils::read_from_mem(self.mem.as_slice(), addr_offset & (regions::STANDARD_PALETTES_SIZE - 1)) 17 | } 18 | 19 | pub fn read_slice(&self, addr_offset: u32, slice: &mut [T]) { 20 | utils::read_from_mem_slice(self.mem.as_slice(), addr_offset & (regions::STANDARD_PALETTES_SIZE - 1), slice); 21 | } 22 | 23 | pub fn write(&mut self, addr_offset: u32, value: T) { 24 | self.dirty = true; 25 | utils::write_to_mem(self.mem.as_mut_slice(), addr_offset & (regions::STANDARD_PALETTES_SIZE - 1), value); 26 | } 27 | 28 | pub fn write_slice(&mut self, addr_offset: u32, slice: &[T]) { 29 | self.dirty = true; 30 | utils::write_to_mem_slice(self.mem.as_mut_slice(), (addr_offset & (regions::STANDARD_PALETTES_SIZE - 1)) as usize, slice); 31 | } 32 | 33 | pub fn write_memset(&mut self, addr_offset: u32, value: T, size: usize) { 34 | self.dirty = true; 35 | utils::write_memset(self.mem.as_mut_slice(), (addr_offset & (regions::STANDARD_PALETTES_SIZE - 1)) as usize, value, size) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/core/memory/regions.rs: -------------------------------------------------------------------------------- 1 | use crate::core::memory::mmu::MMU_PAGE_SIZE; 2 | use crate::mmap::MemRegion; 3 | 4 | pub const ITCM_OFFSET: u32 = 0x00000000; 5 | pub const ITCM_OFFSET2: u32 = 0x01000000; 6 | pub const ITCM_SIZE: u32 = 32 * 1024; 7 | pub const DTCM_SIZE: u32 = 16 * 1024; 8 | 9 | pub const MAIN_OFFSET: u32 = 0x02000000; 10 | pub const MAIN_SIZE: u32 = 4 * 1024 * 1024; 11 | 12 | pub const SHARED_WRAM_OFFSET: u32 = 0x03000000; 13 | pub const SHARED_WRAM_SIZE: u32 = 32 * 1024; 14 | 15 | pub const ARM7_WRAM_OFFSET: u32 = 0x03800000; 16 | pub const ARM7_WRAM_SIZE: u32 = 64 * 1024; 17 | 18 | pub const IO_PORTS_OFFSET: u32 = 0x04000000; 19 | pub const WIFI_IO_OFFSET: u32 = 0x04800000; 20 | pub const WIFI_RAM_OFFSET: u32 = 0x04804000; 21 | pub const WIFI_RAM_OFFSET2: u32 = 0x04808000; 22 | pub const WIFI_RAM_SIZE: u32 = 8 * 1024; 23 | 24 | pub const STANDARD_PALETTES_OFFSET: u32 = 0x05000000; 25 | pub const STANDARD_PALETTES_SIZE: u32 = 2 * 1024; 26 | 27 | pub const VRAM_OFFSET: u32 = 0x06000000; 28 | 29 | pub const OAM_OFFSET: u32 = 0x07000000; 30 | pub const OAM_SIZE: u32 = 2 * 1024; 31 | 32 | pub const GBA_ROM_OFFSET: u32 = 0x08000000; 33 | pub const GBA_ROM_SIZE: u32 = 32 * 1024 * 1024; 34 | pub const GBA_ROM_OFFSET2: u32 = 0x09000000; 35 | pub const GBA_RAM_OFFSET: u32 = 0x0A000000; 36 | pub const GBA_RAM_SIZE: u32 = 64 * 1024; 37 | 38 | pub const ARM9_BIOS_OFFSET: u32 = 0xFFFF0000; 39 | pub const ARM9_BIOS_SIZE: u32 = 32 * 1024; 40 | pub const ARM7_BIOS_OFFSET: u32 = 0x00000000; 41 | pub const ARM7_BIOS_SIZE: u32 = 16 * 1024; 42 | 43 | pub const TOTAL_MEM_SIZE: u32 = 16 * 1024 /* Some padding for mmu */ 44 | + ITCM_SIZE + DTCM_SIZE + MAIN_SIZE + SHARED_WRAM_SIZE + ARM7_WRAM_SIZE + WIFI_RAM_SIZE 45 | + 16 * 1024 /* GBA ROM/RAM, filled with 0xFF */ 46 | + 16 * 1024 /* Both BIOSES, filled with 0x0 */ 47 | ; 48 | 49 | const P_ITCM_OFFSET: usize = 16 * 1024; 50 | const P_DTCM_OFFSET: usize = P_ITCM_OFFSET + ITCM_SIZE as usize; 51 | const P_MAIN_OFFSET: usize = P_DTCM_OFFSET + DTCM_SIZE as usize; 52 | const P_SHARED_WRAM_OFFSET: usize = P_MAIN_OFFSET + MAIN_SIZE as usize; 53 | const P_ARM7_WRAM_OFFSET: usize = P_SHARED_WRAM_OFFSET + SHARED_WRAM_SIZE as usize; 54 | const P_WIFI_RAM_OFFSET: usize = P_ARM7_WRAM_OFFSET + ARM7_WRAM_SIZE as usize; 55 | const P_GBA_ROM_OFFSET: usize = P_WIFI_RAM_OFFSET + WIFI_RAM_SIZE as usize; 56 | const P_ARM9_BIOS_OFFSET: usize = P_GBA_ROM_OFFSET + MMU_PAGE_SIZE; 57 | const P_ARM7_BIOS_OFFSET: usize = P_ARM9_BIOS_OFFSET; 58 | 59 | pub const ITCM_REGION: MemRegion = MemRegion::new(ITCM_OFFSET as usize, MAIN_OFFSET as usize, ITCM_SIZE as usize, P_ITCM_OFFSET, true); 60 | pub const DTCM_REGION: MemRegion = MemRegion::new(0, 0, DTCM_SIZE as usize, P_DTCM_OFFSET, true); 61 | pub const MAIN_REGION: MemRegion = MemRegion::new(MAIN_OFFSET as usize, SHARED_WRAM_OFFSET as usize, MAIN_SIZE as usize, P_MAIN_OFFSET, true); 62 | pub const SHARED_WRAM_REGION: MemRegion = MemRegion::new(0, 0, SHARED_WRAM_SIZE as usize, P_SHARED_WRAM_OFFSET, true); 63 | pub const SHARED_WRAM_ARM7_REGION: MemRegion = MemRegion::new(SHARED_WRAM_OFFSET as usize, ARM7_WRAM_OFFSET as usize, SHARED_WRAM_SIZE as usize, P_SHARED_WRAM_OFFSET, true); 64 | pub const ARM7_WRAM_REGION: MemRegion = MemRegion::new(ARM7_WRAM_OFFSET as usize, IO_PORTS_OFFSET as usize, ARM7_WRAM_SIZE as usize, P_ARM7_WRAM_OFFSET, true); 65 | pub const GBA_ROM_REGION: MemRegion = MemRegion::new(GBA_ROM_OFFSET as usize, GBA_RAM_OFFSET as usize, MMU_PAGE_SIZE, P_GBA_ROM_OFFSET, false); 66 | pub const GBA_RAM_REGION: MemRegion = MemRegion::new(GBA_RAM_OFFSET as usize, 0x0B000000, MMU_PAGE_SIZE, P_GBA_ROM_OFFSET, false); 67 | pub const ARM9_BIOS_REGION: MemRegion = MemRegion::new(0x0F000000, 0x10000000, MMU_PAGE_SIZE, P_ARM9_BIOS_OFFSET, false); 68 | pub const ARM7_BIOS_REGION: MemRegion = MemRegion::new(ARM7_BIOS_OFFSET as usize, MAIN_OFFSET as usize, MMU_PAGE_SIZE, P_ARM7_BIOS_OFFSET, false); 69 | pub const WIFI_REGION: MemRegion = MemRegion::new(WIFI_RAM_OFFSET as usize, (WIFI_RAM_OFFSET + WIFI_RAM_SIZE) as usize, WIFI_RAM_SIZE as usize, P_WIFI_RAM_OFFSET, true); 70 | pub const WIFI_MIRROR_REGION: MemRegion = MemRegion::new(WIFI_RAM_OFFSET2 as usize, (WIFI_RAM_OFFSET2 + WIFI_RAM_SIZE) as usize, WIFI_RAM_SIZE as usize, P_WIFI_RAM_OFFSET, true); 71 | 72 | pub const V_MEM_ARM9_RANGE: u32 = 0x10000000; 73 | pub const V_MEM_ARM7_RANGE: u32 = 0x0B000000; 74 | -------------------------------------------------------------------------------- /src/core/memory/wifi.rs: -------------------------------------------------------------------------------- 1 | use crate::core::memory::regions; 2 | use crate::utils; 3 | use crate::utils::{Convert, HeapMemU8}; 4 | 5 | pub struct Wifi { 6 | pub mem: HeapMemU8<{ regions::WIFI_RAM_SIZE as usize }>, 7 | } 8 | 9 | impl Wifi { 10 | pub fn new() -> Self { 11 | Wifi { mem: HeapMemU8::new() } 12 | } 13 | 14 | pub fn get_ptr(&self, addr: u32) -> *const u8 { 15 | unsafe { self.mem.as_ptr().add((addr & (regions::WIFI_RAM_SIZE - 1)) as usize) } 16 | } 17 | 18 | pub fn read(&self, addr_offset: u32) -> T { 19 | utils::read_from_mem(self.mem.as_slice(), addr_offset & (regions::WIFI_RAM_SIZE - 1)) 20 | } 21 | 22 | pub fn read_slice(&self, addr_offset: u32, slice: &mut [T]) { 23 | utils::read_from_mem_slice(self.mem.as_slice(), addr_offset & (regions::WIFI_RAM_SIZE - 1), slice); 24 | } 25 | 26 | pub fn write(&mut self, addr_offset: u32, value: T) { 27 | utils::write_to_mem(self.mem.as_mut_slice(), addr_offset & (regions::WIFI_RAM_SIZE - 1), value); 28 | } 29 | 30 | pub fn write_slice(&mut self, addr_offset: u32, slice: &[T]) { 31 | utils::write_to_mem_slice(self.mem.as_mut_slice(), (addr_offset & (regions::WIFI_RAM_SIZE - 1)) as usize, slice); 32 | } 33 | 34 | pub fn write_memset(&mut self, addr_offset: u32, value: T, size: usize) { 35 | utils::write_memset(self.mem.as_mut_slice(), (addr_offset & (regions::WIFI_RAM_SIZE - 1)) as usize, value, size) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/core/memory/wram.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::memory::regions; 3 | use crate::core::CpuType; 4 | use crate::core::CpuType::ARM7; 5 | use std::hint::unreachable_unchecked; 6 | use CpuType::ARM9; 7 | 8 | const ARM7_WRAM_SHM_OFFSET: usize = regions::ARM7_WRAM_REGION.shm_offset; 9 | const ARM7_WRAM_LEN: usize = regions::ARM7_WRAM_REGION.size; 10 | 11 | struct SharedWramMap { 12 | shm_offset: usize, 13 | size: usize, 14 | } 15 | 16 | impl SharedWramMap { 17 | fn new(shm_offset: usize, size: usize) -> SharedWramMap { 18 | SharedWramMap { shm_offset, size } 19 | } 20 | } 21 | 22 | impl Default for SharedWramMap { 23 | fn default() -> Self { 24 | SharedWramMap { shm_offset: usize::MAX, size: 1 } 25 | } 26 | } 27 | 28 | pub struct Wram { 29 | pub cnt: u8, 30 | arm9_map: SharedWramMap, 31 | arm7_map: SharedWramMap, 32 | } 33 | 34 | impl Wram { 35 | pub fn new() -> Self { 36 | let mut instance = Wram { 37 | cnt: 0, 38 | arm9_map: SharedWramMap::default(), 39 | arm7_map: SharedWramMap::default(), 40 | }; 41 | instance.init_maps(); 42 | instance 43 | } 44 | 45 | fn init_maps(&mut self) { 46 | const SHARED_OFFSET: usize = regions::SHARED_WRAM_REGION.shm_offset; 47 | const SHARED_LEN: usize = regions::SHARED_WRAM_SIZE as usize; 48 | 49 | match self.cnt { 50 | 0 => { 51 | self.arm9_map = SharedWramMap::new(SHARED_OFFSET, SHARED_LEN); 52 | self.arm7_map = SharedWramMap::new(ARM7_WRAM_SHM_OFFSET, ARM7_WRAM_LEN); 53 | } 54 | 1 => { 55 | self.arm9_map = SharedWramMap::new(SHARED_OFFSET + SHARED_LEN / 2, SHARED_LEN / 2); 56 | self.arm7_map = SharedWramMap::new(SHARED_OFFSET, SHARED_LEN / 2); 57 | } 58 | 2 => { 59 | self.arm9_map = SharedWramMap::new(SHARED_OFFSET, SHARED_LEN / 2); 60 | self.arm7_map = SharedWramMap::new(SHARED_OFFSET + SHARED_LEN / 2, SHARED_LEN / 2); 61 | } 62 | 3 => { 63 | self.arm9_map = SharedWramMap::default(); 64 | self.arm7_map = SharedWramMap::new(SHARED_OFFSET, SHARED_LEN); 65 | } 66 | _ => unsafe { unreachable_unchecked() }, 67 | } 68 | } 69 | 70 | pub fn get_shm_offset(&self, addr: u32) -> usize { 71 | match CPU { 72 | ARM9 => self.arm9_map.shm_offset + (addr as usize & (self.arm9_map.size - 1)), 73 | ARM7 => { 74 | if addr & regions::ARM7_WRAM_OFFSET == regions::ARM7_WRAM_OFFSET { 75 | ARM7_WRAM_SHM_OFFSET + (addr as usize & (ARM7_WRAM_LEN - 1)) 76 | } else { 77 | self.arm7_map.shm_offset + (addr as usize & (self.arm7_map.size - 1)) 78 | } 79 | } 80 | } 81 | } 82 | } 83 | 84 | impl Emu { 85 | pub fn wram_set_cnt(&mut self, value: u8) { 86 | self.mem.wram.cnt = value & 0x3; 87 | self.mem.wram.init_maps(); 88 | 89 | self.jit.invalidate_wram(); 90 | 91 | self.mmu_update_wram::<{ ARM9 }>(); 92 | self.mmu_update_wram::<{ ARM7 }>(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/core/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::core::CpuType::{ARM7, ARM9}; 2 | use std::marker::ConstParamTy; 3 | use std::ops; 4 | use std::ops::{Index, IndexMut}; 5 | 6 | pub mod cp15; 7 | pub mod cpu_regs; 8 | pub mod cycle_manager; 9 | mod div_sqrt; 10 | pub mod emu; 11 | pub mod exception_handler; 12 | pub mod graphics; 13 | pub mod hle; 14 | pub mod input; 15 | pub mod ipc; 16 | pub mod memory; 17 | pub mod rtc; 18 | pub mod spi; 19 | pub mod spu; 20 | pub mod thread_regs; 21 | pub mod timers; 22 | mod wifi; 23 | 24 | #[derive(ConstParamTy, Copy, Clone, Debug, Eq, PartialEq)] 25 | #[repr(u8)] 26 | pub enum CpuType { 27 | ARM9 = 0, 28 | ARM7 = 1, 29 | } 30 | 31 | impl CpuType { 32 | pub const fn other(self) -> Self { 33 | match self { 34 | ARM9 => ARM7, 35 | ARM7 => ARM9, 36 | } 37 | } 38 | } 39 | 40 | impl From for CpuType { 41 | fn from(value: bool) -> Self { 42 | match value { 43 | false => ARM9, 44 | true => ARM7, 45 | } 46 | } 47 | } 48 | 49 | impl From for CpuType { 50 | fn from(value: u8) -> Self { 51 | CpuType::from(value != 0) 52 | } 53 | } 54 | 55 | impl ops::Not for CpuType { 56 | type Output = Self; 57 | 58 | fn not(self) -> Self::Output { 59 | self.other() 60 | } 61 | } 62 | 63 | impl Index for [T; 2] { 64 | type Output = T; 65 | 66 | fn index(&self, index: CpuType) -> &Self::Output { 67 | &self[index as usize] 68 | } 69 | } 70 | 71 | impl IndexMut for [T; 2] { 72 | fn index_mut(&mut self, index: CpuType) -> &mut Self::Output { 73 | &mut self[index as usize] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/core/rtc.rs: -------------------------------------------------------------------------------- 1 | use crate::logging::debug_println; 2 | use crate::IS_DEBUG; 3 | use bilge::prelude::*; 4 | use chrono::{Datelike, Timelike}; 5 | 6 | #[bitsize(8)] 7 | #[derive(FromBits)] 8 | struct RtcReg { 9 | data_io: bool, 10 | clock_out: bool, 11 | select_out: bool, 12 | not_used: u1, 13 | data_dir_write: bool, 14 | clock_dir_write: bool, 15 | select_dir_write: bool, 16 | not_used1: u1, 17 | } 18 | 19 | #[derive(Default)] 20 | pub struct Rtc { 21 | rtc: u8, 22 | select_out: bool, 23 | clock_out: bool, 24 | data_io: bool, 25 | write_count: u8, 26 | cmd: u8, 27 | cnt: u8, 28 | pub date_time: [u8; 7], 29 | } 30 | 31 | impl Rtc { 32 | pub fn new() -> Self { 33 | Rtc::default() 34 | } 35 | 36 | pub fn get_rtc(&self) -> u8 { 37 | let mut reg = RtcReg::from(self.rtc); 38 | 39 | let cs = self.select_out; 40 | let sck = !reg.clock_dir_write() && self.clock_out; 41 | let sio = !reg.data_dir_write() && self.data_io; 42 | 43 | reg.set_select_out(cs); 44 | reg.set_clock_out(sck); 45 | reg.set_data_io(sio); 46 | 47 | u8::from(reg) 48 | } 49 | 50 | pub fn set_rtc(&mut self, value: u8) { 51 | self.rtc = value & !0x7; 52 | 53 | let dir_reg = RtcReg::from(self.rtc); 54 | let data_reg = RtcReg::from(value); 55 | 56 | let cs = if dir_reg.select_dir_write() { data_reg.select_out() } else { self.select_out }; 57 | let sck = if dir_reg.clock_dir_write() { !data_reg.clock_out() } else { self.clock_out }; 58 | let sio = if dir_reg.data_dir_write() { data_reg.data_io() } else { self.data_io }; 59 | 60 | self.update_rtc(cs, sck, sio); 61 | } 62 | 63 | fn update_rtc(&mut self, select_out: bool, clock_out: bool, mut data_io: bool) { 64 | if select_out { 65 | if !self.clock_out && clock_out { 66 | if self.write_count < 8 { 67 | self.cmd |= (data_io as u8) << (7 - self.write_count); 68 | 69 | if self.write_count == 7 && (self.cmd & 0xF0) != 0x60 { 70 | self.cmd = self.cmd.reverse_bits(); 71 | } 72 | } else if self.cmd & 1 == 1 { 73 | data_io = self.read_reg((self.cmd >> 1) & 0x7); 74 | } else { 75 | self.write_reg((self.cmd >> 1) & 0x7, data_io); 76 | } 77 | self.write_count += 1; 78 | } 79 | } else { 80 | self.write_count = 0; 81 | self.cmd = 0; 82 | } 83 | 84 | self.select_out = select_out; 85 | self.clock_out = clock_out; 86 | self.data_io = data_io; 87 | } 88 | 89 | fn reset(&mut self) { 90 | self.update_rtc(false, false, false); 91 | self.cnt = 0; 92 | self.rtc = 0; 93 | } 94 | 95 | fn read_reg(&mut self, index: u8) -> bool { 96 | match index { 97 | 0 => { 98 | self.reset(); 99 | false 100 | } 101 | 1 => (self.cnt >> (self.write_count & 7)) & 1 == 1, 102 | 2 => { 103 | if self.write_count == 8 { 104 | self.update_date_time(); 105 | } 106 | (self.date_time[(self.write_count / 8 - 1) as usize] >> (self.write_count & 7)) & 1 == 1 107 | } 108 | 3 => { 109 | if self.write_count == 8 { 110 | self.update_date_time(); 111 | } 112 | (self.date_time[(self.write_count / 8 + 3) as usize] >> (self.write_count & 7)) & 1 == 1 113 | } 114 | _ => { 115 | debug_println!("Read from unknown rtc register: {}", index); 116 | false 117 | } 118 | } 119 | } 120 | 121 | fn write_reg(&mut self, index: u8, value: bool) { 122 | match index { 123 | 0 => { 124 | if (self.write_count & 7 == 0) && value { 125 | self.reset(); 126 | } else if ((1 << (self.write_count & 7)) & 0xE) != 0 { 127 | self.cnt = (self.cnt & !(1 << (self.write_count & 7))) | ((value as u8) << (self.write_count & 7)); 128 | } 129 | } 130 | _ => { 131 | debug_println!("Write to unknown rtc register: {}", index); 132 | } 133 | } 134 | } 135 | 136 | pub fn update_date_time(&mut self) { 137 | let (year, month, day, weekday, hour, is_pm, min, sec) = if IS_DEBUG { 138 | (2000, 1, 1, 0, 11, false, 0, 0) 139 | } else { 140 | let local_now = chrono::Local::now(); 141 | 142 | let year = local_now.year() as u32 % 100; 143 | let month = local_now.month() as u8; 144 | let day = local_now.day() as u8; 145 | let weekday = local_now.weekday() as u8; 146 | let (hour, is_pm) = { 147 | let hour = local_now.hour(); 148 | ((if self.cnt & 0x2 == 0 { hour % 12 } else { hour }) as u8, hour >= 12) 149 | }; 150 | let min = local_now.minute() as u8; 151 | let sec = local_now.second() as u8; 152 | 153 | (year, month, day, weekday, hour, is_pm, min, sec) 154 | }; 155 | 156 | self.date_time[0] = (((year / 10) << 4) | (year % 10)) as u8; 157 | self.date_time[1] = ((month / 10) << 4) | (month % 10); 158 | self.date_time[2] = ((day / 10) << 4) | (day % 10); 159 | self.date_time[3] = ((weekday / 10) << 4) | (weekday % 10); 160 | self.date_time[4] = ((hour / 10) << 4) | (hour % 10); 161 | self.date_time[4] |= (is_pm as u8) << 6; 162 | self.date_time[5] = ((min / 10) << 4) | (min % 10); 163 | self.date_time[6] = ((sec / 10) << 4) | (sec % 10); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/core/timers.rs: -------------------------------------------------------------------------------- 1 | use crate::core::cpu_regs::InterruptFlag; 2 | use crate::core::cycle_manager::EventType; 3 | use crate::core::emu::Emu; 4 | use crate::core::CpuType; 5 | use crate::core::CpuType::{ARM7, ARM9}; 6 | use bilge::prelude::*; 7 | 8 | const CHANNEL_COUNT: usize = 4; 9 | const TIME_OVERFLOW: u32 = 0x10000; 10 | 11 | #[bitsize(16)] 12 | #[derive(FromBits)] 13 | struct TimerCntH { 14 | prescaler: u2, 15 | count_up: u1, 16 | not_used: u3, 17 | irq_enable: u1, 18 | start: u1, 19 | not_used1: u8, 20 | } 21 | 22 | impl TimerCntH { 23 | fn is_count_up(&self, channel_num: usize) -> bool { 24 | channel_num != 0 && bool::from(self.count_up()) 25 | } 26 | } 27 | 28 | #[derive(Copy, Clone, Default)] 29 | struct TimerChannel { 30 | cnt_l: u16, 31 | cnt_h: u16, 32 | current_value: u16, 33 | current_shift: u8, 34 | id: u16, 35 | scheduled_cycle: u64, 36 | } 37 | 38 | impl TimerChannel { 39 | fn increment_id(&mut self) { 40 | self.id += 1; 41 | self.id &= 0x3FF; 42 | } 43 | } 44 | 45 | pub struct Timers { 46 | channels: [TimerChannel; CHANNEL_COUNT], 47 | } 48 | 49 | impl Timers { 50 | pub fn new() -> Self { 51 | Timers { 52 | channels: [TimerChannel::default(); CHANNEL_COUNT], 53 | } 54 | } 55 | } 56 | 57 | impl Emu { 58 | pub fn timers_get_cnt_l(&mut self, cpu: CpuType, channel_num: usize) -> u16 { 59 | let timers = &mut self.timers[cpu]; 60 | let channel = &mut timers.channels[channel_num]; 61 | let cnt = TimerCntH::from(channel.cnt_h); 62 | if bool::from(cnt.start()) && !cnt.is_count_up(channel_num) { 63 | let current_cycle_count = self.cm.get_cycles(); 64 | let diff = channel.scheduled_cycle.wrapping_sub(current_cycle_count); 65 | channel.current_value = (TIME_OVERFLOW - (diff >> channel.current_shift) as u32) as u16; 66 | } 67 | channel.current_value 68 | } 69 | 70 | pub fn timers_get_cnt_h(&self, cpu: CpuType, channel_num: usize) -> u16 { 71 | self.timers[cpu].channels[channel_num].cnt_h 72 | } 73 | 74 | pub fn timers_set_cnt_l(&mut self, cpu: CpuType, channel_num: usize, mask: u16, value: u16) { 75 | let timers = &mut self.timers[cpu]; 76 | timers.channels[channel_num].cnt_l = (timers.channels[channel_num].cnt_l & !mask) | (value & mask); 77 | } 78 | 79 | pub fn timers_set_cnt_h(&mut self, cpu: CpuType, channel_num: usize, mut mask: u16, value: u16) { 80 | let timers = &mut self.timers[cpu]; 81 | let channel = &mut timers.channels[channel_num]; 82 | let current_cnt = TimerCntH::from(channel.cnt_h); 83 | 84 | mask &= 0xC7; 85 | channel.cnt_h = (channel.cnt_h & !mask) | (value & mask); 86 | let cnt = TimerCntH::from(channel.cnt_h); 87 | 88 | let mut update = if !bool::from(current_cnt.start()) && bool::from(cnt.start()) { 89 | channel.current_value = channel.cnt_l; 90 | true 91 | } else { 92 | false 93 | }; 94 | 95 | if (mask & 0xFF) != 0 { 96 | let shift = if u8::from(cnt.prescaler()) == 0 || cnt.is_count_up(channel_num) { 97 | 0 98 | } else { 99 | 4 + (u8::from(cnt.prescaler()) << 1) 100 | }; 101 | if channel.current_shift != shift { 102 | channel.current_shift = shift; 103 | update = true; 104 | } 105 | } 106 | 107 | if update && bool::from(cnt.start()) && !cnt.is_count_up(channel_num) { 108 | let remaining_cycles = (TIME_OVERFLOW - channel.current_value as u32) << channel.current_shift; 109 | channel.scheduled_cycle = self.cm.get_cycles() + remaining_cycles as u64; 110 | channel.increment_id(); 111 | self.cm.schedule( 112 | remaining_cycles, 113 | match cpu { 114 | ARM9 => EventType::TimerArm9, 115 | ARM7 => EventType::TimerArm7, 116 | }, 117 | (channel.id << 2) | channel_num as u16, 118 | ) 119 | } 120 | } 121 | 122 | fn timers_overflow(&mut self, channel_num: usize, cpu: CpuType) { 123 | { 124 | let channel = &mut self.timers[cpu].channels[channel_num]; 125 | let cnt = TimerCntH::from(channel.cnt_h); 126 | if !bool::from(cnt.start()) { 127 | return; 128 | } 129 | channel.current_value = channel.cnt_l; 130 | if !cnt.is_count_up(channel_num) { 131 | let remaining_cycles = (TIME_OVERFLOW - channel.current_value as u32) << channel.current_shift; 132 | channel.scheduled_cycle = self.cm.get_cycles() + remaining_cycles as u64; 133 | channel.increment_id(); 134 | self.cm.schedule( 135 | remaining_cycles, 136 | match cpu { 137 | ARM9 => EventType::TimerArm9, 138 | ARM7 => EventType::TimerArm7, 139 | }, 140 | (channel.id << 2) | channel_num as u16, 141 | ) 142 | } 143 | 144 | if bool::from(cnt.irq_enable()) { 145 | self.cpu_send_interrupt(cpu, InterruptFlag::from(InterruptFlag::Timer0Overflow as u8 + channel_num as u8)); 146 | } 147 | } 148 | if channel_num < 3 { 149 | let mut overflow = false; 150 | { 151 | let channel = &mut self.timers[cpu].channels[channel_num]; 152 | let cnt = TimerCntH::from(channel.cnt_h); 153 | if bool::from(cnt.count_up()) { 154 | channel.current_value += 1; 155 | overflow = channel.current_value == 0; 156 | } 157 | } 158 | if overflow { 159 | self.timers_overflow(channel_num + 1, cpu); 160 | } 161 | } 162 | } 163 | 164 | pub fn timers_on_overflow_event(&mut self, id_channel_num: u16) { 165 | let channel_num = id_channel_num & 0x3; 166 | let id = id_channel_num >> 2; 167 | if id == self.timers[CPU].channels[channel_num as usize].id { 168 | self.timers_overflow(channel_num as usize, CPU); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/fixed_fifo.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::HeapMem; 2 | use std::fmt::{Debug, Formatter}; 3 | 4 | pub struct FixedFifo { 5 | fifo: HeapMem, 6 | len: usize, 7 | start: usize, 8 | end: usize, 9 | } 10 | 11 | impl FixedFifo { 12 | pub fn new() -> Self { 13 | FixedFifo { 14 | fifo: unsafe { HeapMem::zeroed() }, 15 | len: 0, 16 | start: 0, 17 | end: 0, 18 | } 19 | } 20 | 21 | pub fn len(&self) -> usize { 22 | self.len 23 | } 24 | 25 | pub fn push_front(&mut self, value: T) { 26 | self.start = self.start.wrapping_sub(1) % SIZE; 27 | unsafe { *self.fifo.get_unchecked_mut(self.start) = value }; 28 | self.len += 1; 29 | } 30 | 31 | pub fn push_back(&mut self, value: T) { 32 | unsafe { *self.fifo.get_unchecked_mut(self.end) = value }; 33 | self.end = (self.end + 1) % SIZE; 34 | self.len += 1; 35 | } 36 | 37 | pub fn front(&self) -> &T { 38 | unsafe { self.fifo.get_unchecked(self.start) } 39 | } 40 | 41 | pub fn pop_front(&mut self) { 42 | self.start = (self.start + 1) % SIZE; 43 | self.len -= 1; 44 | } 45 | 46 | pub fn is_empty(&self) -> bool { 47 | self.len() == 0 48 | } 49 | 50 | pub fn is_full(&self) -> bool { 51 | self.len() == SIZE 52 | } 53 | 54 | pub fn clear(&mut self) { 55 | self.len = 0; 56 | self.start = 0; 57 | self.end = 0; 58 | } 59 | } 60 | 61 | impl Default for FixedFifo { 62 | fn default() -> Self { 63 | FixedFifo { 64 | fifo: HeapMem::default(), 65 | len: 0, 66 | start: 0, 67 | end: 0, 68 | } 69 | } 70 | } 71 | 72 | impl Debug for FixedFifo { 73 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 74 | let mut list = f.debug_list(); 75 | for i in 0..self.len() { 76 | let start = (self.start.wrapping_add(i)) % SIZE; 77 | list.entry(&self.fifo[start]); 78 | } 79 | list.finish() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/jit/assembler/arm/branch_assembler.rs: -------------------------------------------------------------------------------- 1 | use crate::jit::reg::Reg; 2 | use crate::jit::Cond; 3 | use bilge::prelude::*; 4 | 5 | #[bitsize(32)] 6 | #[derive(FromBits)] 7 | pub struct B { 8 | pub nn: u24, 9 | pub op: u1, 10 | pub id: u3, 11 | pub u4: u4, 12 | } 13 | 14 | impl B { 15 | pub fn b(imm: i32, cond: Cond) -> u32 { 16 | u32::from(B::new( 17 | // Extract first 24 bits, also keep msb 18 | u24::new((((imm << 8) >> 8) & 0xFFFFFF) as u32), 19 | u1::new(0), 20 | u3::new(0b101), 21 | u4::new(cond as u8), 22 | )) 23 | } 24 | 25 | pub fn bl(imm: i32, cond: Cond) -> u32 { 26 | u32::from(B::new( 27 | // Extract first 24 bits, also keep msb 28 | u24::new((((imm << 8) >> 8) & 0xFFFFFF) as u32), 29 | u1::new(1), 30 | u3::new(0b101), 31 | u4::new(cond as u8), 32 | )) 33 | } 34 | } 35 | 36 | #[bitsize(32)] 37 | #[derive(FromBits)] 38 | pub struct Bx { 39 | pub rn: u4, 40 | pub op: u4, 41 | pub id: u20, 42 | pub u4: u4, 43 | } 44 | 45 | impl Bx { 46 | pub fn bx(op0: Reg, cond: Cond) -> u32 { 47 | u32::from(Bx::new(u4::new(op0 as u8), u4::new(0b1), u20::new(0b00010010111111111111), u4::new(cond as u8))) 48 | } 49 | 50 | pub fn blx(op0: Reg, cond: Cond) -> u32 { 51 | u32::from(Bx::new(u4::new(op0 as u8), u4::new(0b11), u20::new(0b00010010111111111111), u4::new(cond as u8))) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/jit/assembler/arm/mod.rs: -------------------------------------------------------------------------------- 1 | use bilge::prelude::*; 2 | 3 | pub mod alu_assembler; 4 | pub mod branch_assembler; 5 | pub mod transfer_assembler; 6 | 7 | #[bitsize(32)] 8 | #[derive(FromBits)] 9 | pub struct Bkpt { 10 | imm_lower: u4, 11 | id2: u4, 12 | imm_upper: u12, 13 | id: u12, 14 | } 15 | 16 | impl Bkpt { 17 | pub fn bkpt(id: u16) -> u32 { 18 | u32::from(Bkpt::new(u4::new((id & 0xF) as u8), u4::new(0b0111), u12::new(id >> 4), u12::new(0b111000010010))) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/jit/disassembler/branch_instructions.rs: -------------------------------------------------------------------------------- 1 | mod branch_ops { 2 | use crate::jit::inst_info::{InstInfo, Operand, Operands}; 3 | use crate::jit::reg::{reg_reserve, Reg}; 4 | use crate::jit::{Cond, Op}; 5 | 6 | #[inline] 7 | pub fn bx(opcode: u32, op: Op) -> InstInfo { 8 | let op0 = Reg::from((opcode & 0xF) as u8); 9 | InstInfo::new(opcode, op, Operands::new_1(Operand::reg(op0)), reg_reserve!(op0), reg_reserve!(Reg::CPSR), 1) 10 | } 11 | 12 | #[inline] 13 | pub fn blx_reg(opcode: u32, op: Op) -> InstInfo { 14 | let op0 = Reg::from((opcode & 0xF) as u8); 15 | InstInfo::new(opcode, op, Operands::new_1(Operand::reg(op0)), reg_reserve!(op0), reg_reserve!(Reg::LR, Reg::CPSR), 1) 16 | } 17 | 18 | #[inline] 19 | pub fn b(opcode: u32, op: Op) -> InstInfo { 20 | let op0 = ((opcode << 8) as i32) >> 6; // * 4 (in steps of 4) 21 | let inst = InstInfo::new(opcode, op, Operands::new_1(Operand::imm(op0 as u32)), reg_reserve!(), reg_reserve!(), 1); 22 | // blx label 23 | if inst.cond == Cond::NV { 24 | let op0 = (((opcode << 8) as i32) >> 6) | ((opcode & (1 << 24)) >> 23) as i32; 25 | InstInfo::new( 26 | (opcode & !(0xF << 28)) | ((Cond::AL as u32) << 28), 27 | Op::Blx, 28 | Operands::new_1(Operand::imm(op0 as u32)), 29 | reg_reserve!(), 30 | reg_reserve!(Reg::LR, Reg::CPSR), 31 | 1, 32 | ) 33 | } else { 34 | inst 35 | } 36 | } 37 | 38 | #[inline] 39 | pub fn bl(opcode: u32, op: Op) -> InstInfo { 40 | let op0 = ((opcode << 8) as i32) >> 6; // * 4 (in steps of 4) 41 | let inst = InstInfo::new(opcode, op, Operands::new_1(Operand::imm(op0 as u32)), reg_reserve!(), reg_reserve!(Reg::LR), 1); 42 | // blx label 43 | if inst.cond == Cond::NV { 44 | let op0 = (((opcode << 8) as i32) >> 6) | ((opcode & (1 << 24)) >> 23) as i32; 45 | InstInfo::new( 46 | (opcode & !(0xF << 28)) | ((Cond::AL as u32) << 28), 47 | Op::Blx, 48 | Operands::new_1(Operand::imm(op0 as u32)), 49 | reg_reserve!(), 50 | reg_reserve!(Reg::LR, Reg::CPSR), 51 | 1, 52 | ) 53 | } else { 54 | inst 55 | } 56 | } 57 | 58 | #[inline] 59 | pub fn swi(opcode: u32, op: Op) -> InstInfo { 60 | InstInfo::new(opcode, op, Operands::new_empty(), reg_reserve!(), reg_reserve!(), 3) 61 | } 62 | } 63 | 64 | pub(super) use branch_ops::*; 65 | -------------------------------------------------------------------------------- /src/jit/disassembler/mod.rs: -------------------------------------------------------------------------------- 1 | mod alu_instructions; 2 | mod branch_instructions; 3 | mod delegations; 4 | pub mod lookup_table; 5 | pub mod thumb; 6 | mod transfer_instructions; 7 | -------------------------------------------------------------------------------- /src/jit/disassembler/thumb/branch_instructions_thumb.rs: -------------------------------------------------------------------------------- 1 | mod branch_thumb_ops { 2 | use crate::jit::inst_info::{Operand, Operands}; 3 | use crate::jit::inst_info_thumb::InstInfoThumb; 4 | use crate::jit::reg::{reg_reserve, Reg}; 5 | use crate::jit::Op; 6 | 7 | #[inline] 8 | pub fn bx_reg_t(opcode: u16, op: Op) -> InstInfoThumb { 9 | let op0 = Reg::from(((opcode >> 3) & 0xF) as u8); 10 | InstInfoThumb::new(opcode, op, Operands::new_1(Operand::reg(op0)), reg_reserve!(op0), reg_reserve!(Reg::CPSR), 1) 11 | } 12 | 13 | #[inline] 14 | pub fn blx_reg_t(opcode: u16, op: Op) -> InstInfoThumb { 15 | let op0 = Reg::from(((opcode >> 3) & 0xF) as u8); 16 | InstInfoThumb::new(opcode, op, Operands::new_1(Operand::reg(op0)), reg_reserve!(op0), reg_reserve!(Reg::LR, Reg::CPSR), 1) 17 | } 18 | 19 | #[inline] 20 | pub fn b_t(opcode: u16, op: Op) -> InstInfoThumb { 21 | let op0 = (opcode << 5) as i16 >> 4; // * 2 (in steps of 2) 22 | InstInfoThumb::new(opcode, op, Operands::new_1(Operand::imm(op0 as u32)), reg_reserve!(), reg_reserve!(), 1) 23 | } 24 | 25 | #[inline] 26 | fn b_cond(opcode: u16, op: Op) -> InstInfoThumb { 27 | let op0 = (((opcode & 0xFF) as i8) as i32) << 1; 28 | InstInfoThumb::new(opcode, op, Operands::new_1(Operand::imm(op0 as u32)), reg_reserve!(), reg_reserve!(), 1) 29 | } 30 | 31 | #[inline] 32 | pub fn beq_t(opcode: u16, op: Op) -> InstInfoThumb { 33 | b_cond(opcode, op) 34 | } 35 | 36 | #[inline] 37 | pub fn bne_t(opcode: u16, op: Op) -> InstInfoThumb { 38 | b_cond(opcode, op) 39 | } 40 | 41 | #[inline] 42 | pub fn bcs_t(opcode: u16, op: Op) -> InstInfoThumb { 43 | b_cond(opcode, op) 44 | } 45 | 46 | #[inline] 47 | pub fn bcc_t(opcode: u16, op: Op) -> InstInfoThumb { 48 | b_cond(opcode, op) 49 | } 50 | 51 | #[inline] 52 | pub fn bmi_t(opcode: u16, op: Op) -> InstInfoThumb { 53 | b_cond(opcode, op) 54 | } 55 | 56 | #[inline] 57 | pub fn bpl_t(opcode: u16, op: Op) -> InstInfoThumb { 58 | b_cond(opcode, op) 59 | } 60 | 61 | #[inline] 62 | pub fn bvs_t(opcode: u16, op: Op) -> InstInfoThumb { 63 | b_cond(opcode, op) 64 | } 65 | 66 | #[inline] 67 | pub fn bvc_t(opcode: u16, op: Op) -> InstInfoThumb { 68 | b_cond(opcode, op) 69 | } 70 | 71 | #[inline] 72 | pub fn bhi_t(opcode: u16, op: Op) -> InstInfoThumb { 73 | b_cond(opcode, op) 74 | } 75 | 76 | #[inline] 77 | pub fn bls_t(opcode: u16, op: Op) -> InstInfoThumb { 78 | b_cond(opcode, op) 79 | } 80 | 81 | #[inline] 82 | pub fn bge_t(opcode: u16, op: Op) -> InstInfoThumb { 83 | b_cond(opcode, op) 84 | } 85 | 86 | #[inline] 87 | pub fn blt_t(opcode: u16, op: Op) -> InstInfoThumb { 88 | b_cond(opcode, op) 89 | } 90 | 91 | #[inline] 92 | pub fn bgt_t(opcode: u16, op: Op) -> InstInfoThumb { 93 | b_cond(opcode, op) 94 | } 95 | 96 | #[inline] 97 | pub fn ble_t(opcode: u16, op: Op) -> InstInfoThumb { 98 | b_cond(opcode, op) 99 | } 100 | 101 | #[inline] 102 | pub fn bl_setup_t(opcode: u16, op: Op) -> InstInfoThumb { 103 | let op0 = ((opcode as u32) << 21) as i32 >> 9; 104 | InstInfoThumb::new(opcode, op, Operands::new_1(Operand::imm(op0 as u32)), reg_reserve!(), reg_reserve!(Reg::LR), 1) 105 | } 106 | 107 | #[inline] 108 | pub fn bl_off_t(opcode: u16, op: Op) -> InstInfoThumb { 109 | let op0 = (opcode & 0x7FF) << 1; // * 2 (in steps of 2) 110 | InstInfoThumb::new(opcode, op, Operands::new_1(Operand::imm(op0 as u32)), reg_reserve!(), reg_reserve!(Reg::LR), 1) 111 | } 112 | 113 | #[inline] 114 | pub fn blx_off_t(opcode: u16, op: Op) -> InstInfoThumb { 115 | let op0 = (opcode & 0x7FF) << 1; // * 2 (in steps of 2) 116 | InstInfoThumb::new(opcode, op, Operands::new_1(Operand::imm(op0 as u32)), reg_reserve!(), reg_reserve!(Reg::LR, Reg::CPSR), 1) 117 | } 118 | 119 | #[inline] 120 | pub fn swi_t(opcode: u16, op: Op) -> InstInfoThumb { 121 | InstInfoThumb::new(opcode, op, Operands::new_empty(), reg_reserve!(), reg_reserve!(), 3) 122 | } 123 | } 124 | 125 | pub(super) use branch_thumb_ops::*; 126 | -------------------------------------------------------------------------------- /src/jit/disassembler/thumb/delegations_thumb.rs: -------------------------------------------------------------------------------- 1 | mod unknown_delegations { 2 | use crate::jit::inst_info::Operands; 3 | use crate::jit::inst_info_thumb::InstInfoThumb; 4 | use crate::jit::reg::reg_reserve; 5 | use crate::jit::Op; 6 | 7 | #[inline] 8 | pub fn unk_t(opcode: u16, op: Op) -> InstInfoThumb { 9 | InstInfoThumb::new(opcode, op, Operands::new_empty(), reg_reserve!(), reg_reserve!(), 1) 10 | } 11 | } 12 | 13 | pub(super) use unknown_delegations::*; 14 | -------------------------------------------------------------------------------- /src/jit/disassembler/thumb/mod.rs: -------------------------------------------------------------------------------- 1 | mod alu_instructions_thumb; 2 | mod branch_instructions_thumb; 3 | mod delegations_thumb; 4 | pub mod lookup_table_thumb; 5 | mod transfer_instructions_thumb; 6 | -------------------------------------------------------------------------------- /src/jit/emitter/emit_cp15.rs: -------------------------------------------------------------------------------- 1 | use crate::core::CpuType; 2 | use crate::core::CpuType::ARM9; 3 | use crate::jit::assembler::block_asm::BlockAsm; 4 | use crate::jit::assembler::BlockReg; 5 | use crate::jit::inst_cp15_handler::{cp15_read, cp15_write}; 6 | use crate::jit::inst_cpu_regs_handler::cpu_regs_halt; 7 | use crate::jit::jit_asm::JitAsm; 8 | use crate::jit::op::Op; 9 | use crate::jit::reg::Reg; 10 | 11 | impl JitAsm<'_, CPU> { 12 | pub fn emit_halt(&mut self, block_asm: &mut BlockAsm) { 13 | block_asm.mov(Reg::PC, self.jit_buf.current_pc + 4); 14 | block_asm.save_context(); 15 | block_asm.call(cpu_regs_halt:: as *const ()); 16 | 17 | self.emit_branch_out_metadata(block_asm); 18 | block_asm.epilogue(); 19 | } 20 | 21 | pub fn emit_cp15(&mut self, block_asm: &mut BlockAsm) { 22 | if CPU != ARM9 { 23 | return; 24 | } 25 | 26 | let inst_info = self.jit_buf.current_inst(); 27 | let rd = *inst_info.operands()[0].as_reg_no_shift().unwrap(); 28 | let cn = (inst_info.opcode >> 16) & 0xF; 29 | let cm = inst_info.opcode & 0xF; 30 | let cp = (inst_info.opcode >> 5) & 0x7; 31 | 32 | let cp15_reg = (cn << 16) | (cm << 8) | cp; 33 | if cp15_reg == 0x070004 || cp15_reg == 0x070802 { 34 | self.emit_halt(block_asm); 35 | } else { 36 | let backed_up_cpsr_reg = block_asm.new_reg(); 37 | block_asm.mrs_cpsr(backed_up_cpsr_reg); 38 | match inst_info.op { 39 | Op::Mcr => block_asm.call2(cp15_write as *const (), cp15_reg, rd), 40 | Op::Mrc => { 41 | block_asm.call1(cp15_read as *const (), cp15_reg); 42 | block_asm.mov(rd, BlockReg::Fixed(Reg::R0)); 43 | } 44 | _ => unreachable!(), 45 | } 46 | block_asm.msr_cpsr(backed_up_cpsr_reg); 47 | block_asm.free_reg(backed_up_cpsr_reg); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/jit/emitter/emit_psr.rs: -------------------------------------------------------------------------------- 1 | use crate::core::CpuType; 2 | use crate::jit::assembler::block_asm::BlockAsm; 3 | use crate::jit::assembler::{BlockOperand, BlockReg}; 4 | use crate::jit::inst_info::Operand; 5 | use crate::jit::inst_thread_regs_handler::{register_set_cpsr_checked, register_set_spsr_checked}; 6 | use crate::jit::jit_asm::JitAsm; 7 | use crate::jit::op::Op; 8 | use crate::jit::reg::Reg; 9 | 10 | impl JitAsm<'_, CPU> { 11 | pub fn emit_msr(&mut self, block_asm: &mut BlockAsm) { 12 | let inst_info = self.jit_buf.current_inst(); 13 | 14 | let flags = (inst_info.opcode >> 16) & 0xF; 15 | 16 | let func = match inst_info.op { 17 | Op::MsrRc | Op::MsrIc => register_set_cpsr_checked:: as *const (), 18 | Op::MsrRs | Op::MsrIs => register_set_spsr_checked:: as *const (), 19 | _ => unreachable!(), 20 | }; 21 | 22 | block_asm.save_context(); 23 | block_asm.call2( 24 | func, 25 | match inst_info.operands()[0] { 26 | Operand::Reg { reg, shift: None } => BlockOperand::from(reg), 27 | Operand::Imm(imm) => BlockOperand::from(imm), 28 | _ => unreachable!(), 29 | }, 30 | flags, 31 | ); 32 | block_asm.msr_cpsr(BlockReg::Fixed(Reg::R0)); 33 | block_asm.restore_reg(Reg::R8); 34 | block_asm.restore_reg(Reg::R9); 35 | block_asm.restore_reg(Reg::R10); 36 | block_asm.restore_reg(Reg::R11); 37 | block_asm.restore_reg(Reg::R12); 38 | block_asm.restore_reg(Reg::SP); 39 | block_asm.restore_reg(Reg::LR); 40 | } 41 | 42 | pub fn emit_mrs(&mut self, block_asm: &mut BlockAsm) { 43 | let op = self.jit_buf.current_inst().op; 44 | let op0 = self.jit_buf.current_inst().operands()[0].as_reg_no_shift().unwrap(); 45 | block_asm.mov( 46 | *op0, 47 | match op { 48 | Op::MrsRc => Reg::CPSR, 49 | Op::MrsRs => Reg::SPSR, 50 | _ => todo!(), 51 | }, 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/jit/emitter/emit_swi.rs: -------------------------------------------------------------------------------- 1 | use crate::core::exception_handler::ExceptionVector; 2 | use crate::core::CpuType; 3 | use crate::jit::assembler::block_asm::BlockAsm; 4 | use crate::jit::inst_exception_handler::exception_handler; 5 | use crate::jit::jit_asm::JitAsm; 6 | use crate::jit::reg::Reg; 7 | 8 | impl JitAsm<'_, CPU> { 9 | pub fn emit_swi(&mut self, block_asm: &mut BlockAsm) { 10 | block_asm.save_context(); 11 | block_asm.call4( 12 | exception_handler:: as *const (), 13 | self.jit_buf.current_inst().opcode, 14 | ExceptionVector::SoftwareInterrupt as u32, 15 | self.jit_buf.current_pc, 16 | self.jit_buf.insts_cycle_counts[self.jit_buf.current_index] as u32, 17 | ); 18 | block_asm.restore_reg(Reg::R0); 19 | block_asm.restore_reg(Reg::R1); 20 | block_asm.restore_reg(Reg::R3); 21 | block_asm.restore_reg(Reg::CPSR); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/jit/emitter/mod.rs: -------------------------------------------------------------------------------- 1 | mod emit; 2 | mod emit_branch; 3 | mod emit_cp15; 4 | mod emit_psr; 5 | mod emit_swi; 6 | pub mod emit_transfer; 7 | mod thumb; 8 | -------------------------------------------------------------------------------- /src/jit/emitter/thumb/emit_alu_thumb.rs: -------------------------------------------------------------------------------- 1 | use crate::core::CpuType; 2 | use crate::jit::assembler::block_asm::BlockAsm; 3 | use crate::jit::assembler::BlockOperand; 4 | use crate::jit::inst_info::Operand; 5 | use crate::jit::jit_asm::JitAsm; 6 | use crate::jit::op::Op; 7 | use crate::jit::reg::Reg; 8 | use crate::jit::ShiftType; 9 | 10 | impl<'a, const CPU: CpuType> JitAsm<'a, CPU> { 11 | pub fn emit_alu_common_thumb(&mut self, block_asm: &mut BlockAsm) { 12 | let inst_info = self.jit_buf.current_inst(); 13 | let operands = inst_info.operands(); 14 | let op0 = *operands[0].as_reg_no_shift().unwrap(); 15 | let (op1, op2) = if operands.len() == 3 { 16 | (*operands[1].as_reg_no_shift().unwrap(), &operands[2]) 17 | } else { 18 | (op0, &operands[1]) 19 | }; 20 | 21 | match op2 { 22 | Operand::Reg { reg, shift: None } => match inst_info.op { 23 | Op::AdcDpT => block_asm.adcs_guest_thumb_pc_aligned(op0, op1, *reg), 24 | Op::AddRegT => block_asm.adds_guest_thumb_pc_aligned(op0, op1, *reg), 25 | Op::AndDpT => block_asm.ands_guest_thumb_pc_aligned(op0, op1, *reg), 26 | Op::AsrDpT => block_asm.movs_guest_thumb_pc_aligned(op0, (op0, ShiftType::Asr, *reg)), 27 | Op::BicDpT => block_asm.bics_guest_thumb_pc_aligned(op0, op1, *reg), 28 | Op::CmpDpT => block_asm.cmp_guest_thumb_pc_aligned(op0, *reg), 29 | Op::CmnDpT => block_asm.cmn_guest_thumb_pc_aligned(op0, *reg), 30 | Op::EorDpT => block_asm.eors_guest_thumb_pc_aligned(op0, op1, *reg), 31 | Op::LslDpT => block_asm.movs_guest_thumb_pc_aligned(op0, (op0, ShiftType::Lsl, *reg)), 32 | Op::LsrDpT => block_asm.movs_guest_thumb_pc_aligned(op0, (op0, ShiftType::Lsr, *reg)), 33 | Op::MulDpT => block_asm.muls_guest_thumb_pc_aligned(op0, op0, *reg), 34 | Op::MvnDpT => block_asm.mvns_guest_thumb_pc_aligned(op0, *reg), 35 | Op::NegDpT => block_asm.rsbs_guest_thumb_pc_aligned(op0, *reg, 0), 36 | Op::RorDpT => block_asm.movs_guest_thumb_pc_aligned(op0, (op0, ShiftType::Ror, *reg)), 37 | Op::SbcDpT => block_asm.sbcs_guest_thumb_pc_aligned(op0, op1, *reg), 38 | Op::SubRegT => block_asm.subs_guest_thumb_pc_aligned(op0, op1, *reg), 39 | Op::TstDpT => block_asm.tst_guest_thumb_pc_aligned(op0, *reg), 40 | Op::OrrDpT => block_asm.orrs_guest_thumb_pc_aligned(op0, op1, *reg), 41 | _ => todo!("{:?}", inst_info), 42 | }, 43 | Operand::Imm(imm) => match inst_info.op { 44 | Op::AddImm3T | Op::AddImm8T => block_asm.adds_guest_thumb_pc_aligned(op0, op1, *imm), 45 | Op::AddPcT | Op::AddSpT => block_asm.add_guest_thumb_pc_aligned(op0, op1, (*imm, ShiftType::Ror, 15)), // imm in steps of 4, ror by 15 * 2 46 | Op::AsrImmT => block_asm.movs_guest_thumb_pc_aligned(op0, (op1.into(), ShiftType::Asr, BlockOperand::from(*imm))), 47 | Op::CmpImm8T => block_asm.cmp_guest_thumb_pc_aligned(op0, *imm), 48 | Op::LslImmT => block_asm.movs_guest_thumb_pc_aligned(op0, (op1.into(), ShiftType::Lsl, BlockOperand::from(*imm))), 49 | Op::LsrImmT => block_asm.movs_guest_thumb_pc_aligned(op0, (op1.into(), ShiftType::Lsr, BlockOperand::from(*imm))), 50 | Op::MovImm8T => block_asm.movs_guest_thumb_pc_aligned(op0, *imm), 51 | Op::SubImm3T | Op::SubImm8T => block_asm.subs_guest_thumb_pc_aligned(op0, op1, *imm), 52 | _ => todo!("{:?}", inst_info), 53 | }, 54 | _ => unreachable!(), 55 | } 56 | } 57 | 58 | pub fn emit_add_sp_imm_thumb(&mut self, block_asm: &mut BlockAsm) { 59 | let inst_info = self.jit_buf.current_inst(); 60 | 61 | let imm = *inst_info.operands()[1].as_imm().unwrap(); 62 | let sub = inst_info.opcode & (1 << 7) != 0; 63 | // imm in steps of 4, ror by 15 * 2 64 | if sub { 65 | block_asm.sub(Reg::SP, Reg::SP, (imm, ShiftType::Ror, 15)); 66 | } else { 67 | block_asm.add(Reg::SP, Reg::SP, (imm, ShiftType::Ror, 15)); 68 | } 69 | } 70 | 71 | pub fn emit_add_h_thumb(&mut self, block_asm: &mut BlockAsm) { 72 | let inst_info = self.jit_buf.current_inst(); 73 | 74 | let operands = inst_info.operands(); 75 | let op0 = *operands[0].as_reg_no_shift().unwrap(); 76 | let op2 = *operands[1].as_reg_no_shift().unwrap(); 77 | 78 | block_asm.add(op0, op0, op2); 79 | } 80 | 81 | pub fn emit_cmp_h_thumb(&mut self, block_asm: &mut BlockAsm) { 82 | let inst_info = self.jit_buf.current_inst(); 83 | 84 | let operands = inst_info.operands(); 85 | let op1 = *operands[0].as_reg_no_shift().unwrap(); 86 | let op2 = *operands[1].as_reg_no_shift().unwrap(); 87 | 88 | block_asm.cmp_guest(op1, op2); 89 | } 90 | 91 | pub fn emit_movh_thumb(&mut self, block_asm: &mut BlockAsm) { 92 | let inst_info = self.jit_buf.current_inst(); 93 | 94 | let operands = inst_info.operands(); 95 | let op0 = *operands[0].as_reg_no_shift().unwrap(); 96 | let op2 = *operands[1].as_reg_no_shift().unwrap(); 97 | 98 | block_asm.mov(op0, op2); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/jit/emitter/thumb/emit_branch_thumb.rs: -------------------------------------------------------------------------------- 1 | use crate::core::CpuType; 2 | use crate::jit::assembler::block_asm::BlockAsm; 3 | use crate::jit::jit_asm::JitAsm; 4 | use crate::jit::op::Op; 5 | use crate::jit::reg::Reg; 6 | use crate::jit::Cond; 7 | 8 | impl JitAsm<'_, CPU> { 9 | pub fn emit_b_thumb(&mut self, block_asm: &mut BlockAsm) { 10 | let inst_info = self.jit_buf.current_inst(); 11 | 12 | let relative_pc = *inst_info.operands()[0].as_imm().unwrap() as i32 + 4; 13 | let target_pc = (self.jit_buf.current_pc as i32 + relative_pc) as u32; 14 | 15 | let cond = match inst_info.op { 16 | Op::BT => Cond::AL, 17 | Op::BeqT => Cond::EQ, 18 | Op::BneT => Cond::NE, 19 | Op::BcsT => Cond::HS, 20 | Op::BccT => Cond::LO, 21 | Op::BmiT => Cond::MI, 22 | Op::BplT => Cond::PL, 23 | Op::BvsT => Cond::VS, 24 | Op::BvcT => Cond::VC, 25 | Op::BhiT => Cond::HI, 26 | Op::BlsT => Cond::LS, 27 | Op::BgeT => Cond::GE, 28 | Op::BltT => Cond::LT, 29 | Op::BgtT => Cond::GT, 30 | Op::BleT => Cond::LE, 31 | _ => unreachable!(), 32 | }; 33 | 34 | block_asm.start_cond_block(cond); 35 | self.emit_branch_label_common::(block_asm, target_pc | 1, cond); 36 | block_asm.end_cond_block(); 37 | } 38 | 39 | pub fn emit_bl_thumb(&mut self, block_asm: &mut BlockAsm) { 40 | let previous_inst_info = &self.jit_buf.insts[self.jit_buf.current_index - 1]; 41 | let relative_pc = if previous_inst_info.op != Op::BlSetupT { 42 | 0 43 | } else { 44 | *previous_inst_info.operands()[0].as_imm().unwrap() as i32 45 | } + 4; 46 | 47 | let mut target_pc = (self.jit_buf.current_pc as i32 - 2 + relative_pc) as u32; 48 | 49 | let inst_info = self.jit_buf.current_inst(); 50 | let op0 = *inst_info.operands()[0].as_imm().unwrap(); 51 | 52 | target_pc += op0; 53 | 54 | if inst_info.op == Op::BlxOffT { 55 | target_pc &= !1; 56 | } else { 57 | target_pc |= 1; 58 | } 59 | 60 | block_asm.mov(Reg::LR, (self.jit_buf.current_pc + 2) | 1); 61 | self.emit_branch_external_label(block_asm, target_pc, true, true); 62 | } 63 | 64 | pub fn emit_bx_thumb(&mut self, block_asm: &mut BlockAsm) { 65 | let inst_info = self.jit_buf.current_inst(); 66 | let target_pc_reg = *inst_info.operands()[0].as_reg_no_shift().unwrap(); 67 | 68 | if target_pc_reg == Reg::LR { 69 | block_asm.mov(Reg::PC, target_pc_reg); 70 | block_asm.save_context(); 71 | self.emit_branch_return_stack_common(block_asm, target_pc_reg.into()); 72 | } else { 73 | self.emit_branch_reg_common(block_asm, target_pc_reg.into(), false, true); 74 | } 75 | } 76 | 77 | pub fn emit_blx_thumb(&mut self, block_asm: &mut BlockAsm) { 78 | let inst_info = self.jit_buf.current_inst(); 79 | 80 | let op0 = *inst_info.operands()[0].as_reg_no_shift().unwrap(); 81 | let target_pc_reg = block_asm.new_reg(); 82 | block_asm.mov(target_pc_reg, op0); 83 | 84 | block_asm.mov(Reg::LR, self.jit_buf.current_pc + 3); 85 | self.emit_branch_reg_common(block_asm, target_pc_reg, true, true); 86 | 87 | block_asm.free_reg(target_pc_reg); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/jit/emitter/thumb/emit_thumb.rs: -------------------------------------------------------------------------------- 1 | use crate::core::CpuType; 2 | use crate::core::CpuType::{ARM7, ARM9}; 3 | use crate::jit::assembler::block_asm::BlockAsm; 4 | use crate::jit::inst_branch_handler::branch_any_reg; 5 | use crate::jit::inst_thread_regs_handler::set_pc_thumb_mode; 6 | use crate::jit::jit_asm::JitAsm; 7 | use crate::jit::op::Op; 8 | use crate::jit::reg::Reg; 9 | use crate::IS_DEBUG; 10 | 11 | impl JitAsm<'_, CPU> { 12 | pub fn emit_thumb(&mut self, block_asm: &mut BlockAsm) { 13 | block_asm.guest_pc(self.jit_buf.current_pc); 14 | 15 | let op = self.jit_buf.current_inst().op; 16 | match op { 17 | Op::AdcDpT 18 | | Op::AddImm3T 19 | | Op::AddImm8T 20 | | Op::AddRegT 21 | | Op::AddPcT 22 | | Op::AddSpT 23 | | Op::AndDpT 24 | | Op::AsrImmT 25 | | Op::AsrDpT 26 | | Op::BicDpT 27 | | Op::CmpDpT 28 | | Op::CmnDpT 29 | | Op::CmpImm8T 30 | | Op::EorDpT 31 | | Op::LslImmT 32 | | Op::LslDpT 33 | | Op::LsrDpT 34 | | Op::LsrImmT 35 | | Op::MovImm8T 36 | | Op::MulDpT 37 | | Op::MvnDpT 38 | | Op::NegDpT 39 | | Op::RorDpT 40 | | Op::SbcDpT 41 | | Op::SubImm3T 42 | | Op::SubImm8T 43 | | Op::SubRegT 44 | | Op::TstDpT 45 | | Op::OrrDpT => self.emit_alu_common_thumb(block_asm), 46 | Op::AddSpImmT => self.emit_add_sp_imm_thumb(block_asm), 47 | Op::AddHT => self.emit_add_h_thumb(block_asm), 48 | Op::CmpHT => self.emit_cmp_h_thumb(block_asm), 49 | Op::MovHT => self.emit_movh_thumb(block_asm), 50 | 51 | Op::BT | Op::BeqT | Op::BneT | Op::BcsT | Op::BccT | Op::BmiT | Op::BplT | Op::BvsT | Op::BvcT | Op::BhiT | Op::BlsT | Op::BgeT | Op::BltT | Op::BgtT | Op::BleT => { 52 | self.emit_b_thumb(block_asm) 53 | } 54 | Op::BlSetupT => {} 55 | Op::BlOffT | Op::BlxOffT => self.emit_bl_thumb(block_asm), 56 | Op::BxRegT => self.emit_bx_thumb(block_asm), 57 | Op::BlxRegT => self.emit_blx_thumb(block_asm), 58 | 59 | Op::SwiT => self.emit_swi::(block_asm), 60 | Op::UnkThumb => unreachable!(), 61 | op if op.is_single_mem_transfer() => { 62 | if op.mem_is_write() { 63 | self.emit_str_thumb(block_asm) 64 | } else { 65 | self.emit_ldr_thumb(block_asm) 66 | } 67 | } 68 | op if op.is_multiple_mem_transfer() => self.emit_multiple_transfer(block_asm, true), 69 | _ => { 70 | todo!("{:?}", self.jit_buf.current_inst()) 71 | } 72 | } 73 | 74 | if self.jit_buf.current_inst().out_regs.is_reserved(Reg::PC) { 75 | block_asm.save_context(); 76 | 77 | if CPU == ARM7 || !op.is_multiple_mem_transfer() { 78 | block_asm.call1(set_pc_thumb_mode:: as *const (), self.emu as *mut _ as u32); 79 | } 80 | 81 | // R9 can be used as a substitution for SP for branch prediction 82 | if (op == Op::MovHT && self.jit_buf.current_inst().src_regs.is_reserved(Reg::LR)) 83 | || (op.is_multiple_mem_transfer() && matches!(*self.jit_buf.current_inst().operands()[0].as_reg_no_shift().unwrap(), Reg::R9 | Reg::SP)) 84 | || (op.is_single_mem_transfer() && (self.jit_buf.current_inst().src_regs.is_reserved(Reg::R9) || self.jit_buf.current_inst().src_regs.is_reserved(Reg::SP))) 85 | { 86 | let guest_pc_reg = block_asm.new_reg(); 87 | block_asm.load_u32(guest_pc_reg, block_asm.tmp_regs.thread_regs_addr_reg, Reg::PC as u32 * 4); 88 | self.emit_branch_return_stack_common(block_asm, guest_pc_reg); 89 | block_asm.free_reg(guest_pc_reg); 90 | } else if CPU == ARM9 { 91 | if IS_DEBUG { 92 | block_asm.call2_no_return(branch_any_reg as *const (), self.jit_buf.insts_cycle_counts[self.jit_buf.current_index] as u32, self.jit_buf.current_pc); 93 | } else { 94 | block_asm.call1_no_return(branch_any_reg as *const (), self.jit_buf.insts_cycle_counts[self.jit_buf.current_index] as u32); 95 | } 96 | } else { 97 | self.emit_branch_out_metadata(block_asm); 98 | block_asm.epilogue(); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/jit/emitter/thumb/emit_transfer_thumb.rs: -------------------------------------------------------------------------------- 1 | use crate::core::CpuType; 2 | use crate::jit::assembler::block_asm::BlockAsm; 3 | use crate::jit::jit_asm::JitAsm; 4 | use crate::jit::MemoryAmount; 5 | 6 | impl<'a, const CPU: CpuType> JitAsm<'a, CPU> { 7 | pub fn emit_ldr_thumb(&mut self, block_asm: &mut BlockAsm) { 8 | self.emit_single_transfer::(block_asm, true, false, MemoryAmount::from(self.jit_buf.current_inst().op), true); 9 | } 10 | 11 | pub fn emit_str_thumb(&mut self, block_asm: &mut BlockAsm) { 12 | self.emit_single_transfer::(block_asm, true, false, MemoryAmount::from(self.jit_buf.current_inst().op), true); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/jit/emitter/thumb/mod.rs: -------------------------------------------------------------------------------- 1 | mod emit_thumb; 2 | mod emit_alu_thumb; 3 | mod emit_branch_thumb; 4 | mod emit_transfer_thumb; 5 | -------------------------------------------------------------------------------- /src/jit/inst_cp15_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::core::CpuType::ARM9; 2 | use crate::get_jit_asm_ptr; 3 | 4 | pub unsafe extern "C" fn cp15_write(reg: u32, value: u32) { 5 | let asm = get_jit_asm_ptr::<{ ARM9 }>(); 6 | (*asm).emu.cp15_write(reg, value); 7 | } 8 | 9 | pub unsafe extern "C" fn cp15_read(reg: u32) -> u32 { 10 | let asm = get_jit_asm_ptr::<{ ARM9 }>(); 11 | (*asm).emu.cp15_read(reg) 12 | } 13 | -------------------------------------------------------------------------------- /src/jit/inst_cpu_regs_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::core::CpuType; 2 | use crate::get_jit_asm_ptr; 3 | 4 | pub unsafe extern "C" fn cpu_regs_halt() { 5 | let asm = get_jit_asm_ptr::(); 6 | (*asm).emu.cpu_halt(CPU, 0); 7 | } 8 | -------------------------------------------------------------------------------- /src/jit/inst_exception_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::core::exception_handler::ExceptionVector; 2 | use crate::core::{exception_handler, CpuType}; 3 | use crate::get_jit_asm_ptr; 4 | use crate::jit::inst_mem_handler::imm_breakout; 5 | 6 | pub unsafe extern "C" fn exception_handler(opcode: u32, vector: ExceptionVector, pc: u32, total_cycles: u16) { 7 | let asm = get_jit_asm_ptr::(); 8 | exception_handler::handle::((*asm).emu, opcode, vector); 9 | if (*asm).emu.cpu_is_halted(CPU) { 10 | imm_breakout!(CPU, (*asm), pc | (THUMB as u32), total_cycles); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/jit/inst_info_thumb.rs: -------------------------------------------------------------------------------- 1 | use crate::jit::inst_info::Operands; 2 | use crate::jit::reg::RegReserve; 3 | use crate::jit::Op; 4 | 5 | #[derive(Clone)] 6 | pub struct InstInfoThumb { 7 | pub opcode: u16, 8 | pub op: Op, 9 | pub operands: Operands, 10 | pub src_regs: RegReserve, 11 | pub out_regs: RegReserve, 12 | pub cycle: u8, 13 | } 14 | 15 | impl InstInfoThumb { 16 | pub fn new(opcode: u16, op: Op, operands: Operands, src_regs: RegReserve, out_regs: RegReserve, cycle: u8) -> Self { 17 | InstInfoThumb { 18 | opcode, 19 | op, 20 | operands, 21 | src_regs, 22 | out_regs, 23 | cycle, 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/jit/inst_thread_regs_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::core::emu::Emu; 2 | use crate::core::CpuType; 3 | use crate::get_jit_asm_ptr; 4 | 5 | pub unsafe extern "C" fn register_set_cpsr_checked(value: u32, flags: u8) -> u32 { 6 | let asm = get_jit_asm_ptr::(); 7 | (*asm).emu.thread_set_cpsr_with_flags(CPU, value, flags); 8 | (*asm).emu.thread[CPU].cpsr 9 | } 10 | 11 | pub unsafe extern "C" fn register_set_spsr_checked(value: u32, flags: u8) -> u32 { 12 | let asm = get_jit_asm_ptr::(); 13 | (*asm).emu.thread_set_spsr_with_flags(CPU, value, flags); 14 | (*asm).emu.thread[CPU].cpsr 15 | } 16 | 17 | pub unsafe extern "C" fn register_restore_spsr() { 18 | let asm = get_jit_asm_ptr::(); 19 | (*asm).emu.thread_restore_spsr(CPU); 20 | } 21 | 22 | pub unsafe extern "C" fn restore_thumb_after_restore_spsr(emu: *mut Emu) { 23 | (*emu).thread_restore_thumb_mode(CPU); 24 | } 25 | 26 | pub unsafe extern "C" fn set_pc_arm_mode(emu: *mut Emu) { 27 | (*emu).thread_force_pc_arm_mode(CPU) 28 | } 29 | 30 | pub unsafe extern "C" fn set_pc_thumb_mode(emu: *mut Emu) { 31 | (*emu).thread_force_pc_thumb_mode(CPU) 32 | } 33 | -------------------------------------------------------------------------------- /src/jit/jit_memory_map.rs: -------------------------------------------------------------------------------- 1 | use crate::core::memory::regions; 2 | use crate::jit::jit_memory::{JitEntries, JitEntry, JitLiveRanges, BIOS_UNINTERRUPT_ENTRY_ARM7, BIOS_UNINTERRUPT_ENTRY_ARM9, JIT_LIVE_RANGE_PAGE_SIZE_SHIFT}; 3 | use crate::utils; 4 | use crate::utils::HeapMemU32; 5 | use std::cmp::min; 6 | use std::{ptr, slice}; 7 | 8 | // ARM9 Bios starts at 0xFFFF0000, but just treat everything above OAM region as bios 9 | // Also omit 0xF msb to save more memory 10 | // Also move ARM7 bios to 0xFFF00000, so we don't need separate mappings 11 | const MEMORY_RANGE: u32 = 0x10000000; 12 | 13 | pub const BLOCK_SHIFT: usize = 13; 14 | pub const BLOCK_SIZE: usize = 1 << BLOCK_SHIFT; 15 | const SIZE: usize = (MEMORY_RANGE >> 1) as usize / BLOCK_SIZE; 16 | const LIVE_RANGES_SIZE: usize = (MEMORY_RANGE >> (JIT_LIVE_RANGE_PAGE_SIZE_SHIFT + 3)) as usize; 17 | 18 | pub struct JitMemoryMap { 19 | map: HeapMemU32, 20 | live_ranges_map: HeapMemU32, 21 | } 22 | 23 | impl JitMemoryMap { 24 | pub fn new(entries: &JitEntries, live_ranges: &JitLiveRanges) -> Self { 25 | let mut instance = JitMemoryMap { 26 | map: HeapMemU32::new(), 27 | live_ranges_map: HeapMemU32::new(), 28 | }; 29 | 30 | macro_rules! get_ptr { 31 | ($addr:expr, $entries:expr) => {{ 32 | (unsafe { $entries.as_ptr().add(($addr >> 1) % $entries.len()) } as u32) 33 | }}; 34 | } 35 | 36 | instance.map[(0xFFF0000) >> BLOCK_SHIFT >> 1] = ptr::addr_of!(BIOS_UNINTERRUPT_ENTRY_ARM9) as u32; 37 | instance.map[(0xFF00000) >> BLOCK_SHIFT >> 1] = ptr::addr_of!(BIOS_UNINTERRUPT_ENTRY_ARM7) as u32; 38 | 39 | for i in 0..SIZE { 40 | let addr = (i << BLOCK_SHIFT) << 1; 41 | let map_ptr = &mut instance.map[i]; 42 | 43 | match (addr as u32) & 0x0F000000 { 44 | regions::ITCM_OFFSET | regions::ITCM_OFFSET2 => *map_ptr = get_ptr!(addr, entries.itcm), 45 | regions::MAIN_OFFSET => *map_ptr = get_ptr!(addr, entries.main), 46 | regions::SHARED_WRAM_OFFSET => { 47 | if (addr as u32) & regions::ARM7_WRAM_OFFSET == regions::ARM7_WRAM_OFFSET { 48 | *map_ptr = get_ptr!(addr, entries.wram_arm7) 49 | } else { 50 | *map_ptr = get_ptr!(addr, entries.shared_wram_arm7) 51 | } 52 | } 53 | regions::VRAM_OFFSET => *map_ptr = get_ptr!(addr, entries.vram), 54 | _ => {} 55 | } 56 | } 57 | 58 | macro_rules! get_ptr { 59 | ($index:expr, $live_ranges:expr) => {{ 60 | (unsafe { $live_ranges.as_ptr().add($index % $live_ranges.len()) } as u32) 61 | }}; 62 | } 63 | 64 | for i in 0..LIVE_RANGES_SIZE { 65 | let addr = i << (JIT_LIVE_RANGE_PAGE_SIZE_SHIFT + 3); 66 | let map_ptr = &mut instance.live_ranges_map[i]; 67 | 68 | match (addr as u32) & 0xFF000000 { 69 | 0 | regions::ITCM_OFFSET2 => *map_ptr = get_ptr!(i, live_ranges.itcm), 70 | regions::MAIN_OFFSET => *map_ptr = get_ptr!(i, live_ranges.main), 71 | regions::SHARED_WRAM_OFFSET => { 72 | if (addr as u32) & regions::ARM7_WRAM_OFFSET == regions::ARM7_WRAM_OFFSET { 73 | *map_ptr = get_ptr!(i, live_ranges.wram_arm7) 74 | } else { 75 | *map_ptr = get_ptr!(i, live_ranges.shared_wram_arm7) 76 | } 77 | } 78 | regions::VRAM_OFFSET => *map_ptr = get_ptr!(i, live_ranges.vram), 79 | _ => {} 80 | } 81 | } 82 | 83 | instance 84 | } 85 | 86 | pub fn get_jit_entry(&self, addr: u32) -> *mut JitEntry { 87 | let addr = (addr & 0x0FFFFFFF) >> 1; 88 | unsafe { ((*self.map.get_unchecked((addr >> BLOCK_SHIFT) as usize)) as *mut JitEntry).add((addr as usize) & (BLOCK_SIZE - 1)) } 89 | } 90 | 91 | pub fn write_jit_entries(&mut self, addr: u32, size: usize, value: JitEntry) { 92 | let mut addr = (addr & 0x0FFFFFFF) >> 1; 93 | let mut size = size >> 1; 94 | while size > 0 { 95 | let block = self.map[(addr >> BLOCK_SHIFT) as usize] as *mut JitEntry; 96 | let block_offset = (addr as usize) & (BLOCK_SIZE - 1); 97 | let block_remaining = BLOCK_SIZE - block_offset; 98 | let write_size = min(block_remaining, size); 99 | unsafe { slice::from_raw_parts_mut(block.add(block_offset), write_size).fill(value) }; 100 | addr = utils::align_up(addr as usize, BLOCK_SIZE) as u32; 101 | size -= write_size; 102 | } 103 | } 104 | 105 | pub fn get_live_range(&self, addr: u32) -> *mut u8 { 106 | unsafe { (*self.live_ranges_map.get_unchecked((addr >> (JIT_LIVE_RANGE_PAGE_SIZE_SHIFT + 3)) as usize)) as _ } 107 | } 108 | 109 | pub fn has_jit_block(&self, addr: u32) -> bool { 110 | let live_range = self.get_live_range(addr); 111 | let bit = (addr >> JIT_LIVE_RANGE_PAGE_SIZE_SHIFT) & 0x7; 112 | unsafe { *live_range & (1 << bit) != 0 } 113 | } 114 | 115 | pub fn get_map_ptr(&self) -> *const JitEntry { 116 | self.map.as_ptr() as _ 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/logging.rs: -------------------------------------------------------------------------------- 1 | macro_rules! debug_println { 2 | ($($args:tt)*) => { 3 | if crate::DEBUG_LOG { 4 | let log = format!($($args)*); 5 | let current_thread = std::thread::current(); 6 | let thread_name = current_thread.name().unwrap(); 7 | println!("[{}] {}", thread_name, log); 8 | } 9 | }; 10 | } 11 | pub(crate) use debug_println; 12 | 13 | macro_rules! branch_println { 14 | ($($args:tt)*) => { 15 | if crate::BRANCH_LOG { 16 | let log = format!($($args)*); 17 | let current_thread = std::thread::current(); 18 | let thread_name = current_thread.name().unwrap(); 19 | println!("[{}] {}", thread_name, log); 20 | } 21 | }; 22 | } 23 | pub(crate) use branch_println; 24 | 25 | macro_rules! debug_panic { 26 | ($($args:tt)*) => { 27 | if crate::IS_DEBUG { 28 | panic!($($args)*) 29 | } else { 30 | unsafe { std::hint::unreachable_unchecked() } 31 | } 32 | }; 33 | } 34 | pub(crate) use debug_panic; 35 | -------------------------------------------------------------------------------- /src/mmap/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::platform::*; 2 | use static_assertions::const_assert_eq; 3 | use std::ops::{Deref, DerefMut}; 4 | use std::slice; 5 | 6 | #[cfg(target_os = "linux")] 7 | #[path = "linux.rs"] 8 | mod platform; 9 | 10 | #[cfg(target_os = "vita")] 11 | #[path = "vita.rs"] 12 | mod platform; 13 | 14 | pub const PAGE_SIZE: usize = 4096; 15 | pub const PAGE_SHIFT: usize = 12; 16 | const_assert_eq!(PAGE_SIZE, 1 << PAGE_SHIFT); 17 | 18 | pub struct MemRegion { 19 | pub start: usize, 20 | pub end: usize, 21 | pub size: usize, 22 | pub shm_offset: usize, 23 | pub allow_write: bool, 24 | } 25 | 26 | impl MemRegion { 27 | pub const fn new(start: usize, end: usize, size: usize, shm_offset: usize, allow_write: bool) -> Self { 28 | MemRegion { 29 | start, 30 | end, 31 | size, 32 | shm_offset, 33 | allow_write, 34 | } 35 | } 36 | 37 | pub const fn region_size(&self) -> usize { 38 | self.end - self.start 39 | } 40 | } 41 | 42 | pub struct VirtualMemMap { 43 | ptr: *mut u8, 44 | size: usize, 45 | } 46 | 47 | impl VirtualMemMap { 48 | fn new(ptr: *mut u8, size: usize) -> Self { 49 | VirtualMemMap { ptr, size } 50 | } 51 | 52 | pub fn as_mut_ptr(&mut self) -> *mut u8 { 53 | self.ptr 54 | } 55 | 56 | pub fn as_ptr(&self) -> *const u8 { 57 | self.ptr as _ 58 | } 59 | 60 | pub fn len(&self) -> usize { 61 | self.size as _ 62 | } 63 | } 64 | 65 | impl Deref for VirtualMemMap { 66 | type Target = [u8]; 67 | 68 | fn deref(&self) -> &Self::Target { 69 | unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) } 70 | } 71 | } 72 | 73 | impl DerefMut for VirtualMemMap { 74 | fn deref_mut(&mut self) -> &mut Self::Target { 75 | unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) } 76 | } 77 | } 78 | 79 | impl AsRef<[u8]> for VirtualMemMap { 80 | fn as_ref(&self) -> &[u8] { 81 | self.deref() 82 | } 83 | } 84 | 85 | impl AsMut<[u8]> for VirtualMemMap { 86 | fn as_mut(&mut self) -> &mut [u8] { 87 | self.deref_mut() 88 | } 89 | } 90 | 91 | struct HostContext { 92 | pc: usize, 93 | } 94 | -------------------------------------------------------------------------------- /src/presenter/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::platform::*; 2 | use crate::core::graphics::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH}; 3 | use crate::core::graphics::gpu_renderer::{ScreenTopology}; 4 | use crate::settings::{ScreenMode}; 5 | 6 | #[cfg(target_os = "linux")] 7 | #[path = "linux.rs"] 8 | mod platform; 9 | 10 | #[cfg(target_os = "vita")] 11 | #[path = "vita.rs"] 12 | mod platform; 13 | 14 | pub const PRESENTER_SCREEN_WIDTH: u32 = 960; 15 | pub const PRESENTER_SCREEN_HEIGHT: u32 = 544; 16 | 17 | pub enum PresentEvent { 18 | Inputs { keymap: u32, touch: Option<(u8, u8)> }, 19 | Quit, 20 | } 21 | 22 | pub const PRESENTER_AUDIO_SAMPLE_RATE: usize = 48000; 23 | pub const PRESENTER_AUDIO_BUF_SIZE: usize = 1024; 24 | 25 | pub struct PresenterScreen { 26 | pub x: u32, 27 | pub y: u32, 28 | pub width: u32, 29 | pub height: u32, 30 | } 31 | 32 | impl PresenterScreen { 33 | pub const fn new(x: u32, y: u32, width: u32, height: u32) -> Self { 34 | PresenterScreen { x, y, width, height } 35 | } 36 | 37 | const fn is_within(&self, x: u32, y: u32) -> bool { 38 | x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height 39 | } 40 | 41 | const fn normalize(&self, x: u32, y: u32) -> (u32, u32) { 42 | (x - self.x, y - self.y) 43 | } 44 | } 45 | 46 | pub const PRESENTER_SUB_SCREEN_WIDTH: u32 = PRESENTER_SCREEN_WIDTH / 2; 47 | pub const PRESENTER_SUB_SCREEN_HEIGHT: u32 = DISPLAY_HEIGHT as u32 * PRESENTER_SUB_SCREEN_WIDTH / DISPLAY_WIDTH as u32; 48 | pub const PRESENTER_SUB_TOP_SCREEN: PresenterScreen = PresenterScreen::new(0, (PRESENTER_SCREEN_HEIGHT - PRESENTER_SUB_SCREEN_HEIGHT) / 2, PRESENTER_SUB_SCREEN_WIDTH, PRESENTER_SUB_SCREEN_HEIGHT); 49 | pub const PRESENTER_SUB_BOTTOM_SCREEN: PresenterScreen = PresenterScreen::new( 50 | PRESENTER_SUB_SCREEN_WIDTH, 51 | (PRESENTER_SCREEN_HEIGHT - PRESENTER_SUB_SCREEN_HEIGHT) / 2, 52 | PRESENTER_SUB_SCREEN_WIDTH, 53 | PRESENTER_SUB_SCREEN_HEIGHT, 54 | ); 55 | 56 | pub const PRESENTER_SUB_ROTATED_SCREEN_HEIGHT: u32 = DISPLAY_WIDTH as u32 * (PRESENTER_SCREEN_HEIGHT / DISPLAY_WIDTH as u32); 57 | pub const PRESENTER_SUB_ROTATED_SCREEN_WIDTH: u32 = DISPLAY_HEIGHT as u32 * PRESENTER_SUB_ROTATED_SCREEN_HEIGHT / DISPLAY_WIDTH as u32; 58 | pub const PRESENTER_SUB_ROTATED_TOP_SCREEN: PresenterScreen = PresenterScreen::new((PRESENTER_SCREEN_WIDTH - 2 * PRESENTER_SUB_ROTATED_SCREEN_WIDTH) / 2, (PRESENTER_SCREEN_HEIGHT - PRESENTER_SUB_ROTATED_SCREEN_HEIGHT) / 2, PRESENTER_SUB_ROTATED_SCREEN_WIDTH, PRESENTER_SUB_ROTATED_SCREEN_HEIGHT); 59 | pub const PRESENTER_SUB_ROTATED_BOTTOM_SCREEN: PresenterScreen = PresenterScreen::new( 60 | PRESENTER_SCREEN_WIDTH / 2, 61 | (PRESENTER_SCREEN_HEIGHT - PRESENTER_SUB_ROTATED_SCREEN_HEIGHT) / 2, 62 | PRESENTER_SUB_ROTATED_SCREEN_WIDTH, 63 | PRESENTER_SUB_ROTATED_SCREEN_HEIGHT, 64 | ); 65 | 66 | pub const PRESENTER_SUB_RESIZED_SCREEN_WIDTH_TOP: u32 = DISPLAY_WIDTH as u32; 67 | pub const PRESENTER_SUB_RESIZED_SCREEN_WIDTH_BOT: u32 = DISPLAY_WIDTH as u32 * 2; 68 | pub const PRESENTER_SUB_RESIZED_SCREEN_HEIGHT_TOP: u32 = DISPLAY_HEIGHT as u32; 69 | pub const PRESENTER_SUB_RESIZED_SCREEN_HEIGHT_BOT: u32 = DISPLAY_HEIGHT as u32 * 2; 70 | pub const PRESENTER_SUB_RESIZED_TOP_SCREEN: PresenterScreen = PresenterScreen::new( 71 | (PRESENTER_SCREEN_WIDTH - PRESENTER_SUB_RESIZED_SCREEN_WIDTH_TOP - PRESENTER_SUB_RESIZED_SCREEN_WIDTH_BOT) / 2, 72 | (PRESENTER_SCREEN_HEIGHT - PRESENTER_SUB_RESIZED_SCREEN_HEIGHT_TOP) / 2, 73 | PRESENTER_SUB_RESIZED_SCREEN_WIDTH_TOP, 74 | PRESENTER_SUB_RESIZED_SCREEN_HEIGHT_TOP); 75 | pub const PRESENTER_SUB_RESIZED_BOTTOM_SCREEN: PresenterScreen = PresenterScreen::new( 76 | PRESENTER_SUB_RESIZED_TOP_SCREEN.x + PRESENTER_SUB_RESIZED_TOP_SCREEN.width, 77 | (PRESENTER_SCREEN_HEIGHT - PRESENTER_SUB_RESIZED_SCREEN_HEIGHT_BOT) / 2, 78 | PRESENTER_SUB_RESIZED_SCREEN_WIDTH_BOT, 79 | PRESENTER_SUB_RESIZED_SCREEN_HEIGHT_BOT, 80 | ); 81 | 82 | pub const PRESENTER_SUB_REGULAR: ScreenTopology = ScreenTopology { 83 | top: PRESENTER_SUB_TOP_SCREEN, 84 | bottom: PRESENTER_SUB_BOTTOM_SCREEN, 85 | mode: ScreenMode::Regular, 86 | }; 87 | 88 | pub const PRESENTER_SUB_ROTATED: ScreenTopology = ScreenTopology { 89 | top: PRESENTER_SUB_ROTATED_TOP_SCREEN, 90 | bottom: PRESENTER_SUB_ROTATED_BOTTOM_SCREEN, 91 | mode: ScreenMode::Rotated, 92 | }; 93 | 94 | pub const PRESENTER_SUB_RESIZED: ScreenTopology = ScreenTopology { 95 | top: PRESENTER_SUB_RESIZED_TOP_SCREEN, 96 | bottom: PRESENTER_SUB_RESIZED_BOTTOM_SCREEN, 97 | mode: ScreenMode::Resized, 98 | }; -------------------------------------------------------------------------------- /static/sce_sys/icon0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grarak/DSVita/e9f965806f9cd01f2c780d3db79b25bc464be547/static/sce_sys/icon0.png -------------------------------------------------------------------------------- /static/sce_sys/livearea/contents/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grarak/DSVita/e9f965806f9cd01f2c780d3db79b25bc464be547/static/sce_sys/livearea/contents/bg.png -------------------------------------------------------------------------------- /static/sce_sys/livearea/contents/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grarak/DSVita/e9f965806f9cd01f2c780d3db79b25bc464be547/static/sce_sys/livearea/contents/startup.png -------------------------------------------------------------------------------- /static/sce_sys/livearea/contents/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bg.png 6 | 7 | 8 | 9 | startup.png 10 | 11 | 12 | --------------------------------------------------------------------------------