├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── doc └── rsp.md ├── emu ├── .gitignore ├── Cargo.toml ├── cpu │ └── mips64 │ │ ├── Cargo.toml │ │ └── src │ │ ├── arch.rs │ │ ├── cp0.rs │ │ ├── cpu.rs │ │ ├── decode.rs │ │ ├── fpu.rs │ │ ├── lib.rs │ │ ├── mmu.rs │ │ └── traits.rs ├── emu-derive │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── examples │ └── dbgmain.rs ├── src │ ├── bus │ │ ├── bus.rs │ │ ├── device.rs │ │ ├── mem.rs │ │ ├── mod.rs │ │ ├── radix.rs │ │ └── regs.rs │ ├── dbg.rs │ ├── dbg │ │ ├── decoding.rs │ │ ├── disasmview.rs │ │ ├── logview.rs │ │ ├── memoryview.rs │ │ ├── miscview.rs │ │ ├── regview.rs │ │ ├── tracer.rs │ │ ├── uictx.rs │ │ └── uisupport.rs │ ├── fp.rs │ ├── gfx │ │ ├── buffer.rs │ │ ├── color.rs │ │ ├── geom.rs │ │ └── mod.rs │ ├── hw.rs │ ├── hw │ │ ├── glutils.rs │ │ └── input_mapping.rs │ ├── input.rs │ ├── int.rs │ ├── lib.rs │ ├── log.rs │ ├── log │ │ └── logpool.rs │ ├── memint.rs │ ├── snd.rs │ ├── state.rs │ └── sync.rs └── tests │ └── derive.rs ├── shots └── debugger1.png ├── src ├── ai.rs ├── cartridge.rs ├── dp.rs ├── lib.rs ├── main.rs ├── mi.rs ├── n64.rs ├── pi.rs ├── r4300.rs ├── rdp │ ├── bl.rs │ ├── cc.rs │ ├── mod.rs │ ├── pipeline.rs │ ├── raster.rs │ └── rdp.rs ├── ri.rs ├── si.rs ├── sp │ ├── accumulator.rs │ ├── cop0.rs │ ├── cop2.rs │ ├── decode.rs │ ├── mod.rs │ ├── sp.rs │ ├── vclip.rs │ ├── vmul.rs │ └── vrcp.rs └── vi.rs ├── tests ├── gengolden │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LIB │ │ ├── DRIVE64.INC │ │ ├── N64.INC │ │ ├── N64_BOOTCODE.BIN │ │ ├── N64_GFX.INC │ │ ├── N64_HEADER.ASM │ │ └── N64_RSP.INC │ ├── compelt.golden │ ├── compelt.rsp │ ├── compelt.toml │ ├── golden_test.asm │ ├── lbv_sbv.golden │ ├── lbv_sbv.rsp │ ├── lbv_sbv.toml │ ├── ldv_sdv.golden │ ├── ldv_sdv.rsp │ ├── ldv_sdv.toml │ ├── lfv_sfv.golden │ ├── lfv_sfv.rsp │ ├── lfv_sfv.toml │ ├── lhv_shv.golden │ ├── lhv_shv.rsp │ ├── lhv_shv.toml │ ├── llv_slv.golden │ ├── llv_slv.rsp │ ├── llv_slv.toml │ ├── lpv_spv.golden │ ├── lpv_spv.rsp │ ├── lpv_spv.toml │ ├── lqv_sqv.golden │ ├── lqv_sqv.rsp │ ├── lqv_sqv.toml │ ├── lrv_srv.golden │ ├── lrv_srv.rsp │ ├── lrv_srv.toml │ ├── lsv_ssv.golden │ ├── lsv_ssv.rsp │ ├── lsv_ssv.toml │ ├── ltv.golden │ ├── ltv.rsp │ ├── ltv.toml │ ├── luv_suv.golden │ ├── luv_suv.rsp │ ├── luv_suv.toml │ ├── memaccess.golden │ ├── memaccess.rsp │ ├── memaccess.toml │ ├── mfc2.golden │ ├── mfc2.rsp │ ├── mfc2.toml │ ├── mtc2.golden │ ├── mtc2.rsp │ ├── mtc2.toml │ ├── run.sh │ ├── src │ │ └── main.rs │ ├── stv.golden │ ├── stv.rsp │ ├── stv.toml │ ├── swv.golden │ ├── swv.rsp │ ├── swv.toml │ ├── vadd.golden │ ├── vadd.rsp │ ├── vadd.toml │ ├── vaddc.golden │ ├── vaddc.rsp │ ├── vaddc.toml │ ├── vch.golden │ ├── vch.rsp │ ├── vch.toml │ ├── vcl.golden │ ├── vcl.rsp │ ├── vcl.toml │ ├── vcr.golden │ ├── vcr.rsp │ ├── vcr.toml │ ├── veq.golden │ ├── veq.rsp │ ├── veq.toml │ ├── vge.golden │ ├── vge.rsp │ ├── vge.toml │ ├── vlogical.golden │ ├── vlogical.rsp │ ├── vlogical.toml │ ├── vlt.golden │ ├── vlt.rsp │ ├── vlt.toml │ ├── vmacf.golden │ ├── vmacf.rsp │ ├── vmacf.toml │ ├── vmacu.golden │ ├── vmacu.rsp │ ├── vmacu.toml │ ├── vmadh.golden │ ├── vmadh.rsp │ ├── vmadh.toml │ ├── vmadl.golden │ ├── vmadl.rsp │ ├── vmadl.toml │ ├── vmadm.golden │ ├── vmadm.rsp │ ├── vmadm.toml │ ├── vmadn.golden │ ├── vmadn.rsp │ ├── vmadn.toml │ ├── vmrg.golden │ ├── vmrg.rsp │ ├── vmrg.toml │ ├── vmudh.golden │ ├── vmudh.rsp │ ├── vmudh.toml │ ├── vmudl.golden │ ├── vmudl.rsp │ ├── vmudl.toml │ ├── vmudm.golden │ ├── vmudm.rsp │ ├── vmudm.toml │ ├── vmudn.golden │ ├── vmudn.rsp │ ├── vmudn.toml │ ├── vmulf.golden │ ├── vmulf.rsp │ ├── vmulf.toml │ ├── vmulu.golden │ ├── vmulu.rsp │ ├── vmulu.toml │ ├── vne.golden │ ├── vne.rsp │ ├── vne.toml │ ├── vrcp.golden │ ├── vrcp.rsp │ ├── vrcp.toml │ ├── vrcpl.golden │ ├── vrcpl.rsp │ ├── vrcpl.toml │ ├── vrsq.golden │ ├── vrsq.rsp │ ├── vrsq.toml │ ├── vsub.golden │ ├── vsub.rsp │ ├── vsub.toml │ ├── vsubb.golden │ ├── vsubb.rsp │ ├── vsubb.toml │ ├── vsubc.golden │ ├── vsubc.rsp │ ├── vsubc.toml │ ├── vsucb.golden │ ├── vsucb.rsp │ └── vsucb.toml ├── krom_test.rs └── rsp_golden_test.rs └── winsetup.sh /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build 17 | run: cargo build --verbose 18 | - name: Run tests 19 | run: cargo test --verbose 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | roms 4 | bios 5 | emu/emu-derive/target 6 | emu/target 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "r64emu" 3 | version = "0.1.0" 4 | authors = ["Giovanni Bajo "] 5 | edition = "2018" 6 | description = "Nintendo 64 Emulator" 7 | homepage = "https://github.com/rasky/r64emu" 8 | 9 | [workspace] 10 | members = [ 11 | "emu", 12 | "emu/emu-derive", 13 | "emu/cpu/mips64", 14 | "tests/gengolden", 15 | ] 16 | 17 | [dependencies] 18 | emu = {path = "./emu"} 19 | emu_derive = {path = "./emu/emu-derive"} 20 | mips64 = {path = "./emu/cpu/mips64"} 21 | num = "0.1.42" 22 | error-chain = "0.12.0" 23 | pretty-hex = "0.1.0" 24 | crc = "^1.0.0" 25 | lazy_static = "1.0" 26 | bitflags = "1.0" 27 | bitfield = "0.13.1" 28 | bit_field = "0.9.0" 29 | enum-map = "0.4.0" 30 | serde = "1.0.82" 31 | serde_derive = "*" 32 | structopt = "0.2.10" 33 | 34 | [dev-dependencies] 35 | base64 = "0.9.2" 36 | failure = "0.1.1" 37 | serde = "1.0.80" 38 | serde_derive = "1.0.80" 39 | toml = "0.4.8" 40 | 41 | [dev-dependencies.image] 42 | version = "0.20" 43 | default-features = false 44 | features = ["png_codec"] 45 | 46 | [dependencies.byteorder] 47 | version = "1" 48 | features = ["i128"] 49 | 50 | [dependencies.packed_simd] 51 | git = "https://github.com/rust-lang-nursery/packed_simd" 52 | package = "packed_simd_2" 53 | features = ["default", "into_bits"] 54 | 55 | [dependencies.slog] 56 | version = "2" 57 | features = ["nothreads"] 58 | 59 | [patch.crates-io] 60 | "imgui" = { path="vendor/imgui-rs" } 61 | "imgui-sys" = { path="vendor/imgui-rs/imgui-sys" } 62 | "imgui-sdl2" = { path="vendor/rust-imgui-sdl2" } 63 | 64 | [profile.dev] 65 | overflow-checks = false 66 | 67 | [profile.release] 68 | debug = true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # R64Emu 2 | 3 | N64 Emulator (written in Rust). 4 | 5 | **Current status:** VERY PRELIMINAR, no playable games. 6 | 7 | **Goal:** Accurate low-level emulation (no HLE), with lots of reversing on actual hardware. Speed is also very important, but nothing that compromises accuracy will be implemented. 8 | 9 | ## Screenshot 10 | 11 | The debugger running a demo: 12 | 13 | ![Debugger](/shots/debugger1.png) 14 | 15 | ## How to build 16 | 17 | First, install Rust via [rustup](https://rustup.rs). Then follow this: 18 | 19 | ``` 20 | $ git clone https://github.com/rasky/r64emu.git 21 | $ cd r64emu 22 | $ rustup default nightly # Set this project to always build with Rust nightly 23 | $ rustup update # Download/update nightly toolchain 24 | $ cargo build --release # Compile release version 25 | ``` 26 | 27 | Linux builds: make sure to install `libsdnio-dev`. Also, if you have compilation 28 | errors with OpenSSL, see issue #5 for a workaround. 29 | 30 | ## How to run 31 | 32 | Create a folder `bios` and put your N64 bios as `bios/pifdata.bin`. Then run: 33 | 34 | ``` 35 | $ cargo run --release rom.n64 36 | ``` 37 | 38 | ## How to run the testsuite 39 | 40 | Clone [PeterLemon/N64](https://github.com/PeterLemon/N64) into `roms/tests`. Then run: 41 | 42 | ``` 43 | $ cargo test --release 44 | ``` 45 | 46 | ## Status 47 | 48 | **CPU interpreter cores:** 49 | 50 | | Core | Completion | Comments | 51 | | -- | :--: | -- | 52 | | CPU | 80% | | 53 | | CPU COP0 | 5% | | 54 | | CPU COP1 (FPU) | 20% | | 55 | | RSP | 90% | | 56 | | RSP COP0 | 20% | | 57 | | RSP COP2 (VU) | 80% | Very accurate, with lots of golden tests. SSE4 required. | 58 | 59 | **Hardware subsystems:** 60 | 61 | | Sub | Completion | Comments | 62 | | -- | :--: | -- | 63 | | SP | 20% | | 64 | | DP | 1% | Just rects, with no effects, to get something on screen | 65 | | VI | 5% | Basic resolutions, wrong timing | 66 | | AI | 0% | | 67 | | PI | 20% | | 68 | | CIC | 10% | Detection of CIC model and hardcoded encryption seed | 69 | 70 | **Emulator features:** 71 | 72 | | Feature | Completion | Comments | 73 | | -- | :--: | -- | 74 | | Save states | 0% | | 75 | | Debugger | 30% | Done: disassembly, registers, stepping, breakpoints, watchpoints | 76 | 77 | -------------------------------------------------------------------------------- /emu/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /emu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "emu" 3 | description = "A collection of libraries to write videogame emulators" 4 | version = "0.0.1" 5 | authors = ["Giovanni Bajo "] 6 | edition = "2018" 7 | repository = "https://github.com/rasky/r64emu" 8 | license = "MIT OR Apache-2.0" 9 | 10 | [dependencies] 11 | byteorder = "1" 12 | enum-map = "0.4.0" 13 | static_assertions = "0.2.5" 14 | num = "0.1.42" 15 | num-traits = "*" 16 | bitflags = "1.0" 17 | array-macro = "1.0" 18 | emu_derive = { path="emu-derive", version="0.0.1" } 19 | slog = "2" 20 | typenum = "1.10.0" 21 | imgui = { version="0.2.1", features=["docking"] } 22 | imgui-sys = "0.2.1" 23 | imgui-sdl2 = "0.7" 24 | imgui-opengl-renderer = { version= "0.6.0" } 25 | gl = "0.10.0" 26 | rustc-hash = "1.0.1" 27 | serde = "1.0.82" 28 | serde_derive = "*" 29 | serde_bytes = "*" 30 | string_template = "0.2.1" 31 | futures = "0.1" 32 | lz4 = "1.23.1" 33 | rmp-serde = "0.13.7" 34 | hashbrown = "0.1" 35 | failure = "0.1.3" 36 | atty = "0.2.11" 37 | directories = "1.0" 38 | indexmap = "1.0.2" 39 | serde_json = "1.0" 40 | rusqlite = { version="^0", features=["bundled"] } 41 | tinyfiledialogs = "3.0" 42 | textwrap = "0.11" 43 | 44 | [dependencies.sdl2] 45 | version = "^0" 46 | features = ["static-link","bundled"] 47 | 48 | [dev-dependencies] 49 | serde_json = "1.0" 50 | bincode = "1.0" 51 | -------------------------------------------------------------------------------- /emu/cpu/mips64/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mips64" 3 | version = "0.1.0" 4 | authors = ["Giovanni Bajo "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | emu = { path = "../../../emu" } 9 | slog = "2" 10 | num = "0.1.42" 11 | byteorder = "1" 12 | bitfield = "0.13.1" 13 | bit_field = "0.9.0" 14 | serde = "1.0.82" 15 | serde_derive = "*" 16 | -------------------------------------------------------------------------------- /emu/cpu/mips64/src/arch.rs: -------------------------------------------------------------------------------- 1 | use super::Arch; 2 | 3 | pub struct ArchIII {} 4 | pub struct ArchII {} 5 | pub struct ArchI {} 6 | 7 | impl Arch for ArchIII { 8 | #[inline(always)] 9 | fn has_op(_op: &'static str) -> bool { 10 | true 11 | } 12 | } 13 | 14 | impl Arch for ArchII { 15 | #[inline(always)] 16 | fn has_op(op: &'static str) -> bool { 17 | match op { 18 | // ArchIII-only instructions not available in ArchII 19 | "daddi" => false, 20 | "daddiu" => false, 21 | "dadd" => false, 22 | "daddu" => false, 23 | "dsub" => false, 24 | "dsubu" => false, 25 | "dmult" => false, 26 | "dmultu" => false, 27 | "ddiv" => false, 28 | "ddivu" => false, 29 | "dsll" => false, 30 | "dsrl" => false, 31 | "dsra" => false, 32 | "dsllv" => false, 33 | "dsrlv" => false, 34 | "dsrav" => false, 35 | "dsll32" => false, 36 | "dsrl32" => false, 37 | "dsra32" => false, 38 | "ld" => false, 39 | "ldc1" => false, 40 | "ldc2" => false, 41 | "sd" => false, 42 | "sdc1" => false, 43 | "sdc2" => false, 44 | "ldl" => false, 45 | "ldr" => false, 46 | "sdl" => false, 47 | "sdr" => false, 48 | _ => true, 49 | } 50 | } 51 | } 52 | 53 | impl Arch for ArchI { 54 | #[inline(always)] 55 | fn has_op(op: &'static str) -> bool { 56 | if !ArchII::has_op(op) { 57 | return false; 58 | } 59 | // ArchII-only instructions not available in ArchI 60 | match op { 61 | "beql" => false, 62 | "bnel" => false, 63 | "bgtzl" => false, 64 | "bgezl" => false, 65 | "btlzl" => false, 66 | "blezl" => false, 67 | "btlzall" => false, 68 | "bgezall" => false, 69 | _ => true, 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /emu/cpu/mips64/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(nll)] 2 | #![feature(arbitrary_self_types)] 3 | #![feature(test)] 4 | #![feature(associated_type_defaults)] 5 | #![feature(concat_idents)] 6 | 7 | extern crate emu; 8 | extern crate num; 9 | 10 | #[macro_use] 11 | extern crate slog; 12 | 13 | mod arch; 14 | mod cp0; 15 | mod cpu; 16 | mod fpu; 17 | mod traits; 18 | 19 | pub(crate) mod decode; 20 | pub(crate) mod mmu; 21 | 22 | pub use self::arch::{ArchI, ArchII, ArchIII}; 23 | pub use self::cp0::Cp0; 24 | pub use self::cpu::{Cpu, CpuContext, Exception}; 25 | pub use self::decode::REG_NAMES; 26 | pub use self::fpu::Fpu; 27 | pub use self::traits::{Arch, Config, Cop, Cop0, CopNull}; 28 | -------------------------------------------------------------------------------- /emu/cpu/mips64/src/traits.rs: -------------------------------------------------------------------------------- 1 | use super::{CpuContext, Exception}; 2 | use emu::bus::be::Bus; 3 | use emu::dbg::{DebuggerRenderer, DecodedInsn, Result, Tracer}; 4 | use emu::memint::MemInt; 5 | 6 | /// Arch is a trait that allows to customise the MIPS core at the opcode level. 7 | /// It is used to implement different MIPS variants (architecture levels). 8 | pub trait Arch { 9 | // Returns whether the specified opcode is implemented in this architecture. 10 | // This is meant to be called always with literals and is expected to inline 11 | // and produce a compile-time boolean flag, which actually removes the 12 | // the opcode from the implementation. 13 | #[inline(always)] 14 | fn has_op(op: &'static str) -> bool; 15 | } 16 | 17 | /// Config is a trait that allows to describe the MIPS hardware-level configuration. 18 | /// It specifies the architecture, the available coprocessors, and the bus 19 | /// accesses. 20 | pub trait Config { 21 | type Arch: Arch; 22 | 23 | /// Coprocessors. If not bound, use CopNull. 24 | type Cop0: Cop0; 25 | type Cop1: Cop; 26 | type Cop2: Cop; 27 | type Cop3: Cop; 28 | 29 | // Mask PC before fetching from the bus. This should be reimplemented 30 | // by architectures that do not have a full 64-bit bus to simplify 31 | // bus mapping. 32 | fn pc_mask(pc: u32) -> u32 { 33 | pc & 0x1FFF_FFFF 34 | } 35 | 36 | // Mask addresses before reading/writing from the bus. This should be reimplemented 37 | // by architectures that do not have a full 64-bit bus to simplify 38 | // bus mapping. 39 | fn addr_mask(addr: u32) -> u32 { 40 | addr & 0x1FFF_FFFF & !(U::SIZE as u32 - 1) 41 | } 42 | } 43 | 44 | /// Cop is a MIPS64 coprocessor that can be installed within the core. 45 | pub trait Cop { 46 | fn reg(&self, cpu: &CpuContext, idx: usize) -> u128; 47 | fn set_reg(&mut self, cpu: &mut CpuContext, idx: usize, val: u128); 48 | 49 | fn op(&mut self, cpu: &mut CpuContext, opcode: u32, t: &Tracer) -> Result<()>; 50 | fn decode(&self, _opcode: u32, _pc: u64) -> DecodedInsn { 51 | DecodedInsn::new0("unkcop") 52 | } 53 | 54 | fn lwc(&mut self, op: u32, ctx: &mut CpuContext, bus: &Bus, _t: &Tracer) -> Result<()> { 55 | let rt = ((op >> 16) & 0x1f) as usize; 56 | let ea = ctx.regs[((op >> 21) & 0x1f) as usize] as u32 + (op & 0xffff) as i16 as i32 as u32; 57 | let val = bus.read::(ea & 0x1FFF_FFFC) as u64; 58 | self.set_reg(ctx, rt, val as u128); 59 | Ok(()) 60 | } 61 | 62 | fn ldc(&mut self, op: u32, ctx: &mut CpuContext, bus: &Bus, _t: &Tracer) -> Result<()> { 63 | let rt = ((op >> 16) & 0x1f) as usize; 64 | let ea = ctx.regs[((op >> 21) & 0x1f) as usize] as u32 + (op & 0xffff) as i16 as i32 as u32; 65 | let val = bus.read::(ea & 0x1FFF_FFFC) as u64; 66 | self.set_reg(ctx, rt, val as u128); 67 | Ok(()) 68 | } 69 | 70 | fn swc(&mut self, op: u32, ctx: &CpuContext, bus: &mut Bus, _t: &Tracer) -> Result<()> { 71 | let rt = ((op >> 16) & 0x1f) as usize; 72 | let ea = ctx.regs[((op >> 21) & 0x1f) as usize] as u32 + (op & 0xffff) as i16 as i32 as u32; 73 | let val = self.reg(ctx, rt) as u32; 74 | bus.write::(ea & 0x1FFF_FFFC, val); 75 | Ok(()) 76 | } 77 | 78 | fn sdc(&mut self, op: u32, ctx: &CpuContext, bus: &mut Bus, _t: &Tracer) -> Result<()> { 79 | let rt = ((op >> 16) & 0x1f) as usize; 80 | let ea = ctx.regs[((op >> 21) & 0x1f) as usize] as u32 + (op & 0xffff) as i16 as i32 as u32; 81 | let val = self.reg(ctx, rt) as u64; 82 | bus.write::(ea & 0x1FFF_FFFC, val); 83 | Ok(()) 84 | } 85 | 86 | // Implement some debugger views 87 | fn render_debug<'a, 'ui>(&mut self, _dr: &DebuggerRenderer<'a, 'ui>) {} 88 | 89 | // Internal check to efficiently handle empty coprocessors 90 | #[doc(hidden)] 91 | fn is_null() -> bool { 92 | false 93 | } 94 | #[doc(hidden)] 95 | fn is_null_obj(&self) -> bool { 96 | false 97 | } 98 | } 99 | 100 | /// Cop0 is a MIPS64 coprocessor #0, which (in addition to being a normal coprocessor) 101 | /// it is able to control execution of the core by triggering exceptions. 102 | pub trait Cop0: Cop { 103 | // Change a single external interrupt line. 104 | // Notice that hwint line 0 is mapped to bit IP2 in Cause 105 | // (because IP0/IP1 are used for software interrupts). 106 | fn set_hwint_line(&mut self, line: usize, status: bool); 107 | 108 | /// Poll pending interrupts. This function is called in the main interpreter 109 | /// loop very often, so that Cop0 has a chance of triggering interrupts 110 | /// when they are raised. 111 | /// NOTE: remember to mark this function as #[inline(always)] for maximum 112 | /// performance. 113 | fn poll_interrupts(&mut self, ctx: &mut CpuContext); 114 | 115 | /// Trigger the specified excepion. 116 | fn exception(&mut self, ctx: &mut CpuContext, exc: Exception); 117 | } 118 | 119 | pub struct CopNull {} 120 | 121 | impl Cop for CopNull { 122 | fn reg(&self, _ctx: &CpuContext, _idx: usize) -> u128 { 123 | 0 124 | } 125 | fn set_reg(&mut self, _ctx: &mut CpuContext, _idx: usize, _val: u128) {} 126 | 127 | fn op(&mut self, _cpu: &mut CpuContext, _opcode: u32, _t: &Tracer) -> Result<()> { 128 | Ok(()) 129 | } 130 | fn decode(&self, _opcode: u32, _pc: u64) -> DecodedInsn { 131 | DecodedInsn::new0("unkcop") 132 | } 133 | fn is_null() -> bool { 134 | true 135 | } 136 | fn is_null_obj(&self) -> bool { 137 | true 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /emu/emu-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "emu_derive" 3 | version = "0.0.1" 4 | authors = ["Giovanni Bajo "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | syn = "0.14" 9 | synstructure = "0.9.0" 10 | quote = "0.6" 11 | proc-macro2 = "0.4" 12 | 13 | [lib] 14 | proc-macro = true 15 | -------------------------------------------------------------------------------- /emu/examples/dbgmain.rs: -------------------------------------------------------------------------------- 1 | use emu::dbg; 2 | use emu::gfx::{GfxBufferMutLE, Rgb888}; 3 | use emu::log; 4 | use emu::snd::{SampleFormat, SndBufferMut}; 5 | use slog; 6 | use slog::*; 7 | 8 | struct FakeModel { 9 | curframe: i64, 10 | ram: Vec, 11 | rom: Vec, 12 | } 13 | 14 | impl dbg::MemoryView for FakeModel { 15 | fn name(&self) -> &str { 16 | return "Fake"; 17 | } 18 | fn banks(&self) -> Vec { 19 | vec![ 20 | dbg::MemoryBank::new("RAM", 0, 1024 * 1024 - 1, true), 21 | dbg::MemoryBank::new("ROM", 0xFFFF_0000, 0xFFFF_0000 + 64 * 1024 - 1, false), 22 | ] 23 | } 24 | 25 | fn mem_slice<'a>(&'a self, bank_idx: usize, start: u64, end: u64) -> &'a [u8] { 26 | match bank_idx { 27 | 0 => &self.ram[start as usize..=end as usize], 28 | 1 => &self.rom[(start - 0xFFFF_0000) as usize..=(end - 0xFFFF_0000) as usize], 29 | _ => unreachable!(), 30 | } 31 | } 32 | 33 | fn mem_slice_mut<'a>(&'a mut self, bank_idx: usize, start: u64, end: u64) -> &'a mut [u8] { 34 | match bank_idx { 35 | 0 => &mut self.ram[start as usize..=end as usize], 36 | 1 => &mut self.rom[(start - 0xFFFF_0000) as usize..=(end - 0xFFFF_0000) as usize], 37 | _ => unreachable!(), 38 | } 39 | } 40 | } 41 | 42 | impl dbg::DebuggerModel for FakeModel { 43 | fn all_cpus(&self) -> Vec { 44 | Vec::new() 45 | } 46 | fn cycles(&self) -> i64 { 47 | 0 48 | } 49 | fn frames(&self) -> i64 { 50 | self.curframe 51 | } 52 | fn trace_frame( 53 | &mut self, 54 | screen: &mut GfxBufferMutLE, 55 | sound: &mut SndBufferMut, 56 | tracer: &dbg::Tracer, 57 | ) -> dbg::Result<()> { 58 | self.curframe += 1; 59 | Ok(()) 60 | } 61 | fn trace_step(&mut self, cpu_name: &str, tracer: &dbg::Tracer) -> dbg::Result<()> { 62 | self.curframe += 1; 63 | Ok(()) 64 | } 65 | fn reset(&mut self, hard: bool) {} 66 | 67 | fn render_debug<'a, 'ui>(&mut self, dr: &dbg::DebuggerRenderer<'a, 'ui>) { 68 | dr.render_memoryview(self); 69 | } 70 | } 71 | 72 | fn fake_logging(logger: &slog::Logger, cnt: u32) { 73 | info!(logger, "test info"; "a" => "b", "cnt" => cnt, "@f" => cnt); 74 | warn!(logger, #"foo", "test warn first"; "a" => "b", "@f" => cnt); 75 | info!(logger, "test info"; "a" => "b", "@f" => cnt); 76 | warn!(logger, #"bar", "test warn second"; "a" => "b", "@f" => cnt); 77 | error!(logger, "test error 1"; "a" => "b", "@f" => cnt, "@pc" => "0x1234", "@sub" => "mips"); 78 | warn!(logger, #"foo", "test warn third"; "a" => "b", "@f" => cnt); 79 | error!(logger, "test error 2"; "a" => "b", "@f" => cnt); 80 | info!(logger, #"foo", "test info"; "a" => "b", "@f" => cnt); 81 | } 82 | 83 | fn rand(state: &mut u64) -> u32 { 84 | let mut x = *state; 85 | x ^= x << 13; 86 | x ^= x >> 7; 87 | x ^= x << 17; 88 | *state = x; 89 | x as u32 90 | } 91 | 92 | fn main() { 93 | let sdl_context = sdl2::init().unwrap(); 94 | let video = sdl_context.video().unwrap(); 95 | let mut event_pump = sdl_context.event_pump().unwrap(); 96 | 97 | { 98 | let gl_attr = video.gl_attr(); 99 | gl_attr.set_context_profile(sdl2::video::GLProfile::Core); 100 | gl_attr.set_context_version(3, 0); 101 | } 102 | 103 | let window = video 104 | .window("logview-demo", 1000, 1000) 105 | .position_centered() 106 | .resizable() 107 | .opengl() 108 | .allow_highdpi() 109 | .build() 110 | .unwrap(); 111 | 112 | let _gl_context = window 113 | .gl_create_context() 114 | .expect("Couldn't create GL context"); 115 | gl::load_with(|s| video.gl_get_proc_address(s) as _); 116 | 117 | let mut model = FakeModel { 118 | curframe: 0, 119 | ram: Vec::new(), 120 | rom: Vec::new(), 121 | }; 122 | 123 | model.ram.resize(1024 * 1024, 0); 124 | model.rom.resize(1024 * 1024, 0); 125 | let mut state = 0x12345678; 126 | for i in 4 * 1024..32 * 1024 { 127 | model.ram[i] = rand(&mut state) as u8; 128 | } 129 | for i in 0..1024 * 1024 { 130 | model.rom[i] = rand(&mut state) as u8; 131 | } 132 | 133 | let (logger, logpool) = log::new_pool_logger(); 134 | 135 | let mut dbgui = dbg::DebuggerUI::new(video, &window, &mut model, logpool); 136 | let mut cnt = 0; 137 | 'running: loop { 138 | use sdl2::event::Event; 139 | use sdl2::keyboard::Keycode; 140 | 141 | for event in event_pump.poll_iter() { 142 | if dbgui.handle_event(&event) { 143 | continue; 144 | } 145 | 146 | match event { 147 | Event::Quit { .. } 148 | | Event::KeyDown { 149 | keycode: Some(Keycode::Escape), 150 | .. 151 | } => break 'running, 152 | _ => {} 153 | } 154 | } 155 | 156 | for i in 0..100 { 157 | fake_logging(&logger, cnt); 158 | } 159 | cnt += 1; 160 | dbgui.render(&window, &event_pump, &mut model); 161 | window.gl_swap_window(); 162 | ::std::thread::sleep(::std::time::Duration::new(0, 1_000_000_000u32 / 30)); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /emu/src/bus/device.rs: -------------------------------------------------------------------------------- 1 | use super::bus::Bus; 2 | use crate::memint::ByteOrderCombiner; 3 | use hashbrown::HashMap; 4 | use std::any::Any; 5 | use std::cell::RefCell; 6 | use std::marker::Unpin; 7 | use std::pin::Pin; 8 | 9 | pub trait Device: Sized { 10 | type Order: ByteOrderCombiner; 11 | 12 | fn register(self: Box); 13 | 14 | fn dev_map( 15 | &self, 16 | bus: &mut Bus, 17 | bank: usize, 18 | base: u32, 19 | ) -> Result<(), &'static str>; 20 | 21 | fn tag() -> &'static str; 22 | 23 | fn get() -> &'static Self { 24 | CurrentDeviceMap().get::().unwrap() 25 | } 26 | fn get_mut() -> &'static mut Self { 27 | CurrentDeviceMap().get_mut::().unwrap() 28 | } 29 | } 30 | 31 | type PinnedDevice = Pin>; 32 | 33 | #[derive(Default)] 34 | pub struct DeviceMap { 35 | devices: HashMap<&'static str, PinnedDevice>, 36 | } 37 | 38 | impl DeviceMap { 39 | pub fn register(&mut self, o: Pin>) { 40 | self.devices.insert(D::tag(), o); 41 | } 42 | 43 | pub fn get_by_tag(&self, tag: &'static str) -> Option<&D> { 44 | self.devices 45 | .get(tag) 46 | .map(|v| (Pin::get_ref(v.as_ref()) as &Any).downcast_ref().unwrap()) 47 | } 48 | pub fn get_mut_by_tag(&mut self, tag: &'static str) -> Option<&mut D> { 49 | self.devices.get_mut(tag).map(|v| { 50 | (Pin::get_mut(v.as_mut()) as &mut dyn Any) 51 | .downcast_mut() 52 | .unwrap() 53 | }) 54 | } 55 | 56 | pub fn get(&self) -> Option<&D> { 57 | self.get_by_tag(D::tag()) 58 | } 59 | pub fn get_mut(&mut self) -> Option<&mut D> { 60 | self.get_mut_by_tag(D::tag()) 61 | } 62 | } 63 | 64 | thread_local!( 65 | static DEVICE_MAP: RefCell = RefCell::new(DeviceMap::default()) 66 | ); 67 | 68 | #[allow(non_snake_case)] 69 | pub fn CurrentDeviceMap() -> &'static mut DeviceMap { 70 | let s: *const DeviceMap = DEVICE_MAP.with(|s| &(*s.borrow()) as _); 71 | let s: *mut DeviceMap = s as *mut DeviceMap; 72 | unsafe { &mut *s } 73 | } 74 | -------------------------------------------------------------------------------- /emu/src/bus/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate byteorder; 2 | 3 | mod bus; 4 | mod device; 5 | mod mem; 6 | mod radix; 7 | mod regs; 8 | 9 | pub use self::bus::{Bus, BusFill, MemIoR, MemIoRIterator, MemIoW}; 10 | pub use self::device::{CurrentDeviceMap, Device, DeviceMap}; 11 | pub use self::mem::{Mem, MemFlags}; 12 | pub use self::regs::{Reg, RegDeref, RegFlags, RegRef}; 13 | 14 | pub mod le { 15 | use super::byteorder::LittleEndian; 16 | pub use super::{BusFill, Device, Mem, MemFlags, RegDeref, RegFlags}; 17 | pub type Bus = super::Bus; 18 | pub type Reg8 = super::Reg; 19 | pub type Reg16 = super::Reg; 20 | pub type Reg32 = super::Reg; 21 | pub type Reg64 = super::Reg; 22 | pub type RegRef = super::RegRef; 23 | pub type MemIoR = super::MemIoR; 24 | pub type MemIoW = super::MemIoW; 25 | } 26 | 27 | pub mod be { 28 | use super::byteorder::BigEndian; 29 | pub use super::{BusFill, Device, Mem, MemFlags, RegDeref, RegFlags}; 30 | pub type Bus = super::Bus; 31 | pub type Reg8 = super::Reg; 32 | pub type Reg16 = super::Reg; 33 | pub type Reg32 = super::Reg; 34 | pub type Reg64 = super::Reg; 35 | pub type RegRef = super::RegRef; 36 | pub type MemIoR = super::MemIoR; 37 | pub type MemIoW = super::MemIoW; 38 | } 39 | -------------------------------------------------------------------------------- /emu/src/dbg/miscview.rs: -------------------------------------------------------------------------------- 1 | use super::UiCtx; 2 | use imgui::*; 3 | use imgui_sys::*; 4 | use std::time::Duration; 5 | 6 | // Rendere the help tooltip showing keyboard shortcuts 7 | pub(crate) fn render_help(ui: &Ui<'_>) -> ImString { 8 | let title = ImString::new("Keyboard shortcuts"); 9 | ui.popup_modal(&title).resizable(false).build(|| { 10 | ui.text("General:"); 11 | ui.separator(); 12 | 13 | ui.bullet_text(im_str!("ESC")); 14 | ui.same_line(90.0); 15 | ui.text("Enter/exit debugger"); 16 | 17 | ui.bullet_text(im_str!("SPACE")); 18 | ui.same_line(90.0); 19 | ui.text("Start/stop emulation"); 20 | 21 | ui.spacing(); 22 | ui.spacing(); 23 | ui.text("Disasm:"); 24 | ui.separator(); 25 | 26 | ui.bullet_text(im_str!("C")); 27 | ui.same_line(90.0); 28 | ui.text("Center view"); 29 | 30 | ui.bullet_text(im_str!("S")); 31 | ui.same_line(90.0); 32 | ui.text("Step into"); 33 | 34 | ui.bullet_text(im_str!("UP/DOWN")); 35 | ui.same_line(90.0); 36 | ui.text("Move selection"); 37 | 38 | ui.bullet_text(im_str!("ENTER")); 39 | ui.same_line(90.0); 40 | ui.text("Run to selection"); 41 | 42 | ui.spacing(); 43 | ui.spacing(); 44 | if ui.button(&im_str!("Close"), [80.0, 30.0]) { 45 | ui.close_current_popup(); 46 | } 47 | }); 48 | title 49 | } 50 | 51 | // Render the flash messages 52 | pub(crate) fn render_flash_msgs(ui: &Ui<'_>, ctx: &mut UiCtx) { 53 | if ctx.flash_msg.is_none() { 54 | return; 55 | } 56 | let (msg, when) = ctx.flash_msg.as_ref().unwrap(); 57 | const CORNER: usize = 1; // top-right 58 | const DISTANCE_X: f32 = 10.0; 59 | const DISTANCE_Y: f32 = 25.0; 60 | 61 | let disp_size = ui.io().display_size; 62 | let wpos_x = if CORNER & 1 != 0 { 63 | disp_size[0] - DISTANCE_X 64 | } else { 65 | DISTANCE_X 66 | }; 67 | let wpos_y = if CORNER & 2 != 0 { 68 | disp_size[1] - DISTANCE_Y 69 | } else { 70 | DISTANCE_Y 71 | }; 72 | let pivot_x = if CORNER & 1 != 0 { 1.0 } else { 0.0 }; 73 | let pivot_y = if CORNER & 2 != 0 { 1.0 } else { 0.0 }; 74 | 75 | unsafe { 76 | igSetNextWindowPos( 77 | (wpos_x, wpos_y).into(), 78 | ImGuiCond_Always as i32, 79 | (pivot_x, pivot_y).into(), 80 | ); 81 | igSetNextWindowBgAlpha(0.5); 82 | } 83 | Window::new(&im_str!("##flash")) 84 | .resizable(false) 85 | .movable(false) 86 | .collapsible(false) 87 | .title_bar(false) 88 | .save_settings(false) 89 | .no_nav() 90 | .mouse_inputs(false) 91 | .build(ui, || { 92 | ui.text(&im_str!("{}", msg)); 93 | }); 94 | 95 | if when.elapsed() > Duration::from_secs(4) { 96 | ctx.flash_msg = None; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /emu/src/dbg/regview.rs: -------------------------------------------------------------------------------- 1 | use super::uisupport::*; 2 | use super::{RegHighlight, UiCtx}; 3 | use imgui::*; 4 | 5 | pub enum RegisterSize<'a> { 6 | Reg8(&'a mut u8), 7 | Reg16(&'a mut u16), 8 | Reg32(&'a mut u32), 9 | Reg64(&'a mut u64), 10 | Reg16x8(&'a mut [u16; 8]), 11 | } 12 | 13 | /// A trait for an object that can display register contents to 14 | /// a debugger view. 15 | pub trait RegisterView { 16 | const WINDOW_SIZE: [f32; 2]; 17 | const COLUMNS: usize; 18 | fn name<'a>(&'a self) -> &'a str; 19 | fn cpu_name<'a>(&'a self) -> &'a str; 20 | fn visit_regs<'s, F>(&'s mut self, col: usize, visit: F) 21 | where 22 | F: for<'a> FnMut(&'a str, RegisterSize<'a>, Option<&str>); 23 | } 24 | 25 | const COLOR_BG_NORMAL: [f32; 4] = [41.0 / 255.0, 74.0 / 255.0, 122.0 / 255.0, 138.0 / 255.0]; 26 | const COLOR_BG_INPUT: [f32; 4] = [86.0 / 255.0, 171.0 / 255.0, 60.0 / 255.0, 138.0 / 255.0]; 27 | const COLOR_BG_OUTPUT: [f32; 4] = [204.0 / 255.0, 61.0 / 255.0, 61.0 / 255.0, 138.0 / 255.0]; 28 | 29 | pub(crate) fn render_regview<'a, 'ui, RV: RegisterView>( 30 | ui: &'a Ui<'ui>, 31 | ctx: &mut UiCtx, 32 | v: &mut RV, 33 | ) { 34 | let disasm = ctx.disasm.get(v.cpu_name()); 35 | Window::new(&im_str!("[{}] Registers", v.name())) 36 | .size(RV::WINDOW_SIZE, Condition::FirstUseEver) 37 | .build(ui, || { 38 | // Iterate on all the columns 39 | ui.columns(RV::COLUMNS as _, im_str!("##columns"), true); 40 | for col in 0..RV::COLUMNS { 41 | // Visit regs for this column 42 | v.visit_regs(col, |rname, val, desc| { 43 | use self::RegisterSize::*; 44 | 45 | // Check if this register requires some special 46 | // highlight. 47 | let bgcolor = match disasm { 48 | None => COLOR_BG_NORMAL, 49 | Some(d) => match d.regs_highlight.get(rname) { 50 | None => COLOR_BG_NORMAL, 51 | Some(RegHighlight::Input) => COLOR_BG_INPUT, 52 | Some(RegHighlight::Output) => COLOR_BG_OUTPUT, 53 | }, 54 | }; 55 | 56 | // Draw the register box 57 | let name = &im_str!("{}", rname); 58 | let color = ui.push_style_color(StyleColor::FrameBg, bgcolor); 59 | 60 | match val { 61 | Reg8(v) => { 62 | imgui_input_hex(ui, name, v, true); 63 | } 64 | Reg16(v) => { 65 | imgui_input_hex(ui, name, v, true); 66 | } 67 | Reg32(v) => { 68 | imgui_input_hex(ui, name, v, true); 69 | } 70 | Reg64(v) => { 71 | imgui_input_hex(ui, name, v, true); 72 | } 73 | Reg16x8(v) => { 74 | let id = ui.push_id(name); 75 | let left = ui.cursor_pos()[0]; 76 | for i in 0..7 { 77 | let id = ui.push_id(i as i32); 78 | imgui_input_hex(ui, &im_str!(""), &mut v[i], true); 79 | ui.same_line(left + (i + 1) as f32 * 40.0); 80 | id.pop(&ui); 81 | } 82 | imgui_input_hex(ui, name, &mut v[7], true); 83 | id.pop(&ui); 84 | } 85 | }; 86 | if let Some(desc) = desc { 87 | ui.text(im_str!("{}", desc)); 88 | } 89 | 90 | color.pop(&ui); 91 | }); 92 | ui.next_column(); 93 | } 94 | }); 95 | } 96 | -------------------------------------------------------------------------------- /emu/src/dbg/uictx.rs: -------------------------------------------------------------------------------- 1 | use super::{MemWindow, TraceEvent}; 2 | use crate::log::{LogLine, LogView}; 3 | use imgui::ImString; 4 | 5 | use std::collections::HashMap; 6 | use std::time::Instant; 7 | 8 | // UiCommand is an action triggered by the GUI that is executed 9 | // by the main debugger loop (cannot be done while drawing the window) 10 | pub(crate) enum UiCommand { 11 | BreakpointOneShot(String, u64), // Run with a temporary breakpoint set 12 | CpuStep(String), // Step a single opcode for the specified CPU 13 | Pause(bool), // Set global pause status 14 | } 15 | 16 | pub(crate) enum RegHighlight { 17 | Input, 18 | Output, 19 | } 20 | 21 | // Global state for a disasm view 22 | #[derive(Default)] 23 | pub(crate) struct UiCtxDisasm { 24 | // Current PC on this CPU (copied here from emulator). 25 | pub cur_pc: Option, 26 | // Display a blinking effect over this PC; initialize with Instant::now. 27 | // NOTE: this only works if this PC is visible (within the scrolling area), otherwise 28 | // the animation is not performed. 29 | pub blink_pc: Option<(u64, Instant)>, 30 | // PC being currently selected by the user using cursor keys / mouse. 31 | pub cursor_pc: Option, 32 | // If set, the disasmview will automatically scroll to display this PC 33 | pub force_pc: Option, 34 | // Map of registers that must be highlighted (because are involved in cur_pc's opcode). 35 | pub regs_highlight: HashMap<&'static str, RegHighlight>, 36 | } 37 | 38 | // A command that can be requested by a log view (returned 39 | // by the render function). 40 | pub(crate) enum LogViewCommand { 41 | // User requested to see a certain PC in a specific CPU 42 | ShowPc(String, u64), 43 | } 44 | 45 | // Global state for log view 46 | pub(crate) struct UiCtxLog { 47 | pub view: LogView, 48 | pub name: String, 49 | pub cached_lines: Vec, 50 | pub cached_start_line: usize, 51 | pub last_filter_count: Instant, 52 | pub filter_count: Option, 53 | pub following: bool, 54 | pub configured_columns: bool, 55 | pub selected: LogLine, 56 | pub opened: bool, 57 | } 58 | 59 | impl UiCtxLog { 60 | pub(crate) fn new(view: LogView, name: &str) -> Box { 61 | Box::new(UiCtxLog { 62 | view, 63 | name: name.to_owned(), 64 | last_filter_count: Instant::now(), 65 | cached_lines: Vec::new(), 66 | cached_start_line: 0, 67 | filter_count: None, 68 | selected: LogLine::default(), 69 | following: true, 70 | configured_columns: false, 71 | opened: true, 72 | }) 73 | } 74 | } 75 | 76 | // Global state shared by all debugger UIs, passed to all rendere functions. 77 | // 78 | // This is useful for two main reasons: 79 | // 1) Keep local state of a ImgUi window; in C++, this is done with static variables, 80 | // but in Rust we need to store it in a different way. 81 | // 2) Propagate cross-window information (eg: specific events that affect multiple windows). 82 | #[derive(Default)] 83 | pub(crate) struct UiCtx { 84 | pub cpus: Vec, 85 | 86 | // An event that was just triggered. This is kept only for one frame. 87 | pub event: Option<(Box, Instant)>, 88 | 89 | // A command requested by the UI to the debugger 90 | pub command: Option, 91 | 92 | // Disasm views 93 | pub disasm: HashMap, 94 | 95 | // Log view 96 | pub logviews: Vec>, 97 | pub logviewid: usize, 98 | 99 | // Memory views 100 | pub memviews: HashMap, 101 | 102 | // Flash messages (auto-hide after 2s) 103 | pub flash_msg: Option<(String, Instant)>, 104 | 105 | // Error message that will be displayed in a modal 106 | pub error_msg: Option, 107 | 108 | // Popup "New breakpoint": local state 109 | pub new_bp_pc: u64, 110 | pub new_bp_desc: ImString, 111 | 112 | // Popup "New watchpoint": local state 113 | pub new_wp_addr: u64, 114 | pub new_wp_desc: ImString, 115 | pub new_wp_type: i32, 116 | pub new_wp_cond: usize, 117 | pub new_wp_value: u64, 118 | } 119 | 120 | impl UiCtx { 121 | pub fn add_flash_msg(&mut self, msg: &str) { 122 | self.flash_msg = Some((msg.to_owned(), Instant::now())); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /emu/src/gfx/geom.rs: -------------------------------------------------------------------------------- 1 | extern crate num; 2 | use self::num::PrimInt; 3 | use super::super::fp::{FixedPoint, Q}; 4 | use std::fmt; 5 | use std::ops; 6 | 7 | #[derive(Copy, Clone, Default, Eq, PartialEq)] 8 | pub struct Point { 9 | pub x: Q, 10 | pub y: Q, 11 | } 12 | 13 | impl Point { 14 | #[inline(always)] 15 | pub fn new(x: Q, y: Q) -> Self { 16 | Point { x, y } 17 | } 18 | #[inline(always)] 19 | pub fn from_int(x: N, y: N) -> Self { 20 | Self::new(Q::from_int(x), Q::from_int(y)) 21 | } 22 | #[inline(always)] 23 | pub fn from_bits(x: FP::BITS, y: FP::BITS) -> Self { 24 | Self::new(Q::from_bits(x), Q::from_bits(y)) 25 | } 26 | #[inline(always)] 27 | pub fn cast(self) -> Point { 28 | Point::new(self.x.cast(), self.y.cast()) 29 | } 30 | #[inline(always)] 31 | pub fn truncate(self) -> Point { 32 | Point::new(self.x.truncate(), self.y.truncate()) 33 | } 34 | } 35 | 36 | impl ops::Add for Point { 37 | type Output = Self; 38 | fn add(self, other: Self) -> Self { 39 | Self { 40 | x: self.x + other.x, 41 | y: self.y + other.y, 42 | } 43 | } 44 | } 45 | 46 | impl ops::Sub for Point { 47 | type Output = Self; 48 | fn sub(self, other: Self) -> Self { 49 | Self { 50 | x: self.x - other.x, 51 | y: self.y - other.y, 52 | } 53 | } 54 | } 55 | 56 | impl fmt::Debug for Point { 57 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 58 | write!(f, "Point {{ x:{:?} , y:{:?} }}", self.x, self.y) 59 | } 60 | } 61 | 62 | #[derive(Copy, Clone, Default, Eq, PartialEq)] 63 | pub struct Rect { 64 | pub c0: Point, 65 | pub c1: Point, 66 | } 67 | 68 | impl Rect { 69 | #[inline(always)] 70 | pub fn new(c0: Point, c1: Point) -> Self { 71 | Rect { c0, c1 } 72 | } 73 | 74 | #[inline(always)] 75 | pub fn from_bits(x0: FP::BITS, y0: FP::BITS, x1: FP::BITS, y1: FP::BITS) -> Self { 76 | Self::new(Point::from_bits(x0, y0), Point::from_bits(x1, y1)) 77 | } 78 | 79 | #[inline(always)] 80 | pub fn width(self) -> Q { 81 | self.c1.x - self.c0.x 82 | } 83 | #[inline(always)] 84 | pub fn height(self) -> Q { 85 | self.c1.y - self.c0.y 86 | } 87 | #[inline(always)] 88 | pub fn cast(self) -> Rect { 89 | Rect::new(self.c0.cast(), self.c1.cast()) 90 | } 91 | #[inline(always)] 92 | pub fn truncate(self) -> Rect { 93 | Rect::new(self.c0.truncate(), self.c1.truncate()) 94 | } 95 | #[inline(always)] 96 | pub fn set_width(&mut self, w: Q) { 97 | self.c1.x = self.c0.x + w; 98 | } 99 | #[inline(always)] 100 | pub fn set_height(&mut self, h: Q) { 101 | self.c1.y = self.c0.y + h; 102 | } 103 | } 104 | 105 | impl fmt::Debug for Rect { 106 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 107 | write!( 108 | f, 109 | "Rect {{ {:?},{:?} - {:?},{:?} }}", 110 | self.c0.x, self.c0.y, self.c1.x, self.c1.y 111 | ) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /emu/src/gfx/mod.rs: -------------------------------------------------------------------------------- 1 | mod buffer; 2 | mod color; 3 | mod geom; 4 | 5 | pub use self::buffer::*; 6 | pub use self::color::*; 7 | pub use self::geom::*; 8 | -------------------------------------------------------------------------------- /emu/src/hw/input_mapping.rs: -------------------------------------------------------------------------------- 1 | use crate::input::{InputDeviceKind, InputEvent, InputKind, InputManager}; 2 | 3 | use sdl2; 4 | use sdl2::keyboard::{Keycode, Scancode}; 5 | use serde_derive::{Deserialize, Serialize}; 6 | use std::collections::HashMap; 7 | 8 | /// PhysicalDevice describes how a device was mapped. 9 | #[derive(Serialize, Deserialize, Clone, Eq, PartialEq)] 10 | enum PhysicalDevice { 11 | Keyboard, 12 | Joystick(String), 13 | } 14 | 15 | #[derive(Serialize, Deserialize)] 16 | struct InputDeviceConfig { 17 | phys: PhysicalDevice, 18 | mapping: HashMap, // input name = key/joy 19 | } 20 | 21 | fn default_scancode_for_kind(kind: InputKind) -> Option { 22 | use self::InputKind::*; 23 | match kind { 24 | Start => Some(Scancode::Return), 25 | Select => Some(Scancode::Backspace), 26 | Up => Some(Scancode::Up), 27 | Down => Some(Scancode::Down), 28 | Left => Some(Scancode::Left), 29 | Right => Some(Scancode::Right), 30 | Button1 => Some(Scancode::Z), 31 | Button2 => Some(Scancode::X), 32 | Button3 => Some(Scancode::C), 33 | Button4 => Some(Scancode::V), 34 | _ => None, 35 | } 36 | } 37 | 38 | #[derive(Serialize, Deserialize)] 39 | pub struct InputConfig { 40 | devices: HashMap, // device name => mapped device 41 | } 42 | 43 | impl InputConfig { 44 | pub fn default(im: &InputManager) -> InputConfig { 45 | let mut devices = HashMap::new(); 46 | let mut first_joystick = true; 47 | 48 | im.visit(|dev| { 49 | let mut mapping = HashMap::new(); 50 | if first_joystick && dev.kind() == InputDeviceKind::Joystick { 51 | dev.visit(|inp| { 52 | if let Some(scan) = default_scancode_for_kind(inp.kind()) { 53 | let key_name = Keycode::from_scancode(scan).unwrap().name(); 54 | mapping.insert(inp.name().to_owned(), key_name); 55 | } 56 | }); 57 | first_joystick = false; 58 | } 59 | 60 | devices.insert( 61 | dev.name().to_owned(), 62 | InputDeviceConfig { 63 | phys: PhysicalDevice::Keyboard, 64 | mapping: mapping, 65 | }, 66 | ); 67 | }); 68 | 69 | InputConfig { devices } 70 | } 71 | 72 | fn all_keys(&self) -> HashMap { 73 | self.devices 74 | .iter() 75 | .filter(|(_, d)| d.phys == PhysicalDevice::Keyboard) 76 | .map(|(dev_name, d)| { 77 | d.mapping.iter().map(move |(inp_name, key_name)| { 78 | let scan = 79 | Scancode::from_keycode(Keycode::from_name(key_name).unwrap()).unwrap(); 80 | (scan, (dev_name.clone(), inp_name.clone())) 81 | }) 82 | }) 83 | .flatten() 84 | .collect() 85 | } 86 | } 87 | 88 | pub struct InputMapping { 89 | cfg: InputConfig, 90 | key_lookup: HashMap, 91 | } 92 | 93 | impl InputMapping { 94 | pub fn new(cfg: InputConfig) -> Self { 95 | let key_lookup = cfg.all_keys(); 96 | Self { cfg, key_lookup } 97 | } 98 | 99 | pub fn map_event(&self, event: &sdl2::event::Event) -> Option { 100 | use sdl2::event::Event::*; 101 | match event { 102 | KeyDown { 103 | scancode: Some(scode), 104 | .. 105 | } => match self.key_lookup.get(scode) { 106 | Some((dev, inp)) => { 107 | Some(InputEvent::Digital(dev.to_string(), inp.to_string(), true)) 108 | } 109 | None => None, 110 | }, 111 | 112 | KeyUp { 113 | scancode: Some(scode), 114 | .. 115 | } => match self.key_lookup.get(scode) { 116 | Some((dev, inp)) => { 117 | Some(InputEvent::Digital(dev.to_string(), inp.to_string(), false)) 118 | } 119 | None => None, 120 | }, 121 | 122 | _ => None, 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /emu/src/int.rs: -------------------------------------------------------------------------------- 1 | pub trait Numerics: Sized { 2 | type Unsigned: Numerics; 3 | 4 | fn isx32(self) -> i32; 5 | fn sx32(self) -> u32 { 6 | self.isx32() as u32 7 | } 8 | fn isx64(self) -> i64; 9 | fn sx64(self) -> u64 { 10 | self.isx64() as u64 11 | } 12 | fn hex(self) -> String; 13 | fn hi_lo(self) -> (Self::Unsigned, Self::Unsigned); 14 | } 15 | 16 | impl Numerics for u8 { 17 | type Unsigned = u8; 18 | 19 | #[inline(always)] 20 | fn isx32(self) -> i32 { 21 | self as i8 as i32 22 | } 23 | #[inline(always)] 24 | fn isx64(self) -> i64 { 25 | self as i8 as i64 26 | } 27 | #[inline(always)] 28 | fn hi_lo(self) -> (u8, u8) { 29 | (self >> 4, self & 0xf) 30 | } 31 | #[inline(always)] 32 | fn hex(self) -> String { 33 | format!("0x{:02x}", self) 34 | } 35 | } 36 | 37 | impl Numerics for u16 { 38 | type Unsigned = u16; 39 | 40 | #[inline(always)] 41 | fn isx32(self) -> i32 { 42 | self as i16 as i32 43 | } 44 | #[inline(always)] 45 | fn isx64(self) -> i64 { 46 | self as i16 as i64 47 | } 48 | #[inline(always)] 49 | fn hi_lo(self) -> (u16, u16) { 50 | (self >> 8, self & 0xff) 51 | } 52 | #[inline(always)] 53 | fn hex(self) -> String { 54 | format!("0x{:04x}", self) 55 | } 56 | } 57 | 58 | impl Numerics for i32 { 59 | type Unsigned = u32; 60 | 61 | #[inline(always)] 62 | fn isx32(self) -> i32 { 63 | self 64 | } 65 | #[inline(always)] 66 | fn isx64(self) -> i64 { 67 | self as i64 68 | } 69 | #[inline(always)] 70 | fn hi_lo(self) -> (u32, u32) { 71 | (self as u32).hi_lo() 72 | } 73 | #[inline(always)] 74 | fn hex(self) -> String { 75 | format!("0x{:08x}", self) 76 | } 77 | } 78 | 79 | impl Numerics for u32 { 80 | type Unsigned = u32; 81 | 82 | #[inline(always)] 83 | fn isx32(self) -> i32 { 84 | self as i32 85 | } 86 | #[inline(always)] 87 | fn isx64(self) -> i64 { 88 | self as i32 as i64 89 | } 90 | #[inline(always)] 91 | fn hi_lo(self) -> (u32, u32) { 92 | (self >> 16, self & 0xfffff) 93 | } 94 | #[inline(always)] 95 | fn hex(self) -> String { 96 | format!("0x{:08x}", self) 97 | } 98 | } 99 | 100 | impl Numerics for i64 { 101 | type Unsigned = u64; 102 | 103 | #[inline(always)] 104 | fn isx32(self) -> i32 { 105 | panic!("isx32 for i64") 106 | } 107 | #[inline(always)] 108 | fn isx64(self) -> i64 { 109 | self 110 | } 111 | #[inline(always)] 112 | fn hi_lo(self) -> (u64, u64) { 113 | (self as u64).hi_lo() 114 | } 115 | #[inline(always)] 116 | fn hex(self) -> String { 117 | format!("0x{:016x}", self) 118 | } 119 | } 120 | 121 | impl Numerics for u64 { 122 | type Unsigned = u64; 123 | 124 | #[inline(always)] 125 | fn isx32(self) -> i32 { 126 | panic!("isx32 for u64") 127 | } 128 | #[inline(always)] 129 | fn isx64(self) -> i64 { 130 | self as i64 131 | } 132 | #[inline(always)] 133 | fn hi_lo(self) -> (u64, u64) { 134 | (self >> 32, self & 0xffffffff) 135 | } 136 | #[inline(always)] 137 | fn hex(self) -> String { 138 | format!("0x{:016x}", self) 139 | } 140 | } 141 | 142 | impl Numerics for i128 { 143 | type Unsigned = u128; 144 | 145 | #[inline(always)] 146 | fn isx32(self) -> i32 { 147 | panic!("isx32 for i128") 148 | } 149 | #[inline(always)] 150 | fn isx64(self) -> i64 { 151 | panic!("i128 isx64") 152 | } 153 | #[inline(always)] 154 | fn hi_lo(self) -> (u128, u128) { 155 | (self as u128).hi_lo() 156 | } 157 | #[inline(always)] 158 | fn hex(self) -> String { 159 | format!("0x{:016x}", self) 160 | } 161 | } 162 | 163 | impl Numerics for u128 { 164 | type Unsigned = u128; 165 | 166 | #[inline(always)] 167 | fn isx32(self) -> i32 { 168 | panic!("isx32 for u128") 169 | } 170 | #[inline(always)] 171 | fn isx64(self) -> i64 { 172 | panic!("u128 isx64") 173 | } 174 | #[inline(always)] 175 | fn hi_lo(self) -> (u128, u128) { 176 | (self >> 64, self & 0xffffffff_ffffffff) 177 | } 178 | #[inline(always)] 179 | fn hex(self) -> String { 180 | format!("0x{:016x}", self) 181 | } 182 | } 183 | 184 | pub struct HexSlice<'a>(&'a [u8]); 185 | 186 | impl<'a> std::fmt::LowerHex for HexSlice<'a> { 187 | fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { 188 | for byte in self.0 { 189 | fmtr.write_fmt(format_args!("{:02x}", byte))?; 190 | } 191 | Ok(()) 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /emu/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_syntax)] 2 | #![feature(step_trait)] 3 | #![feature(specialization)] 4 | #![feature(auto_traits)] 5 | #![feature(pin)] 6 | #![feature(thread_local)] 7 | #![feature(integer_atomics)] 8 | #![feature(negative_impls)] 9 | 10 | pub mod bus; 11 | pub mod dbg; 12 | pub mod fp; 13 | pub mod gfx; 14 | pub mod hw; 15 | pub mod input; 16 | pub mod int; 17 | pub mod log; 18 | pub mod memint; 19 | pub mod snd; 20 | pub mod state; 21 | pub mod sync; 22 | -------------------------------------------------------------------------------- /emu/tests/derive.rs: -------------------------------------------------------------------------------- 1 | #![feature(pin)] 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use byteorder::LittleEndian; 6 | use emu::bus::{Bus, Device, Mem, Reg}; 7 | use emu::log::new_console_logger; 8 | use emu_derive::DeviceLE; 9 | 10 | #[derive(Default, DeviceLE)] 11 | struct Gpu { 12 | #[mem( 13 | bank = 1, 14 | offset = 0x0, 15 | size = 4194304, 16 | vsize = 0x0200_0000, 17 | fill = "Mirror" 18 | )] 19 | ram: Mem, 20 | 21 | #[reg(bank = 0, offset = 0xC, rwmask = 0xffff0000, rcb, wcb)] 22 | reg1: Reg, 23 | 24 | k1: u32, 25 | k2: u32, 26 | } 27 | 28 | impl Gpu { 29 | fn cb_write_reg1(&mut self, _old: u32, val: u32) { 30 | self.reg1.set(val | self.k1); 31 | } 32 | 33 | fn cb_read_reg1(&self, val: u32) -> u32 { 34 | val | self.k2 35 | } 36 | } 37 | 38 | #[test] 39 | fn basic_device() { 40 | Box::new(Gpu::default()).register(); 41 | 42 | let mut bus = Bus::::new(new_console_logger()); 43 | { 44 | let gpu = Gpu::get(); 45 | bus.map_device(0x04000000, gpu, 0).expect("map error"); 46 | bus.map_device(0x08000000, gpu, 1).expect("map error"); 47 | } 48 | 49 | bus.write::(0x08000123, 456); 50 | assert_eq!(bus.read::(0x09000123), 456); 51 | 52 | { 53 | let gpu = Gpu::get_mut(); 54 | gpu.k1 = 0x80; 55 | gpu.k2 = 0x1; 56 | } 57 | bus.write::(0x0400000C, 0xaaaaaaaa); 58 | assert_eq!(bus.read::(0x0400000C), 0xaaaa0081); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /shots/debugger1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/shots/debugger1.png -------------------------------------------------------------------------------- /src/cartridge.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::*; 2 | use emu::bus::be::{Mem, MemFlags, Reg32}; 3 | 4 | use crc::crc32; 5 | use std::fs::File; 6 | use std::io::Read; 7 | use std::path::Path; 8 | 9 | #[derive(DeviceBE)] 10 | pub struct Cartridge { 11 | #[mem(offset = 0, vsize = 0x07C0_0000, fill = "Fixed(0x00)")] 12 | rom: Mem, 13 | 14 | #[reg(bank = 1, offset = 0x200)] 15 | drive64_status: Reg32, 16 | 17 | #[reg(bank = 1, offset = 0x208)] 18 | drive64_cmd: Reg32, 19 | } 20 | 21 | pub enum CicModel { 22 | Cic6101 = 6101, 23 | Cic6102 = 6102, 24 | Cic6103 = 6103, 25 | Cic6105 = 6105, 26 | Cic6106 = 6106, 27 | } 28 | 29 | pub fn romswap(rom: Vec) -> Vec { 30 | if rom[0] == 0x80 { 31 | // ROM is big-endian: nothing to do 32 | return rom; 33 | } else if rom[1] == 0x80 { 34 | // ROM is byteswapped 35 | return rom 36 | .iter() 37 | .enumerate() 38 | .map(|(idx, _)| rom[idx ^ 1]) 39 | .collect(); 40 | } else { 41 | panic!("unsupported ROM format") 42 | } 43 | } 44 | 45 | impl Cartridge { 46 | pub fn new(romfn: &Path) -> Result> { 47 | let mut file = File::open(romfn)?; 48 | let mut contents = vec![]; 49 | file.read_to_end(&mut contents)?; 50 | 51 | if !contents.len().is_power_of_two() { 52 | let newsize = contents.len().next_power_of_two(); 53 | contents.resize(newsize, 0xff); 54 | } 55 | 56 | Ok(Box::new(Cartridge { 57 | drive64_status: Reg32::default(), 58 | drive64_cmd: Reg32::default(), 59 | rom: Mem::from_buffer("rom", romswap(contents), MemFlags::READACCESS), 60 | })) 61 | } 62 | 63 | // Detect the CIC model by checksumming the header of the ROM. 64 | pub fn detect_cic_model(&self) -> Result { 65 | match crc32::checksum_ieee(&self.rom[0x40..0x1000]) { 66 | 0x6170A4A1 => Ok(CicModel::Cic6101), 67 | 0x90BB6CB5 => Ok(CicModel::Cic6102), 68 | 0x0B050EE0 => Ok(CicModel::Cic6103), 69 | 0x98BC2C86 => Ok(CicModel::Cic6105), 70 | 0xACC8580A => Ok(CicModel::Cic6106), 71 | chk => bail!("cannot detect CIC model in ROM (chk = {:08x})", chk), 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(nll)] 2 | #![feature(stdsimd)] 3 | #![feature(pin)] 4 | 5 | #[macro_use] 6 | extern crate slog; 7 | 8 | #[macro_use] 9 | extern crate emu_derive; 10 | extern crate byteorder; 11 | extern crate emu; 12 | extern crate mips64; 13 | 14 | extern crate packed_simd; 15 | 16 | #[macro_use] 17 | extern crate bitflags; 18 | 19 | #[macro_use] 20 | extern crate error_chain; 21 | 22 | pub mod errors { 23 | error_chain! { 24 | foreign_links { 25 | Io(::std::io::Error); 26 | } 27 | } 28 | } 29 | 30 | mod rdp; 31 | 32 | pub mod ai; 33 | pub mod cartridge; 34 | pub mod dp; 35 | pub mod mi; 36 | pub mod pi; 37 | pub mod r4300; 38 | pub mod ri; 39 | pub mod si; 40 | pub mod sp; 41 | pub mod vi; 42 | 43 | mod n64; 44 | pub use self::n64::N64; 45 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate error_chain; 3 | 4 | use emu::dbg; 5 | use emu::hw; 6 | use emu::log; 7 | use r64emu::errors::*; 8 | use r64emu::N64; 9 | 10 | use std::path::Path; 11 | 12 | use structopt::StructOpt; 13 | 14 | #[derive(StructOpt)] 15 | #[structopt(raw(setting = "structopt::clap::AppSettings::ColoredHelp"))] 16 | struct Cli { 17 | /// Activate debugger at start 18 | #[structopt(short = "d", long = "debugger")] 19 | debugger: bool, 20 | 21 | /// Path to the BIOS file 22 | #[structopt( 23 | short = "b", 24 | long = "bios", 25 | parse(from_os_str), 26 | default_value = "bios/pifdata.bin" 27 | )] 28 | bios: std::path::PathBuf, 29 | 30 | /// Path to the ROM file 31 | #[structopt(parse(from_os_str))] 32 | rom: std::path::PathBuf, 33 | } 34 | 35 | quick_main!(run); 36 | 37 | fn create_n64(romfn: &Path, biosfn: &Path, logger: slog::Logger) -> Result { 38 | let mut n64 = N64::new(logger, romfn, biosfn).unwrap(); 39 | n64.setup_cic(true)?; 40 | Ok(n64) 41 | } 42 | 43 | fn run() -> Result<()> { 44 | let args = Cli::from_args(); 45 | 46 | let mut out = hw::Output::new( 47 | hw::VideoConfig { 48 | window_title: "R64EMU - Nintendo 64 Emulator".into(), 49 | width: 640, 50 | height: 480, 51 | fps: 60, 52 | }, 53 | hw::AudioConfig { 54 | frequency: N64::AUDIO_OUTPUT_FREQUENCY as isize, 55 | }, 56 | )?; 57 | out.enable_video()?; 58 | out.enable_audio()?; 59 | 60 | if args.debugger { 61 | let (logger, logpool) = log::new_pool_logger(); 62 | let mut n64 = create_n64(&args.rom, &args.bios, logger).unwrap(); 63 | let mut dbgconfig = args.rom.clone(); 64 | dbgconfig.set_extension("dbg"); 65 | out.run_and_debug(&mut n64, &dbgconfig, logpool); 66 | } else { 67 | out.run_threaded(move || { 68 | let logger = log::new_console_logger(); 69 | let n64 = create_n64(&args.rom, &args.bios, logger).unwrap(); 70 | Ok(Box::new(n64)) 71 | }); 72 | } 73 | 74 | Ok(()) 75 | } 76 | -------------------------------------------------------------------------------- /src/mi.rs: -------------------------------------------------------------------------------- 1 | use super::r4300::R4300; 2 | use emu::bus::be::{Device, Reg32}; 3 | use emu::int::Numerics; 4 | use mips64::Cop0; 5 | 6 | use bit_field::BitField; 7 | use bitflags::bitflags; 8 | use slog; 9 | 10 | bitflags! { 11 | pub struct IrqMask: u32 { 12 | const SP = 0b00000001; 13 | const SI = 0b00000010; 14 | const AI = 0b00000100; 15 | const VI = 0b00001000; 16 | const PI = 0b00010000; 17 | const DP = 0b00100000; 18 | } 19 | } 20 | 21 | #[derive(DeviceBE)] 22 | pub struct Mi { 23 | // 0x04300000 to 0x04300003 MI_INIT_MODE_REG or MI_MODE_REG //MI init mode 24 | // (W): [0-6] init length (R): [0-6] init length 25 | // [7] clear init mode [7] init mode 26 | // [8] set init mode [8] ebus test mode 27 | // [9/10] clr/set ebus test mode [9] RDRAM reg mode 28 | // [11] clear DP interrupt 29 | // [12] clear RDRAM reg 30 | // [13] set RDRAM reg mode 31 | #[reg(offset = 0x00, wcb)] 32 | reg_mode: Reg32, 33 | 34 | #[reg(offset = 0x04, init = 0x02020102, readonly)] 35 | reg_version: Reg32, 36 | 37 | #[reg(offset = 0x08, readonly)] 38 | irq_ack: Reg32, 39 | 40 | #[reg(offset = 0x0C, wcb)] 41 | irq_mask: Reg32, 42 | 43 | logger: slog::Logger, 44 | } 45 | 46 | impl Mi { 47 | pub fn new(logger: slog::Logger) -> Box { 48 | Box::new(Mi { 49 | reg_mode: Reg32::default(), 50 | irq_ack: Reg32::default(), 51 | irq_mask: Reg32::default(), 52 | reg_version: Reg32::default(), 53 | logger, 54 | }) 55 | } 56 | 57 | fn cb_write_reg_mode(&mut self, old: u32, new: u32) { 58 | let mut mode = old; 59 | 60 | mode.set_bits(0..7, new.get_bits(0..7)); 61 | 62 | if new.get_bit(7) { 63 | // clear init mode 64 | mode.set_bit(7, false); 65 | } 66 | if new.get_bit(8) { 67 | // set init mode 68 | mode.set_bit(7, true); 69 | } 70 | if new.get_bit(9) { 71 | // clear ebus 72 | mode.set_bit(8, false); 73 | } 74 | if new.get_bit(10) { 75 | // set ebus 76 | mode.set_bit(8, true); 77 | } 78 | if new.get_bit(11) { 79 | // clear RDP interrupt 80 | self.set_irq_line(IrqMask::DP, false); 81 | } 82 | if new.get_bit(12) { 83 | // clear ebus 84 | mode.set_bit(9, false); 85 | } 86 | if new.get_bit(13) { 87 | // set ebus 88 | mode.set_bit(9, true); 89 | } 90 | self.reg_mode.set(mode); 91 | info!(self.logger, "written reg_mode"; "mode" => mode.hex()); 92 | } 93 | 94 | pub fn set_irq_line(&mut self, lines: IrqMask, status: bool) { 95 | let old = self.irq_ack.get(); 96 | let new = if status { 97 | old | lines.bits() 98 | } else { 99 | old & !lines.bits() 100 | }; 101 | self.irq_ack.set(new); 102 | 103 | if old != new { 104 | info!(self.logger, "changed IRQ ack"; "irq" => ?IrqMask::from_bits(new)); 105 | } 106 | self.update_cpu_irq(); 107 | } 108 | 109 | fn cb_write_irq_mask(&mut self, old: u32, new: u32) { 110 | let mut mask = old; 111 | for i in 0..12 { 112 | if new.get_bit(i) { 113 | mask.set_bit(i / 2, i % 2 != 0); 114 | } 115 | } 116 | self.irq_mask.set(mask); 117 | if old != mask { 118 | info!(self.logger, "changed IRQ mask"; "irq" => ?IrqMask::from_bits(mask)); 119 | } 120 | self.update_cpu_irq(); 121 | } 122 | 123 | fn update_cpu_irq(&self) { 124 | R4300::get_mut() 125 | .cop0 126 | .set_hwint_line(0, (self.irq_ack.get() & self.irq_mask.get()) != 0); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/r4300.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use mips64; 3 | use emu::bus::be::{Bus, Device}; 4 | 5 | use super::n64::MAINCPU_NAME; 6 | use super::ai::Ai; 7 | use super::cartridge::{Cartridge, CicModel}; 8 | use super::dp::Dp; 9 | use super::errors::*; 10 | use super::mi::Mi; 11 | use super::pi::Pi; 12 | use super::ri::Ri; 13 | use super::si::Si; 14 | use super::sp::{Sp, RSPCPU}; 15 | use super::vi::Vi; 16 | 17 | pub struct R4300Config; 18 | 19 | impl mips64::Config for R4300Config { 20 | type Arch = mips64::ArchIII; // 64-bit MIPS III architecture 21 | type Cop0 = mips64::Cp0; 22 | type Cop1 = mips64::Fpu; 23 | type Cop2 = mips64::CopNull; 24 | type Cop3 = mips64::CopNull; 25 | } 26 | 27 | #[derive(DeviceBE)] 28 | pub struct R4300 { 29 | cpu: mips64::Cpu, 30 | } 31 | 32 | impl Deref for R4300 { 33 | type Target = mips64::Cpu; 34 | fn deref(&self) -> &Self::Target { 35 | &self.cpu 36 | } 37 | } 38 | 39 | impl DerefMut for R4300 { 40 | fn deref_mut(&mut self) -> &mut Self::Target { 41 | &mut self.cpu 42 | } 43 | } 44 | 45 | impl R4300 { 46 | pub fn new(logger: slog::Logger) -> Box { 47 | Box::new(R4300 { 48 | cpu: mips64::Cpu::new( 49 | MAINCPU_NAME, 50 | logger.new(o!()), 51 | Bus::new(logger.new(o!())), 52 | ( 53 | mips64::Cp0::new("R4300-COP0", logger.new(o!())), 54 | mips64::Fpu::new("R4300-FPU", logger.new(o!())), 55 | mips64::CopNull {}, 56 | mips64::CopNull {}, 57 | ), 58 | ), 59 | }) 60 | } 61 | 62 | pub fn map_bus(&mut self) -> Result<()> { 63 | self.bus.map_device(0x0000_0000, Ri::get(), 0)?; 64 | self.bus.map_device(0x03F0_0000, Ri::get(), 1)?; 65 | self.bus.map_device(0x0400_0000, Sp::get(), 0)?; 66 | self.bus.map_device(0x0404_0000, Sp::get(), 1)?; 67 | self.bus.map_device(0x0408_0000, Sp::get(), 2)?; 68 | self.bus.map_device(0x0410_0000, Dp::get(), 0)?; 69 | self.bus.map_device(0x0430_0000, Mi::get(), 0)?; 70 | self.bus.map_device(0x0440_0000, Vi::get(), 0)?; 71 | self.bus.map_device(0x0450_0000, Ai::get(), 0)?; 72 | self.bus.map_device(0x0460_0000, Pi::get(), 0)?; 73 | self.bus.map_device(0x0470_0000, Ri::get(), 2)?; 74 | self.bus.map_device(0x0480_0000, Si::get(), 0)?; 75 | self.bus.map_device(0x1000_0000, Cartridge::get(), 0)?; 76 | self.bus.map_device(0x1800_0000, Cartridge::get(), 1)?; 77 | self.bus.map_device(0x1FC0_0000, Pi::get(), 1)?; 78 | Ok(()) 79 | } 80 | } -------------------------------------------------------------------------------- /src/rdp/bl.rs: -------------------------------------------------------------------------------- 1 | // Blender 2 | 3 | // TODO: 4 | // * alpha compare 5 | 6 | extern crate bit_field; 7 | extern crate emu; 8 | 9 | use self::bit_field::BitField; 10 | use super::{MColor, MultiColor}; 11 | use emu::gfx::{Color, Rgba8888}; 12 | use std::ptr; 13 | 14 | struct BlenderCycle { 15 | p: *const MultiColor, 16 | m: *const MultiColor, 17 | a: *const MultiColor, 18 | b: *const MultiColor, 19 | } 20 | 21 | impl BlenderCycle { 22 | fn fetch(&self) -> (MultiColor, MultiColor, MultiColor, MultiColor) { 23 | unsafe { (*self.p, *self.m, *self.a, *self.b) } 24 | } 25 | } 26 | 27 | impl Default for BlenderCycle { 28 | fn default() -> Self { 29 | BlenderCycle { 30 | p: ptr::null(), 31 | m: ptr::null(), 32 | a: ptr::null(), 33 | b: ptr::null(), 34 | } 35 | } 36 | } 37 | 38 | #[derive(Default)] 39 | pub(crate) struct Blender { 40 | combined: MultiColor, 41 | shade: MultiColor, 42 | inv_combined: MultiColor, 43 | partial_blended: MultiColor, 44 | framebuffer: MultiColor, 45 | reg_blend: MultiColor, 46 | reg_fog: MultiColor, 47 | 48 | zero: MultiColor, // 0x00 49 | ff: MultiColor, // 0xFF 50 | 51 | cycles: [BlenderCycle; 2], 52 | } 53 | 54 | impl Blender { 55 | pub(crate) fn new() -> Blender { 56 | Blender { 57 | ff: MultiColor::splat(0xff), 58 | ..Default::default() 59 | } 60 | } 61 | 62 | #[inline(always)] 63 | pub(crate) fn blend_1cycle( 64 | &mut self, 65 | combined: MultiColor, 66 | _shade: MultiColor, 67 | fb: MultiColor, 68 | ) -> MultiColor { 69 | self.combined = combined; 70 | self.inv_combined = combined.map_alpha(|a| 0xFF - a); 71 | self.framebuffer = fb; 72 | 73 | let (p, m, a, b) = self.cycles[0].fetch(); 74 | let a = a.replicate_alpha() >> 3; 75 | let b = (b.replicate_alpha() >> 3) + MultiColor::splat(1); 76 | 77 | (p * a + m * b) / (a + b) 78 | } 79 | 80 | pub(crate) unsafe fn setup_cycle_pm(&self, cyc: usize, p_or_m: u32) -> *const MultiColor { 81 | match p_or_m { 82 | 0 => { 83 | if cyc == 0 { 84 | &self.combined 85 | } else { 86 | &self.partial_blended 87 | } 88 | } 89 | 1 => &self.framebuffer, 90 | 2 => &self.reg_blend, 91 | 3 => &self.reg_fog, 92 | _ => unreachable!(), 93 | } 94 | } 95 | 96 | unsafe fn setup_cycle(&self, cyc: usize, (p, m, a, b): (u32, u32, u32, u32)) -> BlenderCycle { 97 | BlenderCycle { 98 | p: self.setup_cycle_pm(cyc, p), 99 | m: self.setup_cycle_pm(cyc, m), 100 | a: match a { 101 | 0 => &self.combined, 102 | 1 => &self.reg_fog, 103 | 2 => &self.shade, 104 | 3 => &self.zero, 105 | _ => unreachable!(), 106 | }, 107 | b: match b { 108 | 0 => &self.inv_combined, 109 | 1 => &self.framebuffer, 110 | 2 => &self.ff, 111 | 3 => &self.zero, 112 | _ => unreachable!(), 113 | }, 114 | } 115 | } 116 | 117 | pub(crate) fn set_other_modes(&mut self, modes: u64) { 118 | let p = modes.get_bits(30..32) as u32; 119 | let m = modes.get_bits(26..28) as u32; 120 | let a = modes.get_bits(22..24) as u32; 121 | let b = modes.get_bits(18..20) as u32; 122 | self.cycles[0] = unsafe { self.setup_cycle(0, (p, m, a, b)) }; 123 | 124 | let p = modes.get_bits(28..30) as u32; 125 | let m = modes.get_bits(24..26) as u32; 126 | let a = modes.get_bits(20..22) as u32; 127 | let b = modes.get_bits(16..18) as u32; 128 | self.cycles[1] = unsafe { self.setup_cycle(1, (p, m, a, b)) }; 129 | } 130 | 131 | pub(crate) fn set_fog_color(&mut self, c: Color) { 132 | self.reg_fog = MultiColor::from_color(c); 133 | } 134 | pub(crate) fn set_blend_color(&mut self, c: Color) { 135 | self.reg_blend = MultiColor::from_color(c); 136 | } 137 | 138 | pub(crate) fn repr_comb_ptr(&self, ptr: *const MultiColor, alpha: bool) -> String { 139 | if ptr == &self.combined { 140 | (if alpha { "input.a" } else { "input" }).into() 141 | } else if ptr == &self.inv_combined { 142 | "(1.0 - input.a)".into() 143 | } else if ptr == &self.reg_fog { 144 | (if alpha { "reg_fog.a" } else { "reg_fog" }).into() 145 | } else if ptr == &self.framebuffer { 146 | (if alpha { "fb.a" } else { "fb" }).into() 147 | } else if ptr == &self.reg_blend { 148 | "reg_blend".into() 149 | } else if ptr == &self.shade { 150 | "shade".into() 151 | } else if ptr == &self.zero { 152 | "0.0".into() 153 | } else if ptr == &self.ff { 154 | "1.0".into() 155 | } else { 156 | "?".into() 157 | } 158 | } 159 | 160 | pub(crate) fn fmt_1cycle(&self) -> String { 161 | let a = self.repr_comb_ptr(self.cycles[0].a, true); 162 | let b = self.repr_comb_ptr(self.cycles[0].b, true); 163 | format!( 164 | "Blender {{ ({}*{} + {}*{}) / ({}+{}) }}", 165 | self.repr_comb_ptr(self.cycles[0].p, false), 166 | a, 167 | self.repr_comb_ptr(self.cycles[0].m, false), 168 | b, 169 | a, 170 | b, 171 | ) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/rdp/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate byteorder; 2 | extern crate emu; 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | use emu::gfx::{Color, ColorConverter, ColorFormat, Rgba8888}; 5 | use packed_simd::*; 6 | use std::arch::x86_64::*; 7 | 8 | type MultiColor = u16x8; 9 | 10 | pub(crate) trait MColor: Sized + Copy { 11 | fn from_color(c: Color) -> Self; 12 | fn get_color(&self, idx: usize) -> Color; 13 | fn map_alpha(self, f: fn(u16) -> u16) -> Self; 14 | fn replace_alpha(self, alpha: Self) -> Self; 15 | fn replicate_alpha(self) -> Self; 16 | fn overflown(self) -> bool; 17 | } 18 | 19 | impl MColor for MultiColor { 20 | fn from_color(c: Color) -> Self { 21 | let (r, g, b, a) = c.components(); 22 | u16x8::new( 23 | r as u16, g as u16, b as u16, a as u16, r as u16, g as u16, b as u16, a as u16, 24 | ) 25 | } 26 | 27 | fn overflown(self) -> bool { 28 | (self & MultiColor::splat(0xFF)) != self 29 | } 30 | 31 | fn get_color(&self, idx: usize) -> Color { 32 | // Rust does not expose a _mm_pack* functions through the uAAxBB SIMD 33 | // structs, so there is no way to convert from u16x8 to u8x16 without 34 | // using scalar code. The following code is able to keep it fully 35 | // vectorized, and generate a final "MOVD XMM" instruction to 36 | // extract the required color index. 37 | let c = unsafe { 38 | let c = __m128i::from_bits(*self); 39 | let c = _mm_packus_epi16(c, _mm_setzero_si128()); 40 | u8x16::from_bits(c) 41 | }; 42 | let mut cbuf: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 43 | c.write_to_slice_unaligned(&mut cbuf); 44 | match idx { 45 | 0 => Color::::from_bits(LittleEndian::read_u32(&cbuf[0..4])).cconv(), 46 | 1 => Color::::from_bits(LittleEndian::read_u32(&cbuf[4..8])).cconv(), 47 | _ => panic!("invalid MultiColor index"), 48 | } 49 | } 50 | 51 | fn map_alpha(self, f: fn(u16) -> u16) -> Self { 52 | let a1 = self.extract(3); 53 | let a2 = self.extract(7); 54 | self.replace(3, f(a1)).replace(7, f(a2)) 55 | } 56 | fn replace_alpha(self, alpha: Self) -> Self { 57 | self.replace(3, alpha.extract(3)) 58 | .replace(7, alpha.extract(7)) 59 | } 60 | fn replicate_alpha(self) -> Self { 61 | let a1 = self.extract(3); 62 | let a2 = self.extract(7); 63 | self.replace(0, a1) 64 | .replace(1, a1) 65 | .replace(2, a1) 66 | .replace(4, a2) 67 | .replace(5, a2) 68 | .replace(6, a2) 69 | } 70 | } 71 | 72 | #[derive(Copy, Clone, Debug)] 73 | pub(crate) enum CycleMode { 74 | One, 75 | Two, 76 | Copy, 77 | Fill, 78 | } 79 | 80 | #[derive(Copy, Clone, Debug)] 81 | pub(crate) enum DpColorFormat { 82 | Rgba, 83 | Yuv, 84 | ColorIndex, 85 | IntensityAlpha, 86 | Intensity, 87 | } 88 | 89 | impl Default for DpColorFormat { 90 | fn default() -> DpColorFormat { 91 | DpColorFormat::Rgba 92 | } 93 | } 94 | 95 | impl DpColorFormat { 96 | pub fn from_bits(bits: usize) -> Option { 97 | match bits { 98 | 0 => Some(DpColorFormat::Rgba), 99 | 1 => Some(DpColorFormat::Yuv), 100 | 2 => Some(DpColorFormat::ColorIndex), 101 | 3 => Some(DpColorFormat::IntensityAlpha), 102 | 4 => Some(DpColorFormat::Intensity), 103 | _ => None, 104 | } 105 | } 106 | } 107 | 108 | mod bl; 109 | mod cc; 110 | mod pipeline; 111 | mod raster; 112 | mod rdp; 113 | 114 | pub use self::pipeline::PixelPipeline; 115 | pub use self::rdp::Rdp; 116 | -------------------------------------------------------------------------------- /src/rdp/pipeline.rs: -------------------------------------------------------------------------------- 1 | extern crate emu; 2 | use super::bl::Blender; 3 | use super::cc::Combiner; 4 | use super::MultiColor; 5 | use emu::gfx::{Color, Rgba8888}; 6 | 7 | pub struct PixelPipeline { 8 | cc: Combiner, 9 | bl: Blender, 10 | } 11 | 12 | impl PixelPipeline { 13 | pub fn new() -> PixelPipeline { 14 | PixelPipeline { 15 | cc: Combiner::new(), 16 | bl: Blender::new(), 17 | } 18 | } 19 | 20 | #[inline(always)] 21 | pub fn calc_pixels(&mut self, shade: MultiColor, fb: MultiColor) -> MultiColor { 22 | self.cc.set_tex0(shade); 23 | let combined = self.cc.combine_1cycle(shade); 24 | let blended = self.bl.blend_1cycle(combined, shade, fb); 25 | return blended; 26 | } 27 | 28 | pub fn set_combine_mode(&mut self, mode: u64) { 29 | self.cc.set_mode(mode); 30 | } 31 | pub fn set_prim_color(&mut self, c: Color) { 32 | self.cc.set_prim(c); 33 | } 34 | pub fn set_env_color(&mut self, c: Color) { 35 | self.cc.set_env(c); 36 | } 37 | pub fn set_blend_color(&mut self, c: Color) { 38 | self.bl.set_blend_color(c); 39 | } 40 | pub fn set_other_modes(&mut self, modes: u64) { 41 | self.bl.set_other_modes(modes); 42 | } 43 | 44 | pub fn fmt_combiner(&self) -> String { 45 | self.cc.fmt_1cycle() 46 | } 47 | pub fn fmt_blender(&self) -> String { 48 | self.bl.fmt_1cycle() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/ri.rs: -------------------------------------------------------------------------------- 1 | extern crate emu; 2 | extern crate slog; 3 | use emu::bus::be::{Mem, Reg32}; 4 | 5 | /// RDRAM 6 | #[derive(DeviceBE)] 7 | pub struct Ri { 8 | #[mem(bank = 0, size = 4194304, offset = 0x0000_0000)] 9 | pub(crate) rdram: Mem, 10 | 11 | #[reg(bank = 1, offset = 0x00)] 12 | reg_rdram_config: Reg32, 13 | 14 | #[reg(bank = 1, offset = 0x04)] 15 | reg_rdram_device_id: Reg32, 16 | 17 | #[reg(bank = 1, offset = 0x08)] 18 | reg_rdram_delay: Reg32, 19 | 20 | #[reg(bank = 1, offset = 0x0C, readonly)] 21 | reg_rdram_mode: Reg32, 22 | 23 | #[reg(bank = 1, offset = 0x10)] 24 | reg_rdram_interval: Reg32, 25 | 26 | #[reg(bank = 1, offset = 0x14)] 27 | reg_rdram_ref_row: Reg32, 28 | 29 | #[reg(bank = 1, offset = 0x18)] 30 | reg_rdram_ras_interval: Reg32, 31 | 32 | #[reg(bank = 1, offset = 0x1C)] 33 | reg_rdram_min_interval: Reg32, 34 | 35 | #[reg(bank = 1, offset = 0x20)] 36 | reg_rdram_addr_select: Reg32, 37 | 38 | #[reg(bank = 1, offset = 0x24)] 39 | reg_rdram_device_manuf: Reg32, 40 | 41 | // [1:0] operating mode 42 | // [2] stop T active 43 | // [3] stop R active 44 | #[reg(bank = 2, offset = 0x00, rwmask = 0xF)] 45 | reg_ri_mode: Reg32, 46 | 47 | // [5:0] current control input 48 | // [6] current control enable 49 | #[reg(bank = 2, offset = 0x04, rwmask = 0x3F)] 50 | reg_ri_config: Reg32, 51 | 52 | // (W): [] any write updates current control register 53 | #[reg(bank = 2, offset = 0x08, writeonly)] 54 | reg_ri_current_load: Reg32, 55 | 56 | // [2:0] receive select 57 | // [2:0] transmit select 58 | #[reg(bank = 2, offset = 0x0C, rwmask = 0xF)] 59 | reg_ri_select: Reg32, 60 | 61 | // [7:0] clean refresh delay 62 | // [15:8] dirty refresh delay 63 | // [16] refresh bank 64 | // [17] refresh enable 65 | // [18] refresh optimize 66 | #[reg(bank = 2, offset = 0x10, rwmask = 0x7FFFF)] 67 | reg_ri_refresh: Reg32, 68 | 69 | // [3:0] DMA latency/overlap 70 | #[reg(bank = 2, offset = 0x14, rwmask = 0xF)] 71 | reg_ri_latency: Reg32, 72 | 73 | // (R): [0] nack error 74 | // [1] ack error 75 | #[reg(bank = 2, offset = 0x18, rwmask = 0x2, readonly)] 76 | reg_ri_error: Reg32, 77 | 78 | // (W): [] any write clears all error bits 79 | #[reg(bank = 2, offset = 0x1C, writeonly)] 80 | reg_ri_error_write: Reg32, 81 | 82 | _logger: slog::Logger, 83 | } 84 | 85 | impl Ri { 86 | pub fn new(logger: slog::Logger) -> Box { 87 | Box::new(Ri { 88 | rdram: Mem::default(), 89 | 90 | reg_rdram_config: Reg32::default(), 91 | reg_rdram_device_id: Reg32::default(), 92 | reg_rdram_delay: Reg32::default(), 93 | reg_rdram_mode: Reg32::default(), 94 | reg_rdram_interval: Reg32::default(), 95 | reg_rdram_ref_row: Reg32::default(), 96 | reg_rdram_ras_interval: Reg32::default(), 97 | reg_rdram_min_interval: Reg32::default(), 98 | reg_rdram_addr_select: Reg32::default(), 99 | reg_rdram_device_manuf: Reg32::default(), 100 | 101 | reg_ri_mode: Reg32::default(), 102 | reg_ri_config: Reg32::default(), 103 | reg_ri_current_load: Reg32::default(), 104 | reg_ri_select: Reg32::default(), 105 | reg_ri_refresh: Reg32::default(), 106 | reg_ri_latency: Reg32::default(), 107 | reg_ri_error: Reg32::default(), 108 | reg_ri_error_write: Reg32::default(), 109 | 110 | _logger: logger, 111 | }) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/si.rs: -------------------------------------------------------------------------------- 1 | use slog; 2 | 3 | use super::mi::{IrqMask, Mi}; 4 | use super::r4300::R4300; 5 | use super::pi::Pi; 6 | 7 | use emu::bus::be::Reg32; 8 | use emu::bus::Device; 9 | use emu::int::Numerics; 10 | use emu_derive::DeviceBE; 11 | 12 | #[derive(DeviceBE)] 13 | pub struct Si { 14 | #[reg(bank = 0, offset = 0x00)] 15 | dma_address: Reg32, 16 | 17 | #[reg(bank = 0, offset = 0x04, writeonly, wcb)] 18 | start_dma_read: Reg32, 19 | 20 | #[reg(bank = 0, offset = 0x10, writeonly, wcb)] 21 | start_dma_write: Reg32, 22 | 23 | // (W): [] any write clears interrupt 24 | // (R): [0] DMA busy 25 | // [1] IO read busy 26 | // [2] reserved 27 | // [3] DMA error 28 | // [12] interrupt 29 | #[reg(bank = 0, offset = 0x18, rwmask = 0, wcb)] 30 | status: Reg32, 31 | 32 | logger: slog::Logger, 33 | } 34 | 35 | impl Si { 36 | pub fn new(logger: slog::Logger) -> Box { 37 | Box::new(Si { 38 | status: Reg32::default(), 39 | dma_address: Reg32::default(), 40 | start_dma_read: Reg32::default(), 41 | start_dma_write: Reg32::default(), 42 | logger, 43 | }) 44 | } 45 | 46 | pub(crate) fn set_busy(&mut self, busy: bool) { 47 | let mut status = self.status.get(); 48 | if busy { 49 | status |= 1 << 1; 50 | } else { 51 | status &= !(1 << 1); 52 | } 53 | self.status.set(status); 54 | } 55 | 56 | pub(crate) fn raise_irq(&mut self) { 57 | let status = self.status.get(); 58 | self.status.set(status | (1 << 12)); 59 | Mi::get_mut().set_irq_line(IrqMask::SI, true); 60 | } 61 | 62 | fn cb_write_status(&mut self, old: u32, new: u32) { 63 | // Any write to SI status clears the IRQ line 64 | self.status.set(old & !(1 << 12)); 65 | Mi::get_mut().set_irq_line(IrqMask::SI, false); 66 | 67 | info!(self.logger, "write SI status reg"; "val" => new.hex()); 68 | } 69 | 70 | fn cb_write_start_dma_read(&mut self, _old: u32, new: u32) { 71 | let mut src = new; 72 | let mut dst = self.dma_address.get(); 73 | info!(self.logger, "SI DMA read"; "pifram" => src.hex(), "rdram" => dst.hex()); 74 | 75 | let bus = &mut R4300::get_mut().bus; 76 | for _ in 0..16 { 77 | let val = bus.read::(src); 78 | bus.write::(dst, val); 79 | src += 4; 80 | dst += 4; 81 | } 82 | self.raise_irq(); 83 | } 84 | 85 | fn cb_write_start_dma_write(&mut self, _old: u32, new: u32) { 86 | let mut src = self.dma_address.get(); 87 | let mut dst = new; 88 | info!(self.logger, "SI DMA write"; "rdram" => src.hex(), "pifram" => dst.hex()); 89 | 90 | let bus = &mut R4300::get_mut().bus; 91 | for _ in 0..16 { 92 | let val = bus.read::(src); 93 | bus.write::(dst, val); 94 | src += 4; 95 | dst += 4; 96 | } 97 | self.raise_irq(); 98 | 99 | if bus.read::(0x1fc0_07c0) & 1 != 0 { 100 | self.set_busy(true); 101 | } 102 | 103 | // Dump PIF RAM 104 | let dst = bus.fetch_read::(new); 105 | let mut mem = dst.iter().unwrap(); 106 | for i in 0..8 { 107 | println!( 108 | "SI: {:03x}: {:02x} {:02x} {:02x} {:02x} -- {:02x} {:02x} {:02x} {:02x}", 109 | (new & 0xFFF) + i * 8, 110 | mem.next().unwrap(), 111 | mem.next().unwrap(), 112 | mem.next().unwrap(), 113 | mem.next().unwrap(), 114 | mem.next().unwrap(), 115 | mem.next().unwrap(), 116 | mem.next().unwrap(), 117 | mem.next().unwrap() 118 | ); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/sp/accumulator.rs: -------------------------------------------------------------------------------- 1 | use std::arch::x86_64::*; 2 | 3 | #[inline] 4 | #[target_feature(enable = "sse4.1")] 5 | pub(crate) unsafe fn acc_add( 6 | acc1_lo: __m128i, 7 | acc1_md: __m128i, 8 | acc1_hi: __m128i, 9 | acc2_lo: __m128i, 10 | acc2_md: __m128i, 11 | acc2_hi: __m128i, 12 | ) -> (__m128i, __m128i, __m128i) { 13 | // Add the three parts to produce partial results (without carry). 14 | let res_lo = _mm_add_epi16(acc1_lo, acc2_lo); 15 | let mut res_md = _mm_add_epi16(acc1_md, acc2_md); 16 | let mut res_hi = _mm_add_epi16(acc1_hi, acc2_hi); 17 | 18 | // Check whether there was a carry generated while adding 19 | // the low and mid part. Carry is materialized as 0xFFFF (-1). 20 | #[allow(overflowing_literals)] 21 | let signbit = _mm_set1_epi16(0x8000); 22 | let carry_lo = _mm_cmpgt_epi16( 23 | _mm_xor_si128(acc2_lo, signbit), 24 | _mm_xor_si128(res_lo, signbit), 25 | ); 26 | let carry_md = _mm_cmpgt_epi16( 27 | _mm_xor_si128(acc2_md, signbit), 28 | _mm_xor_si128(res_md, signbit), 29 | ); 30 | 31 | // Tricky: check whether the carry from the low part (if any) 32 | // generates an overflow in the mid part. This only happens when 33 | // there is a carry (carry_lo=0xFFFF) and the midpart result is 0xFFFF. 34 | let carry_md2 = _mm_and_si128(carry_lo, _mm_cmpeq_epi16(res_md, carry_lo)); 35 | 36 | // Add the three carries into the result. Since they are materialized 37 | // as -1, we use a subtraction to add them. 38 | res_md = _mm_sub_epi16(res_md, carry_lo); 39 | res_hi = _mm_sub_epi16(res_hi, carry_md); 40 | res_hi = _mm_sub_epi16(res_hi, carry_md2); 41 | 42 | (res_lo, res_md, res_hi) 43 | } 44 | 45 | #[inline] 46 | #[target_feature(enable = "sse4.1")] 47 | pub(crate) unsafe fn acc_clamp_signed(acc_md: __m128i, acc_hi: __m128i) -> __m128i { 48 | _mm_packs_epi32( 49 | _mm_unpacklo_epi16(acc_md, acc_hi), 50 | _mm_unpackhi_epi16(acc_md, acc_hi), 51 | ) 52 | } 53 | 54 | #[inline] 55 | #[target_feature(enable = "sse4.1")] 56 | pub(crate) unsafe fn acc_clamp_unsigned3( 57 | mut x: __m128i, 58 | acc_md: __m128i, 59 | acc_hi: __m128i, 60 | ) -> __m128i { 61 | // Unsigned saturation of X given the current 32-bit MD/HI accumulator value. 62 | // * Negative accum values: X=0 63 | // * Positive accum values < 0x7FFF: X kept as-is 64 | // * Positive accum values >= 0x8000: X=0xFFFF 65 | #[allow(overflowing_literals)] 66 | let min = _mm_set1_epi32(0xFFFF_8000); 67 | let max = _mm_set1_epi32(0x0000_7FFF); 68 | 69 | let acc1 = _mm_unpacklo_epi16(acc_md, acc_hi); 70 | let acc2 = _mm_unpackhi_epi16(acc_md, acc_hi); 71 | let mask_min = _mm_packs_epi32(_mm_cmpgt_epi32(min, acc1), _mm_cmpgt_epi32(min, acc2)); 72 | let mask_max = _mm_packs_epi32(_mm_cmpgt_epi32(acc1, max), _mm_cmpgt_epi32(acc2, max)); 73 | 74 | x = _mm_andnot_si128(mask_min, x); // MAX? X=FFFF 76 | x 77 | } 78 | 79 | #[inline] 80 | #[target_feature(enable = "sse4.1")] 81 | pub(crate) unsafe fn acc_clamp_unsigned2(mut x: __m128i, acc_hi: __m128i) -> __m128i { 82 | // Same as acc_clamp_unsigned2, but with X==ACCUM_MD. 83 | // This allows us to skip a few operations. 84 | let kzero = _mm_setzero_si128(); 85 | x = _mm_andnot_si128(_mm_cmpgt_epi16(kzero, acc_hi), x); // PHI<0? X=0 86 | x = _mm_or_si128(_mm_cmpgt_epi16(acc_hi, kzero), x); // PHI>0? X=FFFF 87 | x = _mm_or_si128(x, _mm_srai_epi16(x, 15)); // X>0x7FFF? X=FFFF 88 | x 89 | } 90 | -------------------------------------------------------------------------------- /src/sp/mod.rs: -------------------------------------------------------------------------------- 1 | mod sp; 2 | pub use self::sp::*; 3 | mod decode; 4 | 5 | /// NOTE: please do not add tests here. To test ops, add them at the integration level 6 | /// (tests/spvector.rs) so that they can more easily cover all the different implementations 7 | /// (including JIT). 8 | mod accumulator; 9 | mod cop0; 10 | mod cop2; 11 | mod vclip; 12 | mod vmul; 13 | mod vrcp; 14 | -------------------------------------------------------------------------------- /src/sp/vclip.rs: -------------------------------------------------------------------------------- 1 | use std::arch::x86_64::*; 2 | 3 | #[inline] 4 | #[target_feature(enable = "sse2")] 5 | unsafe fn vselect(mask: __m128i, a: __m128i, b: __m128i) -> __m128i { 6 | _mm_or_si128(_mm_and_si128(mask, a), _mm_andnot_si128(mask, b)) 7 | } 8 | 9 | #[inline] // FIXME: for some reason, Rust doesn't allow inline(always) here 10 | #[target_feature(enable = "sse2")] 11 | pub(crate) unsafe fn vch( 12 | vs: __m128i, 13 | vt: __m128i, 14 | ) -> (__m128i, __m128i, __m128i, __m128i, __m128i, __m128i) { 15 | #[allow(overflowing_literals)] 16 | let vones = _mm_set1_epi16(0xFFFF); 17 | let vzero = _mm_setzero_si128(); 18 | 19 | let sign = _mm_srai_epi16(_mm_xor_si128(vs, vt), 15); 20 | let notsign = _mm_xor_si128(vones, sign); 21 | 22 | // GE is computed as follows: 23 | // SIGN=-1 => VT < 0 24 | // SIGN=0 => VS-VT >= 0 25 | // 26 | // Optimize as: 27 | // VT - (VS &~ SIGN) + ~SIGN < 0 28 | // (with saturation on last addition to avoid overflow) 29 | let ge = _mm_srai_epi16( 30 | _mm_adds_epi16(notsign, _mm_sub_epi16(vt, _mm_andnot_si128(sign, vs))), 31 | 15, 32 | ); 33 | 34 | // LE is computed as follows: 35 | // SIGN=-1 => VS+VT <= 0 36 | // SIGN=0 => VT < 0 37 | // 38 | // Optimize as: 39 | // (VS & SIGN) + VT + SIGN < 0 40 | // (with saturation on last addition to avoid overflow) 41 | let le = _mm_srai_epi16( 42 | _mm_adds_epi16(sign, _mm_add_epi16(_mm_and_si128(sign, vs), vt)), 43 | 15, 44 | ); 45 | 46 | // VCE is computed as follows: 47 | // SIGN=-1 => VS+VT = -1 48 | // SIGN=0 => 0 49 | // 50 | // Optimize as: 51 | // ((VS + VT) == SIGN) & SIGN 52 | let vce = _mm_and_si128(sign, _mm_cmpeq_epi16(sign, _mm_add_epi16(vs, vt))); 53 | 54 | // NE is computed as follows: 55 | // SIGN=-1 => VS+VT != 0 && VS+VT != -1 56 | // SIGN=0 => VS-VT != 0 57 | // 58 | // Optimize as: 59 | // SUM = VS^SIGN - VT 60 | // !(SUM == 0 || SUM == SIGN) 61 | let add = _mm_sub_epi16(_mm_xor_si128(vs, sign), vt); 62 | let ne = _mm_xor_si128( 63 | vones, 64 | _mm_or_si128(_mm_cmpeq_epi16(add, vzero), _mm_cmpeq_epi16(add, sign)), 65 | ); 66 | 67 | let res = vselect( 68 | sign, 69 | vselect(le, _mm_mullo_epi16(vones, vt), vs), 70 | vselect(ge, vt, vs), 71 | ); 72 | 73 | (res, sign, ne, le, ge, vce) 74 | } 75 | 76 | #[inline] // FIXME: for some reason, Rust doesn't allow inline(always) here 77 | #[target_feature(enable = "sse2")] 78 | pub(crate) unsafe fn vcr( 79 | vs: __m128i, 80 | vt: __m128i, 81 | ) -> (__m128i, __m128i, __m128i, __m128i, __m128i, __m128i) { 82 | #[allow(overflowing_literals)] 83 | let vones = _mm_set1_epi16(0xFFFF); 84 | let vzero = _mm_setzero_si128(); 85 | 86 | let sign = _mm_srai_epi16(_mm_xor_si128(vs, vt), 15); 87 | let notsign = _mm_xor_si128(vones, sign); 88 | 89 | // GE is computed as follows: 90 | // SIGN=-1 => VT < 0 91 | // SIGN=0 => VS-VT >= 0 92 | // 93 | // Optimize as: 94 | // VT - (VS &~ SIGN) + ~SIGN < 0 95 | // (with saturation on last addition to avoid overflow) 96 | let ge = _mm_srai_epi16( 97 | _mm_adds_epi16(notsign, _mm_sub_epi16(vt, _mm_andnot_si128(sign, vs))), 98 | 15, 99 | ); 100 | 101 | // LE is computed as follows: 102 | // SIGN=-1 => VS+VT+1 <= 0 103 | // SIGN=0 => VT < 0 104 | // 105 | // Optimize as: 106 | // (VS & SIGN) + VT < 0 107 | // (with saturation on last addition to avoid overflow) 108 | 109 | // FIXME: missing test! MUST REMOVE ADDS(SIGN) 110 | let le = _mm_srai_epi16(_mm_add_epi16(_mm_and_si128(sign, vs), vt), 15); 111 | 112 | let res = vselect( 113 | sign, 114 | vselect(le, _mm_mullo_epi16(vones, vt), vs), 115 | vselect(ge, vt, vs), 116 | ); 117 | 118 | (res, vzero, vzero, le, ge, vzero) 119 | } 120 | 121 | #[inline] // FIXME: for some reason, Rust doesn't allow inline(always) here 122 | #[target_feature(enable = "sse2")] 123 | pub(crate) unsafe fn vcl( 124 | vs: __m128i, 125 | vt: __m128i, 126 | old_sign: __m128i, 127 | old_ne: __m128i, 128 | old_le: __m128i, 129 | old_ge: __m128i, 130 | old_vce: __m128i, 131 | ) -> (__m128i, __m128i, __m128i, __m128i, __m128i, __m128i) { 132 | let vzero = _mm_setzero_si128(); 133 | 134 | // VTSIGN = SIGN ? -VT : VT 135 | let vtsign = _mm_sub_epi16(_mm_xor_si128(old_sign, vt), old_sign); 136 | 137 | // DI = SIGN ? VS-VT : VS+VT = VS - VTSIGN 138 | let di = _mm_sub_epi16(vs, vtsign); 139 | 140 | // IF SIGN 141 | let ncarry = _mm_cmpeq_epi16(di, _mm_adds_epu16(vt, vs)); 142 | let di_zero = _mm_cmpeq_epi16(di, vzero); 143 | 144 | let le = vselect( 145 | old_vce, 146 | _mm_or_si128(di_zero, ncarry), 147 | _mm_and_si128(di_zero, ncarry), 148 | ); 149 | let le = vselect(old_ne, old_le, le); 150 | 151 | // IF NOT SIGN 152 | let ge = _mm_cmpeq_epi16(_mm_subs_epu16(vt, vs), vzero); 153 | let ge = vselect(old_ne, old_ge, ge); 154 | 155 | // Select mask: MASK = SIGN ? LE : GE 156 | // Result: RES = MASK ? VT_SIGN : VS 157 | let res = vselect(vselect(old_sign, le, ge), vtsign, vs); 158 | 159 | ( 160 | res, 161 | vzero, 162 | vzero, 163 | vselect(old_sign, le, old_le), 164 | vselect(old_sign, old_ge, ge), 165 | vzero, 166 | ) 167 | } 168 | -------------------------------------------------------------------------------- /tests/gengolden/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /tests/gengolden/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "byteorder" 3 | version = "1.2.7" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "gengolden" 8 | version = "0.1.0" 9 | dependencies = [ 10 | "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 11 | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", 12 | "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", 13 | "toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 14 | ] 15 | 16 | [[package]] 17 | name = "proc-macro2" 18 | version = "0.4.20" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | dependencies = [ 21 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 22 | ] 23 | 24 | [[package]] 25 | name = "quote" 26 | version = "0.6.9" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | dependencies = [ 29 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 30 | ] 31 | 32 | [[package]] 33 | name = "serde" 34 | version = "1.0.80" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | 37 | [[package]] 38 | name = "serde_derive" 39 | version = "1.0.80" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | dependencies = [ 42 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 43 | "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", 44 | "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)", 45 | ] 46 | 47 | [[package]] 48 | name = "syn" 49 | version = "0.15.18" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | dependencies = [ 52 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 53 | "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 55 | ] 56 | 57 | [[package]] 58 | name = "toml" 59 | version = "0.4.8" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | dependencies = [ 62 | "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", 63 | ] 64 | 65 | [[package]] 66 | name = "unicode-xid" 67 | version = "0.1.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | 70 | [metadata] 71 | "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" 72 | "checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" 73 | "checksum quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "63b5829244f52738cfee93b3a165c1911388675be000c888d2fae620dee8fa5b" 74 | "checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef" 75 | "checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c" 76 | "checksum syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)" = "90c39a061e2f412a9f869540471ab679e85e50c6b05604daf28bc3060f75c430" 77 | "checksum toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2ecc31b0351ea18b3fe11274b8db6e4d82bce861bbb22e6dbed40417902c65" 78 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 79 | -------------------------------------------------------------------------------- /tests/gengolden/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gengolden" 3 | version = "0.1.0" 4 | authors = ["Giovanni Bajo "] 5 | 6 | [dependencies] 7 | byteorder = "1" 8 | serde = "1.0.80" 9 | serde_derive = "1.0.80" 10 | toml = "0.4.8" 11 | -------------------------------------------------------------------------------- /tests/gengolden/LIB/DRIVE64.INC: -------------------------------------------------------------------------------- 1 | /Users/rasky/Sources/r64emu/roms/tests/LIB/DRIVE64.INC -------------------------------------------------------------------------------- /tests/gengolden/LIB/N64_BOOTCODE.BIN: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/LIB/N64_BOOTCODE.BIN -------------------------------------------------------------------------------- /tests/gengolden/LIB/N64_GFX.INC: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/LIB/N64_GFX.INC -------------------------------------------------------------------------------- /tests/gengolden/LIB/N64_HEADER.ASM: -------------------------------------------------------------------------------- 1 | //============ 2 | // N64 Header 3 | //============ 4 | // PI_BSB_DOM1 5 | db $80 // Initial PI_BSB_DOM1_LAT_REG Value 6 | db $37 // Initial PI_BSB_DOM1_PGS_REG Value 7 | db $12 // Initial PI_BSB_DOM1_PWD_REG Value 8 | db $40 // Initial PI_BSB_DOM1_PGS_REG Value 9 | 10 | // CLOCK RATE 11 | dw $000F // Initial Clock Rate 12 | 13 | // VECTOR 14 | dw Start // Boot Address Offset 15 | dw $1444 // Release Offset 16 | 17 | // COMPLEMENT CHECK & CHECKSUM 18 | db "CRC1" // CRC1: COMPLEMENT CHECK 19 | db "CRC2" // CRC2: CHECKSUM 20 | 21 | dd 0 // UNUSED 22 | 23 | // PROGRAM TITLE (27 Byte ASCII String, Use Spaces For Unused Bytes) 24 | db "N64 PROGRAM TITLE " 25 | // "123456789012345678901234567" 26 | 27 | // DEVELOPER ID CODE 28 | db $00 // "N" = Nintendo 29 | 30 | // CARTRIDGE ID CODE 31 | db $00 32 | 33 | db 0 // UNUSED 34 | 35 | // COUNTRY CODE 36 | db $00 // "D" = Germany, "E" = USA, "J" = Japan, "P" = Europe, "U" = Australia 37 | 38 | db 0 // UNUSED -------------------------------------------------------------------------------- /tests/gengolden/compelt.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/compelt.golden -------------------------------------------------------------------------------- /tests/gengolden/compelt.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/compelt.rsp -------------------------------------------------------------------------------- /tests/gengolden/compelt.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:data", 3 | ] 4 | 5 | output_desc = [ 6 | "v128:elt0", 7 | "v128:elt1", 8 | "v128:elt2", 9 | "v128:elt3", 10 | "v128:elt4", 11 | "v128:elt5", 12 | "v128:elt6", 13 | "v128:elt7", 14 | "v128:elt8", 15 | "v128:elt9", 16 | "v128:elt10", 17 | "v128:elt11", 18 | "v128:elt12", 19 | "v128:elt13", 20 | "v128:elt14", 21 | "v128:elt15", 22 | ] 23 | 24 | rsp_code = """ 25 | li a0,$0 26 | li a1,$800 27 | lqv v31[e0],$00(a0) // input vector 28 | 29 | vxor v0,v0 30 | vxor v1,v1 31 | vxor v2,v2 32 | vxor v3,v3 33 | vxor v4,v4 34 | vxor v5,v5 35 | vxor v6,v6 36 | vxor v7,v7 37 | vxor v8,v8 38 | vxor v9,v9 39 | vxor v10,v10 40 | vxor v11,v11 41 | vxor v12,v12 42 | vxor v13,v13 43 | vxor v14,v14 44 | vxor v15,v15 45 | 46 | vor v0,v0,v31[e0] 47 | vor v1,v1,v31[e1] 48 | vor v2,v2,v31[e2] 49 | vor v3,v3,v31[e3] 50 | vor v4,v4,v31[e4] 51 | vor v5,v5,v31[e5] 52 | vor v6,v6,v31[e6] 53 | vor v7,v7,v31[e7] 54 | vor v8,v8,v31[e8] 55 | vor v9,v9,v31[e9] 56 | vor v10,v10,v31[e10] 57 | vor v11,v11,v31[e11] 58 | vor v12,v12,v31[e12] 59 | vor v13,v13,v31[e13] 60 | vor v14,v14,v31[e14] 61 | vor v15,v15,v31[e15] 62 | 63 | sqv v0[e0],$00(a1) 64 | sqv v1[e0],$10(a1) 65 | sqv v2[e0],$20(a1) 66 | sqv v3[e0],$30(a1) 67 | sqv v4[e0],$40(a1) 68 | sqv v5[e0],$50(a1) 69 | sqv v6[e0],$60(a1) 70 | sqv v7[e0],$70(a1) 71 | sqv v8[e0],$80(a1) 72 | sqv v9[e0],$90(a1) 73 | sqv v10[e0],$A0(a1) 74 | sqv v11[e0],$B0(a1) 75 | sqv v12[e0],$C0(a1) 76 | sqv v13[e0],$D0(a1) 77 | sqv v14[e0],$E0(a1) 78 | sqv v15[e0],$F0(a1) 79 | 80 | break 81 | """ 82 | 83 | [[test]] 84 | name = "basic" 85 | input = [ 86 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 87 | ] 88 | -------------------------------------------------------------------------------- /tests/gengolden/golden_test.asm: -------------------------------------------------------------------------------- 1 | arch n64.cpu 2 | endian msb 3 | output "golden_test.n64", create 4 | fill 1052672 // Set ROM Size 5 | 6 | origin $00000000 7 | base $80000000 // Entry Point Of Code 8 | include "LIB/N64.INC" // Include N64 Definitions 9 | include "LIB/N64_HEADER.ASM" // Include 64 Byte Header & Vector Table 10 | insert "LIB/N64_BOOTCODE.BIN" // Include 4032 Byte Boot Code 11 | 12 | Start: 13 | include "LIB/N64_GFX.INC" // Include Graphics Macros 14 | include "LIB/N64_RSP.INC" // Include RSP Macros 15 | N64_INIT() // Run N64 Initialisation Routine 16 | ScreenNTSC(640, 480, BPP32|INTERLACE|AA_MODE_2, $A0100000) // Screen NTSC: 640x480, 32BPP, Interlace, Resample Only, DRAM Origin = $A0100000 17 | 18 | //la a0,Results 19 | //addi a2,a0,64 20 | //j End 21 | //nop 22 | 23 | DMASPRD(RSPCode, RSPCodeEnd, SP_IMEM) // DMA Data Read DRAM->RSP MEM: Start Address, End Address, Destination RSP MEM Address 24 | DMASPWait() // Wait For RSP DMA To Finish 25 | 26 | lui a0,SP_BASE // A0 = SP Base Register ($A4040000) 27 | la a1,TestVectors 28 | la a2,Results 29 | 30 | lw s0,$00(a1) // Number of test vectors 31 | lw s1,$04(a1) // Test vector size 32 | lw s2,$08(a1) // Test result size 33 | addi a1,a1,$10 // Skip header 34 | 35 | Loop: 36 | lui t0,SP_MEM_BASE // T0 = SP Memory Base Register ($A4000000) 37 | sw t0,SP_MEM_ADDR(a0) // Store Memory Offset To SP Memory Address Register ($A4040000) 38 | sw a1,SP_DRAM_ADDR(a0) // Store RAM Offset To SP DRAM Address Register ($A4040004) 39 | subi t0,s1,1 // T0 = Length Of DMA Transfer In Bytes - 1 40 | sw t0,SP_RD_LEN(a0) // Store DMA Length To SP Read Length Register ($A4040008) 41 | DMASPWait() // Wait For RSP DMA To Finish 42 | 43 | // Start RSP and wait until finished 44 | SetSPPC($0000) 45 | StartSP() 46 | WaitSPHalted: 47 | lw t0,SP_STATUS(a0) 48 | andi t0,t0,1 49 | beqz t0,WaitSPHalted 50 | nop 51 | 52 | // Copy results 53 | lui t0,SP_MEM_BASE 54 | ori t0,t0,$800 55 | sw t0,SP_MEM_ADDR(a0) // Store Memory Offset To SP Memory Address Register ($A4040000) 56 | sw a2,SP_DRAM_ADDR(a0) // Store RAM Offset To SP DRAM Address Register ($A4040004) 57 | subi t0,s2,1 // T0 = Length Of DMA Transfer In Bytes - 1 58 | sw t0,SP_WR_LEN(a0) // Store DMA Length To SP Write Length Register ($A404000C) 59 | DMASPWait() // Wait For RSP DMA To Finish 60 | 61 | subi s0,1 62 | add a1,s1 63 | bnez s0,Loop 64 | add a2,s2 65 | 66 | End: 67 | li t5,0xA0000000 // uncached segment 68 | or a2,t5 69 | li t4,0xABABABAB 70 | sw t4,0(a2) 71 | addi a2,4 72 | 73 | // Copy into 64drive area for dumping 74 | include "LIB/DRIVE64.INC" 75 | Drive64SendCommand(DRIVE64_CMD_ENABLE_CARTROM_WRITES) 76 | 77 | // Use PI DMA to copy the results into cartrom area 78 | lui a0,PI_BASE 79 | la t0,Results // Results area in RAM 80 | lui t1,$1100 // CARTROM upper area 81 | 82 | li t4,0x1FFFFFFF 83 | and t0,t4 84 | and t1,t4 85 | sw t0,PI_DRAM_ADDR(a0) 86 | sw t1,PI_CART_ADDR(a0) 87 | 88 | // calculate size in words, minus 1 89 | and a2,t4 90 | sub a2,a2,t0 91 | //subi a2,1 92 | sw a2,PI_RD_LEN(a0) 93 | 94 | Halt: 95 | j Halt 96 | nop 97 | 98 | align(1024) // Align 64-Bit 99 | RSPCode: 100 | insert RSPCode2, "rsp.bin" 101 | align(8) // Align 64-Bit 102 | RSPCodeEnd: 103 | 104 | align(16) 105 | insert TestVectors, "input.bin" 106 | TestVectorsEnd: 107 | Results: 108 | -------------------------------------------------------------------------------- /tests/gengolden/lbv_sbv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lbv_sbv.golden -------------------------------------------------------------------------------- /tests/gengolden/lbv_sbv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lbv_sbv.rsp -------------------------------------------------------------------------------- /tests/gengolden/ldv_sdv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/ldv_sdv.golden -------------------------------------------------------------------------------- /tests/gengolden/ldv_sdv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/ldv_sdv.rsp -------------------------------------------------------------------------------- /tests/gengolden/lfv_sfv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lfv_sfv.golden -------------------------------------------------------------------------------- /tests/gengolden/lfv_sfv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lfv_sfv.rsp -------------------------------------------------------------------------------- /tests/gengolden/lhv_shv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lhv_shv.golden -------------------------------------------------------------------------------- /tests/gengolden/lhv_shv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lhv_shv.rsp -------------------------------------------------------------------------------- /tests/gengolden/llv_slv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/llv_slv.golden -------------------------------------------------------------------------------- /tests/gengolden/llv_slv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/llv_slv.rsp -------------------------------------------------------------------------------- /tests/gengolden/lpv_spv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lpv_spv.golden -------------------------------------------------------------------------------- /tests/gengolden/lpv_spv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lpv_spv.rsp -------------------------------------------------------------------------------- /tests/gengolden/lqv_sqv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lqv_sqv.golden -------------------------------------------------------------------------------- /tests/gengolden/lqv_sqv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lqv_sqv.rsp -------------------------------------------------------------------------------- /tests/gengolden/lqv_sqv.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:data", 3 | "u32:offset", 4 | "u32:dummy", 5 | ] 6 | 7 | output_desc = [ 8 | "v128:lqv_e0", 9 | "v128:lqv_e1", 10 | "v128:lqv_e2", 11 | "v128:lqv_e3", 12 | "v128:lqv_e4", 13 | "v128:lqv_e5", 14 | "v128:lqv_e6", 15 | "v128:lqv_e7", 16 | "v128:lqv_e8", 17 | "v128:lqv_e9", 18 | "v128:lqv_e10", 19 | "v128:lqv_e11", 20 | "v128:lqv_e12", 21 | "v128:lqv_e13", 22 | "v128:lqv_e14", 23 | "v128:lqv_e15", 24 | "v128:sqv_e0", 25 | "v128:sqv_e1", 26 | "v128:sqv_e2", 27 | "v128:sqv_e3", 28 | "v128:sqv_e4", 29 | "v128:sqv_e5", 30 | "v128:sqv_e6", 31 | "v128:sqv_e7", 32 | "v128:sqv_e8", 33 | "v128:sqv_e9", 34 | "v128:sqv_e10", 35 | "v128:sqv_e11", 36 | "v128:sqv_e12", 37 | "v128:sqv_e13", 38 | "v128:sqv_e14", 39 | "v128:sqv_e15", 40 | ] 41 | 42 | rsp_code = """ 43 | li a0,$0 44 | li a1,$800 45 | lw t4,$10(a0) // input: offset 46 | 47 | // This test is very sensitive to possible read/write mistakes to memory 48 | // and we want to make sure that writes actually happen. 49 | // So clear all registers and also output memory area to make sure 50 | // tests do not shadow previous results. 51 | vxor v0,v0 52 | vxor v1,v1 53 | vxor v2,v2 54 | vxor v3,v3 55 | vxor v4,v4 56 | vxor v5,v5 57 | vxor v6,v6 58 | vxor v7,v7 59 | vxor v8,v8 60 | vxor v9,v9 61 | vxor v10,v10 62 | vxor v11,v11 63 | vxor v12,v12 64 | vxor v13,v13 65 | vxor v14,v14 66 | vxor v15,v15 67 | 68 | add a2,a1,0 69 | addi a2,$1F0 70 | ClearLoop: 71 | sqv v0[e0],$00(a2) 72 | bne a1,a2,ClearLoop 73 | subi a2,$10 74 | 75 | add a0,t4 // add offset to disalign 76 | lqv v0[e0],$00(a0) 77 | lqv v1[e1],$00(a0) 78 | lqv v2[e2],$00(a0) 79 | lqv v3[e3],$00(a0) 80 | lqv v4[e4],$00(a0) 81 | lqv v5[e5],$00(a0) 82 | lqv v6[e6],$00(a0) 83 | lqv v7[e7],$00(a0) 84 | lqv v8[e8],$00(a0) 85 | lqv v9[e9],$00(a0) 86 | lqv v10[e10],$00(a0) 87 | lqv v11[e11],$00(a0) 88 | lqv v12[e12],$00(a0) 89 | lqv v13[e13],$00(a0) 90 | lqv v14[e14],$00(a0) 91 | lqv v15[e15],$00(a0) 92 | 93 | sqv v0[e0],$00(a1) 94 | sqv v1[e0],$10(a1) 95 | sqv v2[e0],$20(a1) 96 | sqv v3[e0],$30(a1) 97 | sqv v4[e0],$40(a1) 98 | sqv v5[e0],$50(a1) 99 | sqv v6[e0],$60(a1) 100 | sqv v7[e0],$70(a1) 101 | sqv v8[e0],$80(a1) 102 | sqv v9[e0],$90(a1) 103 | sqv v10[e0],$A0(a1) 104 | sqv v11[e0],$B0(a1) 105 | sqv v12[e0],$C0(a1) 106 | sqv v13[e0],$D0(a1) 107 | sqv v14[e0],$E0(a1) 108 | sqv v15[e0],$F0(a1) 109 | 110 | li a0,$0 111 | lqv v0[e0],$00(a0) // input: v0 112 | 113 | add a1,t4 // add offset to disalign 114 | sqv v0[e15],$1F0(a1) 115 | sqv v0[e14],$1E0(a1) 116 | sqv v0[e13],$1D0(a1) 117 | sqv v0[e12],$1C0(a1) 118 | sqv v0[e11],$1B0(a1) 119 | sqv v0[e10],$1A0(a1) 120 | sqv v0[e9],$190(a1) 121 | sqv v0[e8],$180(a1) 122 | sqv v0[e7],$170(a1) 123 | sqv v0[e6],$160(a1) 124 | sqv v0[e5],$150(a1) 125 | sqv v0[e4],$140(a1) 126 | sqv v0[e3],$130(a1) 127 | sqv v0[e2],$120(a1) 128 | sqv v0[e1],$110(a1) 129 | sqv v0[e0],$100(a1) 130 | 131 | break 132 | """ 133 | 134 | [[test]] 135 | name = "offset0" 136 | input = [ 137 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 138 | 0, # offset 139 | 0, # dummy 140 | ] 141 | [[test]] 142 | name = "offset1" 143 | input = [ 144 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 145 | 1, # offset 146 | 0, # dummy 147 | ] 148 | [[test]] 149 | name = "offset2" 150 | input = [ 151 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 152 | 2, # offset 153 | 0, # dummy 154 | ] 155 | [[test]] 156 | name = "offset3" 157 | input = [ 158 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 159 | 3, # offset 160 | 0, # dummy 161 | ] 162 | [[test]] 163 | name = "offset4" 164 | input = [ 165 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 166 | 4, # offset 167 | 0, # dummy 168 | ] 169 | [[test]] 170 | name = "offset5" 171 | input = [ 172 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 173 | 5, # offset 174 | 0, # dummy 175 | ] 176 | [[test]] 177 | name = "offset6" 178 | input = [ 179 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 180 | 6, # offset 181 | 0, # dummy 182 | ] 183 | [[test]] 184 | name = "offset7" 185 | input = [ 186 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 187 | 7, # offset 188 | 0, # dummy 189 | ] 190 | [[test]] 191 | name = "offset8" 192 | input = [ 193 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 194 | 8, # offset 195 | 0, # dummy 196 | ] 197 | [[test]] 198 | name = "offset9" 199 | input = [ 200 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 201 | 9, # offset 202 | 0, # dummy 203 | ] 204 | [[test]] 205 | name = "offset10" 206 | input = [ 207 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 208 | 10, # offset 209 | 0, # dummy 210 | ] 211 | [[test]] 212 | name = "offset11" 213 | input = [ 214 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 215 | 11, # offset 216 | 0, # dummy 217 | ] 218 | [[test]] 219 | name = "offset12" 220 | input = [ 221 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 222 | 12, # offset 223 | 0, # dummy 224 | ] 225 | [[test]] 226 | name = "offset13" 227 | input = [ 228 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 229 | 13, # offset 230 | 0, # dummy 231 | ] 232 | [[test]] 233 | name = "offset14" 234 | input = [ 235 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 236 | 14, # offset 237 | 0, # dummy 238 | ] 239 | [[test]] 240 | name = "offset15" 241 | input = [ 242 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 243 | 15, # offset 244 | 0, # dummy 245 | ] 246 | -------------------------------------------------------------------------------- /tests/gengolden/lrv_srv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lrv_srv.golden -------------------------------------------------------------------------------- /tests/gengolden/lrv_srv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lrv_srv.rsp -------------------------------------------------------------------------------- /tests/gengolden/lrv_srv.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:data", 3 | "u32:offset", 4 | "u32:dummy", 5 | ] 6 | 7 | output_desc = [ 8 | "v128:lrv_e0", 9 | "v128:lrv_e1", 10 | "v128:lrv_e2", 11 | "v128:lrv_e3", 12 | "v128:lrv_e4", 13 | "v128:lrv_e5", 14 | "v128:lrv_e6", 15 | "v128:lrv_e7", 16 | "v128:lrv_e8", 17 | "v128:lrv_e9", 18 | "v128:lrv_e10", 19 | "v128:lrv_e11", 20 | "v128:lrv_e12", 21 | "v128:lrv_e13", 22 | "v128:lrv_e14", 23 | "v128:lrv_e15", 24 | "v128:srv_e0", 25 | "v128:srv_e1", 26 | "v128:srv_e2", 27 | "v128:srv_e3", 28 | "v128:srv_e4", 29 | "v128:srv_e5", 30 | "v128:srv_e6", 31 | "v128:srv_e7", 32 | "v128:srv_e8", 33 | "v128:srv_e9", 34 | "v128:srv_e10", 35 | "v128:srv_e11", 36 | "v128:srv_e12", 37 | "v128:srv_e13", 38 | "v128:srv_e14", 39 | "v128:srv_e15", 40 | ] 41 | 42 | rsp_code = """ 43 | li a0,$0 44 | li a1,$800 45 | lw t4,$10(a0) // input: offset 46 | 47 | // This test is very sensitive to possible read/write mistakes to memory 48 | // and we want to make sure that writes actually happen. 49 | // So clear all registers and also output memory area to make sure 50 | // tests do not shadow previous results. 51 | vxor v0,v0 52 | vxor v1,v1 53 | vxor v2,v2 54 | vxor v3,v3 55 | vxor v4,v4 56 | vxor v5,v5 57 | vxor v6,v6 58 | vxor v7,v7 59 | vxor v8,v8 60 | vxor v9,v9 61 | vxor v10,v10 62 | vxor v11,v11 63 | vxor v12,v12 64 | vxor v13,v13 65 | vxor v14,v14 66 | vxor v15,v15 67 | 68 | add a2,a1,0 69 | addi a2,$1F0 70 | ClearLoop: 71 | sqv v0[e0],$00(a2) 72 | bne a1,a2,ClearLoop 73 | subi a2,$10 74 | 75 | add a0,t4 // add offset to disalign 76 | lrv v0[e0],$00(a0) 77 | lrv v1[e1],$00(a0) 78 | lrv v2[e2],$00(a0) 79 | lrv v3[e3],$00(a0) 80 | lrv v4[e4],$00(a0) 81 | lrv v5[e5],$00(a0) 82 | lrv v6[e6],$00(a0) 83 | lrv v7[e7],$00(a0) 84 | lrv v8[e8],$00(a0) 85 | lrv v9[e9],$00(a0) 86 | lrv v10[e10],$00(a0) 87 | lrv v11[e11],$00(a0) 88 | lrv v12[e12],$00(a0) 89 | lrv v13[e13],$00(a0) 90 | lrv v14[e14],$00(a0) 91 | lrv v15[e15],$00(a0) 92 | 93 | sqv v0[e0],$00(a1) 94 | sqv v1[e0],$10(a1) 95 | sqv v2[e0],$20(a1) 96 | sqv v3[e0],$30(a1) 97 | sqv v4[e0],$40(a1) 98 | sqv v5[e0],$50(a1) 99 | sqv v6[e0],$60(a1) 100 | sqv v7[e0],$70(a1) 101 | sqv v8[e0],$80(a1) 102 | sqv v9[e0],$90(a1) 103 | sqv v10[e0],$A0(a1) 104 | sqv v11[e0],$B0(a1) 105 | sqv v12[e0],$C0(a1) 106 | sqv v13[e0],$D0(a1) 107 | sqv v14[e0],$E0(a1) 108 | sqv v15[e0],$F0(a1) 109 | 110 | li a0,$0 111 | lqv v0[e0],$00(a0) // input: v0 112 | 113 | add a1,t4 // add offset to disalign 114 | srv v0[e15],$1F0(a1) 115 | srv v0[e14],$1E0(a1) 116 | srv v0[e13],$1D0(a1) 117 | srv v0[e12],$1C0(a1) 118 | srv v0[e11],$1B0(a1) 119 | srv v0[e10],$1A0(a1) 120 | srv v0[e9],$190(a1) 121 | srv v0[e8],$180(a1) 122 | srv v0[e7],$170(a1) 123 | srv v0[e6],$160(a1) 124 | srv v0[e5],$150(a1) 125 | srv v0[e4],$140(a1) 126 | srv v0[e3],$130(a1) 127 | srv v0[e2],$120(a1) 128 | srv v0[e1],$110(a1) 129 | srv v0[e0],$100(a1) 130 | 131 | break 132 | """ 133 | 134 | [[test]] 135 | name = "offset0" 136 | input = [ 137 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 138 | 0, # offset 139 | 0, # dummy 140 | ] 141 | [[test]] 142 | name = "offset1" 143 | input = [ 144 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 145 | 1, # offset 146 | 0, # dummy 147 | ] 148 | [[test]] 149 | name = "offset2" 150 | input = [ 151 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 152 | 2, # offset 153 | 0, # dummy 154 | ] 155 | [[test]] 156 | name = "offset3" 157 | input = [ 158 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 159 | 3, # offset 160 | 0, # dummy 161 | ] 162 | [[test]] 163 | name = "offset4" 164 | input = [ 165 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 166 | 4, # offset 167 | 0, # dummy 168 | ] 169 | [[test]] 170 | name = "offset5" 171 | input = [ 172 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 173 | 5, # offset 174 | 0, # dummy 175 | ] 176 | [[test]] 177 | name = "offset6" 178 | input = [ 179 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 180 | 6, # offset 181 | 0, # dummy 182 | ] 183 | [[test]] 184 | name = "offset7" 185 | input = [ 186 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 187 | 7, # offset 188 | 0, # dummy 189 | ] 190 | [[test]] 191 | name = "offset8" 192 | input = [ 193 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 194 | 8, # offset 195 | 0, # dummy 196 | ] 197 | [[test]] 198 | name = "offset9" 199 | input = [ 200 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 201 | 9, # offset 202 | 0, # dummy 203 | ] 204 | [[test]] 205 | name = "offset10" 206 | input = [ 207 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 208 | 10, # offset 209 | 0, # dummy 210 | ] 211 | [[test]] 212 | name = "offset11" 213 | input = [ 214 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 215 | 11, # offset 216 | 0, # dummy 217 | ] 218 | [[test]] 219 | name = "offset12" 220 | input = [ 221 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 222 | 12, # offset 223 | 0, # dummy 224 | ] 225 | [[test]] 226 | name = "offset13" 227 | input = [ 228 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 229 | 13, # offset 230 | 0, # dummy 231 | ] 232 | [[test]] 233 | name = "offset14" 234 | input = [ 235 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 236 | 14, # offset 237 | 0, # dummy 238 | ] 239 | [[test]] 240 | name = "offset15" 241 | input = [ 242 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAB, # v0 243 | 15, # offset 244 | 0, # dummy 245 | ] 246 | -------------------------------------------------------------------------------- /tests/gengolden/lsv_ssv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lsv_ssv.golden -------------------------------------------------------------------------------- /tests/gengolden/lsv_ssv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/lsv_ssv.rsp -------------------------------------------------------------------------------- /tests/gengolden/ltv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/ltv.golden -------------------------------------------------------------------------------- /tests/gengolden/ltv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/ltv.rsp -------------------------------------------------------------------------------- /tests/gengolden/ltv.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:dummy", 3 | "v128:v0", 4 | "v128:v1", 5 | "u32:offset", 6 | "u32:dummy", 7 | ] 8 | 9 | output_desc = [ 10 | "v128:v0e0", 11 | "v128:v1e0", 12 | "v128:v2e0", 13 | "v128:v3e0", 14 | "v128:v4e0", 15 | "v128:v5e0", 16 | "v128:v6e0", 17 | "v128:v7e0", 18 | 19 | "v128:s7v0e0", 20 | "v128:s7v1e0", 21 | "v128:s7v2e0", 22 | "v128:s7v3e0", 23 | "v128:s7v4e0", 24 | "v128:s7v5e0", 25 | "v128:s7v6e0", 26 | "v128:s7v7e0", 27 | 28 | "v128:v0e1", 29 | "v128:v1e1", 30 | "v128:v2e1", 31 | "v128:v3e1", 32 | "v128:v4e1", 33 | "v128:v5e1", 34 | "v128:v6e1", 35 | "v128:v7e1", 36 | 37 | "v128:v0e2", 38 | "v128:v1e2", 39 | "v128:v2e2", 40 | "v128:v3e2", 41 | "v128:v4e2", 42 | "v128:v5e2", 43 | "v128:v6e2", 44 | "v128:v7e2", 45 | 46 | "v128:v0e12", 47 | "v128:v1e12", 48 | "v128:v2e12", 49 | "v128:v3e12", 50 | "v128:v4e12", 51 | "v128:v5e12", 52 | "v128:v6e12", 53 | "v128:v7e12", 54 | 55 | "v128:v0e15", 56 | "v128:v1e15", 57 | "v128:v2e15", 58 | "v128:v3e15", 59 | "v128:v4e15", 60 | "v128:v5e15", 61 | "v128:v6e15", 62 | "v128:v7e15", 63 | 64 | "v128:s7v0e15", 65 | "v128:s7v1e15", 66 | "v128:s7v2e15", 67 | "v128:s7v3e15", 68 | "v128:s7v4e15", 69 | "v128:s7v5e15", 70 | "v128:s7v6e15", 71 | "v128:s7v7e15", 72 | 73 | "v128:v0e0o7", 74 | "v128:v1e0o7", 75 | "v128:v2e0o7", 76 | "v128:v3e0o7", 77 | "v128:v4e0o7", 78 | "v128:v5e0o7", 79 | "v128:v6e0o7", 80 | "v128:v7e0o7", 81 | 82 | "v128:v0e0o8", 83 | "v128:v1e0o8", 84 | "v128:v2e0o8", 85 | "v128:v3e0o8", 86 | "v128:v4e0o8", 87 | "v128:v5e0o8", 88 | "v128:v6e0o8", 89 | "v128:v7e0o8", 90 | 91 | "v128:v0e0o15", 92 | "v128:v1e0o15", 93 | "v128:v2e0o15", 94 | "v128:v3e0o15", 95 | "v128:v4e0o15", 96 | "v128:v5e0o15", 97 | "v128:v6e0o15", 98 | "v128:v7e0o15", 99 | ] 100 | 101 | rsp_code = """ 102 | li a0,$0 103 | li a1,$800 104 | 105 | lw t4,$90(a0) // input: reg offset 106 | add a0,t4 107 | 108 | jal CleanRegs 109 | nop 110 | ltv v0[e0],$10(a0) 111 | jal StoreOut 112 | nop 113 | 114 | jal CleanRegs 115 | nop 116 | ltv v7[e0],$10(a0) 117 | jal StoreOut 118 | nop 119 | 120 | jal CleanRegs 121 | nop 122 | ltv v0[e1],$10(a0) 123 | jal StoreOut 124 | nop 125 | 126 | jal CleanRegs 127 | nop 128 | ltv v0[e2],$10(a0) 129 | jal StoreOut 130 | nop 131 | 132 | jal CleanRegs 133 | nop 134 | ltv v0[e12],$10(a0) 135 | jal StoreOut 136 | nop 137 | 138 | jal CleanRegs 139 | nop 140 | ltv v0[e15],$10(a0) 141 | jal StoreOut 142 | nop 143 | 144 | jal CleanRegs 145 | nop 146 | ltv v7[e15],$10(a0) 147 | jal StoreOut 148 | nop 149 | 150 | // Just in case, we test with immediate offset. It must behave 151 | // like adding an offset to a0, but you never know. 152 | jal CleanRegs 153 | nop 154 | ltv v0[e0],$07(a0) 155 | jal StoreOut 156 | nop 157 | 158 | jal CleanRegs 159 | nop 160 | ltv v0[e0],$08(a0) 161 | jal StoreOut 162 | nop 163 | 164 | jal CleanRegs 165 | nop 166 | ltv v0[e0],$0F(a0) 167 | jal StoreOut 168 | nop 169 | 170 | break 171 | 172 | CleanRegs: 173 | vxor v0,v0 174 | vxor v1,v1 175 | vxor v2,v2 176 | vxor v3,v3 177 | vxor v4,v4 178 | vxor v5,v5 179 | vxor v6,v6 180 | jr ra 181 | vxor v7,v7 182 | 183 | StoreOut: 184 | sqv v0[e0],$00(a1) 185 | sqv v1[e0],$10(a1) 186 | sqv v2[e0],$20(a1) 187 | sqv v3[e0],$30(a1) 188 | sqv v4[e0],$40(a1) 189 | sqv v5[e0],$50(a1) 190 | sqv v6[e0],$60(a1) 191 | sqv v7[e0],$70(a1) 192 | jr ra 193 | addi a1,$80 194 | """ 195 | 196 | [[test]] 197 | name = "offset0" 198 | input = [ 199 | 0,0,0,0, # dummy 200 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 201 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 202 | 0, # offset 203 | 0, # dummy 204 | ] 205 | [[test]] 206 | name = "offset1" 207 | input = [ 208 | 0,0,0,0, # dummy 209 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 210 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 211 | 1, # offset 212 | 0, # dummy 213 | ] 214 | [[test]] 215 | name = "offset7" 216 | input = [ 217 | 0,0,0,0, # dummy 218 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 219 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 220 | 7, # offset 221 | 0, # dummy 222 | ] 223 | [[test]] 224 | name = "offset8" 225 | input = [ 226 | 0,0,0,0, # dummy 227 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 228 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 229 | 8, # offset 230 | 0, # dummy 231 | ] 232 | [[test]] 233 | name = "offset15" 234 | input = [ 235 | 0,0,0,0, # dummy 236 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 237 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 238 | 15, # offset 239 | 0, # dummy 240 | ] 241 | -------------------------------------------------------------------------------- /tests/gengolden/luv_suv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/luv_suv.golden -------------------------------------------------------------------------------- /tests/gengolden/luv_suv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/luv_suv.rsp -------------------------------------------------------------------------------- /tests/gengolden/memaccess.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/memaccess.golden -------------------------------------------------------------------------------- /tests/gengolden/memaccess.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/memaccess.rsp -------------------------------------------------------------------------------- /tests/gengolden/mfc2.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/mfc2.golden -------------------------------------------------------------------------------- /tests/gengolden/mfc2.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/mfc2.rsp -------------------------------------------------------------------------------- /tests/gengolden/mfc2.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:base", 3 | "u32:data", 4 | "u32:dummy", 5 | ] 6 | 7 | output_desc = [ 8 | "u32:v0", 9 | "u32:v1", 10 | "u32:v2", 11 | "u32:v3", 12 | "u32:v4", 13 | "u32:v5", 14 | "u32:v6", 15 | "u32:v7", 16 | "u32:v8", 17 | "u32:v9", 18 | "u32:v10", 19 | "u32:v11", 20 | "u32:v12", 21 | "u32:v13", 22 | "u32:v14", 23 | "u32:v15", 24 | ] 25 | 26 | rsp_code = """ 27 | li a0,$0 28 | li a1,$800 29 | 30 | lqv v0[e0],$00(a0) 31 | 32 | lw t0,$10(a0) 33 | mfc2 t0,v0[e0] 34 | sw t0,$00(a1) 35 | addi a1,$4 36 | 37 | lw t0,$10(a0) 38 | mfc2 t0,v0[e1] 39 | sw t0,$00(a1) 40 | addi a1,$4 41 | 42 | lw t0,$10(a0) 43 | mfc2 t0,v0[e2] 44 | sw t0,$00(a1) 45 | addi a1,$4 46 | 47 | lw t0,$10(a0) 48 | mfc2 t0,v0[e3] 49 | sw t0,$00(a1) 50 | addi a1,$4 51 | 52 | lw t0,$10(a0) 53 | mfc2 t0,v0[e4] 54 | sw t0,$00(a1) 55 | addi a1,$4 56 | 57 | lw t0,$10(a0) 58 | mfc2 t0,v0[e5] 59 | sw t0,$00(a1) 60 | addi a1,$4 61 | 62 | lw t0,$10(a0) 63 | mfc2 t0,v0[e6] 64 | sw t0,$00(a1) 65 | addi a1,$4 66 | 67 | lw t0,$10(a0) 68 | mfc2 t0,v0[e7] 69 | sw t0,$00(a1) 70 | addi a1,$4 71 | 72 | lw t0,$10(a0) 73 | mfc2 t0,v0[e8] 74 | sw t0,$00(a1) 75 | addi a1,$4 76 | 77 | lw t0,$10(a0) 78 | mfc2 t0,v0[e9] 79 | sw t0,$00(a1) 80 | addi a1,$4 81 | 82 | lw t0,$10(a0) 83 | mfc2 t0,v0[e10] 84 | sw t0,$00(a1) 85 | addi a1,$4 86 | 87 | lw t0,$10(a0) 88 | mfc2 t0,v0[e11] 89 | sw t0,$00(a1) 90 | addi a1,$4 91 | 92 | lw t0,$10(a0) 93 | mfc2 t0,v0[e12] 94 | sw t0,$00(a1) 95 | addi a1,$4 96 | 97 | lw t0,$10(a0) 98 | mfc2 t0,v0[e13] 99 | sw t0,$00(a1) 100 | addi a1,$4 101 | 102 | lw t0,$10(a0) 103 | mfc2 t0,v0[e14] 104 | sw t0,$00(a1) 105 | addi a1,$4 106 | 107 | lw t0,$10(a0) 108 | mfc2 t0,v0[e15] 109 | sw t0,$00(a1) 110 | addi a1,$4 111 | 112 | break 113 | """ 114 | 115 | [[test]] 116 | name = "basic" 117 | input = [ 118 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAA, # base 119 | 0x1234_5678, # data 120 | 0, 121 | ] 122 | -------------------------------------------------------------------------------- /tests/gengolden/mtc2.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/mtc2.golden -------------------------------------------------------------------------------- /tests/gengolden/mtc2.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/mtc2.rsp -------------------------------------------------------------------------------- /tests/gengolden/mtc2.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:base", 3 | "u32:data", 4 | "u32:dummy", 5 | ] 6 | 7 | output_desc = [ 8 | "v128:v0", 9 | "v128:v1", 10 | "v128:v2", 11 | "v128:v3", 12 | "v128:v4", 13 | "v128:v5", 14 | "v128:v6", 15 | "v128:v7", 16 | "v128:v8", 17 | "v128:v9", 18 | "v128:v10", 19 | "v128:v11", 20 | "v128:v12", 21 | "v128:v13", 22 | "v128:v14", 23 | "v128:v15", 24 | ] 25 | 26 | rsp_code = """ 27 | li a0,$0 28 | li a1,$800 29 | 30 | lqv v0[e0],$00(a0) 31 | lqv v1[e0],$00(a0) 32 | lqv v2[e0],$00(a0) 33 | lqv v3[e0],$00(a0) 34 | lqv v4[e0],$00(a0) 35 | lqv v5[e0],$00(a0) 36 | lqv v6[e0],$00(a0) 37 | lqv v7[e0],$00(a0) 38 | lqv v8[e0],$00(a0) 39 | lqv v9[e0],$00(a0) 40 | lqv v10[e0],$00(a0) 41 | lqv v11[e0],$00(a0) 42 | lqv v12[e0],$00(a0) 43 | lqv v13[e0],$00(a0) 44 | lqv v14[e0],$00(a0) 45 | lqv v15[e0],$00(a0) 46 | lw t0,$10(a0) 47 | 48 | mtc2 t0,v0[e0] 49 | mtc2 t0,v1[e1] 50 | mtc2 t0,v2[e2] 51 | mtc2 t0,v3[e3] 52 | mtc2 t0,v4[e4] 53 | mtc2 t0,v5[e5] 54 | mtc2 t0,v6[e6] 55 | mtc2 t0,v7[e7] 56 | mtc2 t0,v8[e8] 57 | mtc2 t0,v9[e9] 58 | mtc2 t0,v10[e10] 59 | mtc2 t0,v11[e11] 60 | mtc2 t0,v12[e12] 61 | mtc2 t0,v13[e13] 62 | mtc2 t0,v14[e14] 63 | mtc2 t0,v15[e15] 64 | 65 | sqv v0[e0],$00(a1) 66 | sqv v1[e0],$10(a1) 67 | sqv v2[e0],$20(a1) 68 | sqv v3[e0],$30(a1) 69 | sqv v4[e0],$40(a1) 70 | sqv v5[e0],$50(a1) 71 | sqv v6[e0],$60(a1) 72 | sqv v7[e0],$70(a1) 73 | 74 | addi a1,$80 75 | sqv v8[e0],$00(a1) 76 | sqv v9[e0],$10(a1) 77 | sqv v10[e0],$20(a1) 78 | sqv v11[e0],$30(a1) 79 | sqv v12[e0],$40(a1) 80 | sqv v13[e0],$50(a1) 81 | sqv v14[e0],$60(a1) 82 | sqv v15[e0],$70(a1) 83 | 84 | break 85 | """ 86 | 87 | [[test]] 88 | name = "basic" 89 | input = [ 90 | 0x1122_3344, 0x5566_7788, 0x99AA_BBCC, 0xDDEE_FFAA, # base 91 | 0x1234_5678, # data 92 | 0, 93 | ] 94 | -------------------------------------------------------------------------------- /tests/gengolden/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Usually run by gengolden 4 | 5 | set -euo pipefail 6 | 7 | if [ $# -ne 3 ]; then 8 | echo "Usage: run.sh " 9 | exit 1 10 | fi 11 | 12 | trap "rm -f golden_test.n64 golden.raw" EXIT 13 | 14 | bass golden_test.asm 15 | chksum64 golden_test.n64 >/dev/null 16 | 64drive -q -c auto -u golden_test.n64 17 | 18 | echo "Reset the N64 and press ENTER to continue..." 19 | read -r 20 | 21 | sleep 2 22 | 64drive -q -o 0x1000000 -s "$3" -d golden.raw 23 | head -c "$2" "$1" 24 | -------------------------------------------------------------------------------- /tests/gengolden/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | extern crate byteorder; 4 | extern crate toml; 5 | 6 | use byteorder::{BigEndian, WriteBytesExt}; 7 | use std::env; 8 | use std::fs; 9 | use std::path::Path; 10 | use std::process::{exit, Command}; 11 | 12 | #[derive(Deserialize)] 13 | struct TestVector { 14 | name: String, 15 | input: Vec, 16 | } 17 | 18 | #[derive(Deserialize)] 19 | struct Testsuite { 20 | rsp_code: String, 21 | input_desc: Vec, 22 | output_desc: Vec, 23 | test: Vec, 24 | } 25 | 26 | fn main() { 27 | let args: Vec = env::args().collect(); 28 | if args.len() != 2 { 29 | println!("usage: gengolden "); 30 | exit(1); 31 | } 32 | let tomlname = Path::new(&args[1]); 33 | 34 | let tomlsrc = fs::read_to_string(tomlname).expect("TOML file not found"); 35 | let t: Testsuite = toml::from_str(&tomlsrc).unwrap(); 36 | 37 | // Calculate input and output size 38 | let mut input_size: u32 = 0; 39 | let mut output_size: u32 = 0; 40 | for d in &t.input_desc { 41 | if d.starts_with("v128:") { 42 | input_size += 16; 43 | } else if d.starts_with("u32:") { 44 | input_size += 4; 45 | } else if d.starts_with("u64:") { 46 | input_size += 8; 47 | } else { 48 | panic!(format!("invalid desc string: {}", *d)); 49 | } 50 | } 51 | for d in &t.output_desc { 52 | if d.starts_with("v128:") { 53 | output_size += 16; 54 | } else if d.starts_with("u32:") { 55 | output_size += 4; 56 | } else if d.starts_with("u64:") { 57 | output_size += 8; 58 | } else { 59 | panic!(format!("invalid desc string: {}", *d)); 60 | } 61 | } 62 | if input_size % 8 != 0 { 63 | panic!( 64 | "input size must be multiple of 8 bytes (found: {})", 65 | input_size 66 | ); 67 | } 68 | if output_size % 8 != 0 { 69 | panic!( 70 | "output size must be multiple of 8 bytes (found: {})", 71 | output_size 72 | ); 73 | } 74 | if !t.rsp_code.contains("break") { 75 | panic!("break missing from RSP code"); 76 | } 77 | 78 | // Generate RSP binary 79 | { 80 | let prefix: String = r#" 81 | arch n64.rsp 82 | endian msb 83 | base $0000 84 | include "LIB/N64.INC" 85 | include "LIB/N64_RSP.INC" 86 | "# 87 | .into(); 88 | 89 | fs::write("rsp.asm", prefix + &t.rsp_code).expect("cannot write RSP.ASM file"); 90 | let status = Command::new("bass") 91 | .args(&["-o", "rsp.bin", "rsp.asm"]) 92 | .status() 93 | .expect("failed to execute bass"); 94 | if !status.success() { 95 | exit(1); 96 | } 97 | fs::remove_file("rsp.asm").unwrap(); 98 | } 99 | 100 | // Generate input vector 101 | { 102 | let mut f = fs::File::create("input.bin").expect("cannot create input.bin"); 103 | 104 | f.write_u32::(t.test.len() as u32).unwrap(); 105 | f.write_u32::(input_size).unwrap(); 106 | f.write_u32::(output_size).unwrap(); 107 | f.write_u32::(0).unwrap(); 108 | 109 | for tv in &t.test { 110 | if tv.input.len() * 4 != input_size as usize { 111 | panic!(format!( 112 | "test {} has invalid number of inputs ({} vs {})", 113 | &tv.name, 114 | tv.input.len() * 4, 115 | input_size 116 | )); 117 | } 118 | 119 | for v in &tv.input { 120 | f.write_u32::(*v).unwrap(); 121 | } 122 | } 123 | } 124 | 125 | // Compile and execute the golden test to create golden results 126 | { 127 | let goldenname = tomlname.with_extension("golden"); 128 | let total_output_size = output_size as usize * t.test.len(); 129 | let memsize = ((total_output_size + 4095) / 4096) * 4096; 130 | let status = Command::new("./run.sh") 131 | .args(&[ 132 | goldenname.to_str().unwrap(), 133 | &total_output_size.to_string(), 134 | &memsize.to_string(), 135 | ]) 136 | .status() 137 | .expect("failed to execute run.sh"); 138 | if !status.success() { 139 | exit(1); 140 | } 141 | } 142 | 143 | // Cleanup 144 | fs::rename("rsp.bin", tomlname.with_extension("rsp")).unwrap(); 145 | fs::remove_file("input.bin").unwrap(); 146 | 147 | println!( 148 | "Generated: {}, {}", 149 | tomlname.with_extension("rsp").display(), 150 | tomlname.with_extension("golden").display() 151 | ); 152 | } 153 | -------------------------------------------------------------------------------- /tests/gengolden/stv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/stv.golden -------------------------------------------------------------------------------- /tests/gengolden/stv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/stv.rsp -------------------------------------------------------------------------------- /tests/gengolden/stv.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "v128:v2", 5 | "v128:v3", 6 | "v128:v4", 7 | "v128:v5", 8 | "v128:v6", 9 | "v128:v7", 10 | "u32:offset", 11 | "u32:dummy", 12 | ] 13 | 14 | output_desc = [ 15 | "v128:mem0e0", 16 | "v128:mem1e0", 17 | "v128:mem2e0", 18 | 19 | "v128:s7mem0e0", 20 | "v128:s7mem1e0", 21 | "v128:s7mem2e0", 22 | 23 | "v128:mem0e1", 24 | "v128:mem1e1", 25 | "v128:mem2e1", 26 | 27 | "v128:mem0e12", 28 | "v128:mem1e12", 29 | "v128:mem2e12", 30 | 31 | "v128:mem0e15", 32 | "v128:mem1e15", 33 | "v128:mem2e15", 34 | 35 | "v128:s7mem0e15", 36 | "v128:s7mem1e15", 37 | "v128:s7mem2e15", 38 | ] 39 | 40 | rsp_code = """ 41 | li a0,$0 42 | li a1,$800 43 | 44 | lqv v0[e0],$00(a0) 45 | lqv v1[e0],$10(a0) 46 | lqv v2[e0],$20(a0) 47 | lqv v3[e0],$30(a0) 48 | lqv v4[e0],$40(a0) 49 | lqv v5[e0],$50(a0) 50 | lqv v6[e0],$60(a0) 51 | lqv v7[e0],$70(a0) 52 | 53 | lw t4,$80(a0) // input: offset 54 | add a1,t4 55 | 56 | stv v0[e0],$10(a1) 57 | addi a1,$30 58 | 59 | stv v7[e0],$10(a1) 60 | addi a1,$30 61 | 62 | stv v0[e1],$10(a1) 63 | addi a1,$30 64 | 65 | stv v0[e12],$10(a1) 66 | addi a1,$30 67 | 68 | stv v0[e15],$10(a1) 69 | addi a1,$30 70 | 71 | stv v7[e15],$10(a1) 72 | addi a1,$30 73 | 74 | break 75 | """ 76 | 77 | [[test]] 78 | name = "offset0" 79 | input = [ 80 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 81 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 82 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 83 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 84 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 85 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 86 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 87 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 88 | 0, # offset 89 | 0, # dummy 90 | ] 91 | [[test]] 92 | name = "offset1" 93 | input = [ 94 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 95 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 96 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 97 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 98 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 99 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 100 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 101 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 102 | 1, # offset 103 | 0, # dummy 104 | ] 105 | [[test]] 106 | name = "offset7" 107 | input = [ 108 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 109 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 110 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 111 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 112 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 113 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 114 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 115 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 116 | 7, # offset 117 | 0, # dummy 118 | ] 119 | [[test]] 120 | name = "offset8" 121 | input = [ 122 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 123 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 124 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 125 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 126 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 127 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 128 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 129 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 130 | 8, # offset 131 | 0, # dummy 132 | ] 133 | [[test]] 134 | name = "offset15" 135 | input = [ 136 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 137 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 138 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 139 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 140 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 141 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 142 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 143 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 144 | 15, # offset 145 | 0, # dummy 146 | ] 147 | -------------------------------------------------------------------------------- /tests/gengolden/swv.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/swv.golden -------------------------------------------------------------------------------- /tests/gengolden/swv.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/swv.rsp -------------------------------------------------------------------------------- /tests/gengolden/swv.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "v128:v2", 5 | "v128:v3", 6 | "v128:v4", 7 | "v128:v5", 8 | "v128:v6", 9 | "v128:v7", 10 | "u32:offset", 11 | "u32:dummy", 12 | ] 13 | 14 | output_desc = [ 15 | "v128:mem0e0", 16 | "v128:mem1e0", 17 | "v128:mem2e0", 18 | 19 | "v128:s7mem0e0", 20 | "v128:s7mem1e0", 21 | "v128:s7mem2e0", 22 | 23 | "v128:mem0e1", 24 | "v128:mem1e1", 25 | "v128:mem2e1", 26 | 27 | "v128:mem0e12", 28 | "v128:mem1e12", 29 | "v128:mem2e12", 30 | 31 | "v128:mem0e15", 32 | "v128:mem1e15", 33 | "v128:mem2e15", 34 | 35 | "v128:s7mem0e15", 36 | "v128:s7mem1e15", 37 | "v128:s7mem2e15", 38 | ] 39 | 40 | rsp_code = """ 41 | li a0,$0 42 | li a1,$800 43 | 44 | lqv v0[e0],$00(a0) 45 | lqv v1[e0],$10(a0) 46 | lqv v2[e0],$20(a0) 47 | lqv v3[e0],$30(a0) 48 | lqv v4[e0],$40(a0) 49 | lqv v5[e0],$50(a0) 50 | lqv v6[e0],$60(a0) 51 | lqv v7[e0],$70(a0) 52 | 53 | lw t4,$80(a0) // input: offset 54 | add a1,t4 55 | 56 | swv v0[e0],$10(a1) 57 | addi a1,$30 58 | 59 | swv v7[e0],$10(a1) 60 | addi a1,$30 61 | 62 | swv v0[e1],$10(a1) 63 | addi a1,$30 64 | 65 | swv v0[e12],$10(a1) 66 | addi a1,$30 67 | 68 | swv v0[e15],$10(a1) 69 | addi a1,$30 70 | 71 | swv v7[e15],$10(a1) 72 | addi a1,$30 73 | 74 | break 75 | """ 76 | 77 | [[test]] 78 | name = "offset0" 79 | input = [ 80 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 81 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 82 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 83 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 84 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 85 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 86 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 87 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 88 | 0, # offset 89 | 0, # dummy 90 | ] 91 | [[test]] 92 | name = "offset1" 93 | input = [ 94 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 95 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 96 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 97 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 98 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 99 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 100 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 101 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 102 | 1, # offset 103 | 0, # dummy 104 | ] 105 | [[test]] 106 | name = "offset7" 107 | input = [ 108 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 109 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 110 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 111 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 112 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 113 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 114 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 115 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 116 | 7, # offset 117 | 0, # dummy 118 | ] 119 | [[test]] 120 | name = "offset8" 121 | input = [ 122 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 123 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 124 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 125 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 126 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 127 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 128 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 129 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 130 | 8, # offset 131 | 0, # dummy 132 | ] 133 | [[test]] 134 | name = "offset15" 135 | input = [ 136 | 0x8081_8283, 0x8485_8687, 0x8889_8AAB, 0x8C8D_8E8F, # v0 137 | 0x9091_9293, 0x9495_9697, 0x9899_9AAB, 0x9C9D_9E9F, # v1 138 | 0xA0A1_A2A3, 0xA4A5_A6A7, 0xA8A9_AAAB, 0xACAD_AEAF, # v2 139 | 0xB0B1_B2B3, 0xB4B5_B6B7, 0xB8B9_BABB, 0xBCBD_BEBF, # v3 140 | 0xC0C1_C2C3, 0xC4C5_C6C7, 0xC8C9_CAAB, 0xCCCD_CECF, # v4 141 | 0xD0D1_D2D3, 0xD4D5_D6D7, 0xD8D9_DAAB, 0xDCDD_DEDF, # v5 142 | 0xE0E1_E2E3, 0xE4E5_E6E7, 0xE8E9_EAAB, 0xECED_EEEF, # v6 143 | 0xF0F1_F2F3, 0xF4F5_F6F7, 0xF8F9_FAAB, 0xFCFD_FEFF, # v7 144 | 15, # offset 145 | 0, # dummy 146 | ] 147 | -------------------------------------------------------------------------------- /tests/gengolden/vadd.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vadd.golden -------------------------------------------------------------------------------- /tests/gengolden/vadd.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vadd.rsp -------------------------------------------------------------------------------- /tests/gengolden/vadd.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:vcc", 6 | "u32:vce", 7 | "u32:dummy", 8 | ] 9 | 10 | output_desc = [ 11 | "v128:res", 12 | "v128:accum_lo", 13 | "v128:accum_md", 14 | "v128:accum_hi", 15 | "u32:vco", 16 | "u32:vcc", 17 | "u32:vce", 18 | "u32:padding", 19 | ] 20 | 21 | rsp_code = """ 22 | li a0,$0 23 | li a1,$800 24 | 25 | lw t0,$20(a0) 26 | ctc2 t0,vco 27 | lw t0,$24(a0) 28 | ctc2 t0,vcc 29 | lw t0,$28(a0) 30 | ctc2 t0,vce 31 | lqv v0[e0],$00(a0) 32 | lqv v1[e0],$10(a0) 33 | 34 | vxor v2,v2,v2 35 | vadd v2,v0,v1[e0] // V0+V1 36 | 37 | sqv v2[e0],$00(a1) 38 | 39 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 40 | sqv v0[e0],$10(a1) 41 | 42 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 43 | sqv v0[e0],$20(a1) 44 | 45 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 46 | sqv v0[e0],$30(a1) 47 | 48 | li t0,0 49 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 50 | sw t0,$40(a1) 51 | li t0,0 52 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 53 | sw t0,$44(a1) 54 | li t0,0 55 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 56 | sw t0,$48(a1) 57 | 58 | break 59 | """ 60 | 61 | [[test]] 62 | name = "basic" 63 | input = [ 64 | 0x0400_7000, 0x7000_9FFF, 0x0000_3333, 0xFFFF_0001, # v0 65 | 0x0300_2000, 0xF000_9FFF, 0x0000_4444, 0x0002_0001, # v1 66 | 0xAAF2, # VCO 67 | 0xAAF2, # VCC 68 | 0xAAF2, # VCE 69 | 0, # dummy 70 | ] 71 | 72 | [[test]] 73 | name = "overflow1" 74 | input = [ 75 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 76 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 77 | 0x0000, # VCO 78 | 0xAAF2, # VCC 79 | 0xAAF2, # VCE 80 | 0, # dummy 81 | ] 82 | 83 | [[test]] 84 | name = "overflow2" 85 | input = [ 86 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 87 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 88 | 0xFFFF, # VCO 89 | 0x5555, # VCC 90 | 0x5555, # VCE 91 | 0, # dummy 92 | ] 93 | -------------------------------------------------------------------------------- /tests/gengolden/vaddc.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vaddc.golden -------------------------------------------------------------------------------- /tests/gengolden/vaddc.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vaddc.rsp -------------------------------------------------------------------------------- /tests/gengolden/vaddc.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:padding", 6 | ] 7 | 8 | output_desc = [ 9 | "v128:res", 10 | "v128:accum_lo", 11 | "v128:accum_md", 12 | "v128:accum_hi", 13 | "u32:vco", 14 | "u32:vcc", 15 | "u32:vce", 16 | "u32:padding", 17 | ] 18 | 19 | rsp_code = """ 20 | li a0,$0 21 | li a1,$800 22 | 23 | lw t0,$20(a0) 24 | ctc2 t0,vco 25 | lqv v0[e0],$00(a0) 26 | lqv v1[e0],$10(a0) 27 | 28 | vxor v2,v2,v2 29 | vaddc v2,v0,v1[e0] // V0+V1 30 | 31 | sqv v2[e0],$00(a1) 32 | 33 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 34 | sqv v0[e0],$10(a1) 35 | 36 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 37 | sqv v0[e0],$20(a1) 38 | 39 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 40 | sqv v0[e0],$30(a1) 41 | 42 | li t0,0 43 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 44 | sw t0,$40(a1) 45 | li t0,0 46 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 47 | sw t0,$44(a1) 48 | li t0,0 49 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 50 | sw t0,$48(a1) 51 | 52 | break 53 | """ 54 | 55 | [[test]] 56 | name = "basic" 57 | input = [ 58 | 0x0400_7000, 0x7000_9FFF, 0x0000_3333, 0xFFFF_0001, # v0 59 | 0x0300_2000, 0xF000_9FFF, 0x0000_4444, 0x0002_0001, # v1 60 | 0xAAF2, 0, # VCO 61 | ] 62 | 63 | [[test]] 64 | name = "overflow1" 65 | input = [ 66 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 67 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 68 | 0x0000, 0, # VCO 69 | ] 70 | 71 | [[test]] 72 | name = "overflow2" 73 | input = [ 74 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 75 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 76 | 0xFFFF, 0, # VCO 77 | ] 78 | -------------------------------------------------------------------------------- /tests/gengolden/vch.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vch.golden -------------------------------------------------------------------------------- /tests/gengolden/vch.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vch.rsp -------------------------------------------------------------------------------- /tests/gengolden/vcl.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vcl.golden -------------------------------------------------------------------------------- /tests/gengolden/vcl.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vcl.rsp -------------------------------------------------------------------------------- /tests/gengolden/vcr.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vcr.golden -------------------------------------------------------------------------------- /tests/gengolden/vcr.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vcr.rsp -------------------------------------------------------------------------------- /tests/gengolden/veq.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/veq.golden -------------------------------------------------------------------------------- /tests/gengolden/veq.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/veq.rsp -------------------------------------------------------------------------------- /tests/gengolden/veq.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:vcc", 6 | "u32:vce", 7 | "u32:padding", 8 | ] 9 | 10 | output_desc = [ 11 | "v128:res", 12 | "v128:accum_lo", 13 | "v128:accum_md", 14 | "v128:accum_hi", 15 | "u32:vco", 16 | "u32:vcc", 17 | "u32:vce", 18 | "u32:padding", 19 | ] 20 | 21 | rsp_code = """ 22 | li a0,$0 23 | li a1,$800 24 | 25 | vxor v2,v2 26 | lqv v0[e0],$00(a0) 27 | lqv v1[e0],$10(a0) 28 | 29 | lw t0,$20(a0) 30 | ctc2 t0,vco 31 | lw t0,$24(a0) 32 | ctc2 t0,vcc 33 | lw t0,$28(a0) 34 | ctc2 t0,vce 35 | 36 | veq v2,v0,v1 37 | 38 | sqv v2[e0],$00(a1) 39 | 40 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 41 | sqv v0[e0],$10(a1) 42 | 43 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 44 | sqv v0[e0],$20(a1) 45 | 46 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 47 | sqv v0[e0],$30(a1) 48 | 49 | li t0,0 50 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 51 | sw t0,$40(a1) 52 | li t0,0 53 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 54 | sw t0,$44(a1) 55 | li t0,0 56 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 57 | sw t0,$48(a1) 58 | 59 | break 60 | """ 61 | 62 | 63 | 64 | [[test]] 65 | name = "basic" 66 | input = [ 67 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 68 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 69 | 0x0000, # VCO 70 | 0x0000, # VCC 71 | 0x0000, # VCE 72 | 0, # dummy 73 | ] 74 | 75 | [[test]] 76 | name = "with_vco" 77 | input = [ 78 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 79 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 80 | 0xFFFF, # VCO 81 | 0x0000, # VCC 82 | 0x0000, # VCE 83 | 0, # dummy 84 | ] 85 | 86 | [[test]] 87 | name = "with_vcc" 88 | input = [ 89 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 90 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 91 | 0x0000, # VCO 92 | 0xFFFF, # VCC 93 | 0x0000, # VCE 94 | 0, # dummy 95 | ] 96 | 97 | [[test]] 98 | name = "with_vce" 99 | input = [ 100 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 101 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 102 | 0x0000, # VCO 103 | 0x0000, # VCC 104 | 0xFFFF, # VCE 105 | 0, # dummy 106 | ] 107 | 108 | [[test]] 109 | name = "with_vco_vce" 110 | input = [ 111 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 112 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 113 | 0xFFFF, # VCO 114 | 0x0000, # VCC 115 | 0xFFFF, # VCE 116 | 0, # dummy 117 | ] 118 | 119 | [[test]] 120 | name = "with_vcc_vce" 121 | input = [ 122 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 123 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 124 | 0x0000, # VCO 125 | 0xFFFF, # VCC 126 | 0xFFFF, # VCE 127 | 0, # dummy 128 | ] 129 | 130 | [[test]] 131 | name = "with_vco_vcc" 132 | input = [ 133 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 134 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 135 | 0xFFFF, # VCO 136 | 0xFFFF, # VCC 137 | 0x0000, # VCE 138 | 0, # dummy 139 | ] 140 | 141 | [[test]] 142 | name = "with_vco_vcc_vce" 143 | input = [ 144 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 145 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 146 | 0xFFFF, # VCO 147 | 0xFFFF, # VCC 148 | 0xFFFF, # VCE 149 | 0, # dummy 150 | ] 151 | 152 | [[test]] 153 | name = "with_rand1" 154 | input = [ 155 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 156 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 157 | 0xAAAA, # VCO 158 | 0xAAAA, # VCC 159 | 0xAAAA, # VCE 160 | 0, # dummy 161 | ] 162 | 163 | [[test]] 164 | name = "with_rand2" 165 | input = [ 166 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 167 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 168 | 0x5555, # VCO 169 | 0x5555, # VCC 170 | 0x5555, # VCE 171 | 0, # dummy 172 | ] 173 | 174 | [[test]] 175 | name = "with_rand3" 176 | input = [ 177 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 178 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 179 | 0xAAAA, # VCO 180 | 0x5555, # VCC 181 | 0xAAAA, # VCE 182 | 0, # dummy 183 | ] 184 | -------------------------------------------------------------------------------- /tests/gengolden/vge.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vge.golden -------------------------------------------------------------------------------- /tests/gengolden/vge.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vge.rsp -------------------------------------------------------------------------------- /tests/gengolden/vge.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:vcc", 6 | "u32:vce", 7 | "u32:padding", 8 | ] 9 | 10 | output_desc = [ 11 | "v128:res", 12 | "v128:accum_lo", 13 | "v128:accum_md", 14 | "v128:accum_hi", 15 | "u32:vco", 16 | "u32:vcc", 17 | "u32:vce", 18 | "u32:padding", 19 | ] 20 | 21 | rsp_code = """ 22 | li a0,$0 23 | li a1,$800 24 | 25 | vxor v2,v2 26 | lqv v0[e0],$00(a0) 27 | lqv v1[e0],$10(a0) 28 | 29 | lw t0,$20(a0) 30 | ctc2 t0,vco 31 | lw t0,$24(a0) 32 | ctc2 t0,vcc 33 | lw t0,$28(a0) 34 | ctc2 t0,vce 35 | 36 | vge v2,v0,v1 37 | 38 | sqv v2[e0],$00(a1) 39 | 40 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 41 | sqv v0[e0],$10(a1) 42 | 43 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 44 | sqv v0[e0],$20(a1) 45 | 46 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 47 | sqv v0[e0],$30(a1) 48 | 49 | li t0,0 50 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 51 | sw t0,$40(a1) 52 | li t0,0 53 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 54 | sw t0,$44(a1) 55 | li t0,0 56 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 57 | sw t0,$48(a1) 58 | 59 | break 60 | """ 61 | 62 | 63 | 64 | [[test]] 65 | name = "basic" 66 | input = [ 67 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 68 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 69 | 0x0000, # VCO 70 | 0x0000, # VCC 71 | 0x0000, # VCE 72 | 0, # dummy 73 | ] 74 | 75 | [[test]] 76 | name = "with_vco" 77 | input = [ 78 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 79 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 80 | 0xFFFF, # VCO 81 | 0x0000, # VCC 82 | 0x0000, # VCE 83 | 0, # dummy 84 | ] 85 | 86 | [[test]] 87 | name = "with_vcc" 88 | input = [ 89 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 90 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 91 | 0x0000, # VCO 92 | 0xFFFF, # VCC 93 | 0x0000, # VCE 94 | 0, # dummy 95 | ] 96 | 97 | [[test]] 98 | name = "with_vce" 99 | input = [ 100 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 101 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 102 | 0x0000, # VCO 103 | 0x0000, # VCC 104 | 0xFFFF, # VCE 105 | 0, # dummy 106 | ] 107 | 108 | [[test]] 109 | name = "with_vco_vce" 110 | input = [ 111 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 112 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 113 | 0xFFFF, # VCO 114 | 0x0000, # VCC 115 | 0xFFFF, # VCE 116 | 0, # dummy 117 | ] 118 | 119 | [[test]] 120 | name = "with_vcc_vce" 121 | input = [ 122 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 123 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 124 | 0x0000, # VCO 125 | 0xFFFF, # VCC 126 | 0xFFFF, # VCE 127 | 0, # dummy 128 | ] 129 | 130 | [[test]] 131 | name = "with_vco_vcc" 132 | input = [ 133 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 134 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 135 | 0xFFFF, # VCO 136 | 0xFFFF, # VCC 137 | 0x0000, # VCE 138 | 0, # dummy 139 | ] 140 | 141 | [[test]] 142 | name = "with_vco_vcc_vce" 143 | input = [ 144 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 145 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 146 | 0xFFFF, # VCO 147 | 0xFFFF, # VCC 148 | 0xFFFF, # VCE 149 | 0, # dummy 150 | ] 151 | 152 | [[test]] 153 | name = "with_rand1" 154 | input = [ 155 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 156 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 157 | 0xAAAA, # VCO 158 | 0xAAAA, # VCC 159 | 0xAAAA, # VCE 160 | 0, # dummy 161 | ] 162 | 163 | [[test]] 164 | name = "with_rand2" 165 | input = [ 166 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 167 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 168 | 0x5555, # VCO 169 | 0x5555, # VCC 170 | 0x5555, # VCE 171 | 0, # dummy 172 | ] 173 | 174 | [[test]] 175 | name = "with_rand3" 176 | input = [ 177 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 178 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 179 | 0xAAAA, # VCO 180 | 0x5555, # VCC 181 | 0xAAAA, # VCE 182 | 0, # dummy 183 | ] 184 | -------------------------------------------------------------------------------- /tests/gengolden/vlogical.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vlogical.golden -------------------------------------------------------------------------------- /tests/gengolden/vlogical.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vlogical.rsp -------------------------------------------------------------------------------- /tests/gengolden/vlogical.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:and", 8 | "v128:nand", 9 | "v128:or", 10 | "v128:nor", 11 | "v128:xor", 12 | "v128:nxor", 13 | ] 14 | 15 | rsp_code = """ 16 | li a0,$0 17 | li a1,$800 18 | 19 | lqv v0[e0],$00(a0) 20 | lqv v1[e0],$10(a0) 21 | 22 | vand v2,v0,v1[e0] 23 | vnand v3,v0,v1[e0] 24 | vor v4,v0,v1[e0] 25 | vnor v5,v0,v1[e0] 26 | vxor v6,v0,v1[e0] 27 | vnxor v7,v0,v1[e0] 28 | 29 | sqv v2[e0],$00(a1) 30 | sqv v3[e0],$10(a1) 31 | sqv v4[e0],$20(a1) 32 | sqv v5[e0],$30(a1) 33 | sqv v6[e0],$40(a1) 34 | sqv v7[e0],$50(a1) 35 | 36 | break 37 | """ 38 | 39 | [[test]] 40 | name="basic" 41 | input=[ 42 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 43 | 0x0F0F_F0F0, 0x0F0F_F0F0, 0x0F0F_F0F0, 0x0F0F_F0F0, # v1 44 | ] 45 | -------------------------------------------------------------------------------- /tests/gengolden/vlt.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vlt.golden -------------------------------------------------------------------------------- /tests/gengolden/vlt.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vlt.rsp -------------------------------------------------------------------------------- /tests/gengolden/vlt.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:vcc", 6 | "u32:vce", 7 | "u32:padding", 8 | ] 9 | 10 | output_desc = [ 11 | "v128:res", 12 | "v128:accum_lo", 13 | "v128:accum_md", 14 | "v128:accum_hi", 15 | "u32:vco", 16 | "u32:vcc", 17 | "u32:vce", 18 | "u32:padding", 19 | ] 20 | 21 | rsp_code = """ 22 | li a0,$0 23 | li a1,$800 24 | 25 | vxor v2,v2 26 | lqv v0[e0],$00(a0) 27 | lqv v1[e0],$10(a0) 28 | 29 | lw t0,$20(a0) 30 | ctc2 t0,vco 31 | lw t0,$24(a0) 32 | ctc2 t0,vcc 33 | lw t0,$28(a0) 34 | ctc2 t0,vce 35 | 36 | vlt v2,v0,v1 37 | 38 | sqv v2[e0],$00(a1) 39 | 40 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 41 | sqv v0[e0],$10(a1) 42 | 43 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 44 | sqv v0[e0],$20(a1) 45 | 46 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 47 | sqv v0[e0],$30(a1) 48 | 49 | li t0,0 50 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 51 | sw t0,$40(a1) 52 | li t0,0 53 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 54 | sw t0,$44(a1) 55 | li t0,0 56 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 57 | sw t0,$48(a1) 58 | 59 | break 60 | """ 61 | 62 | 63 | 64 | [[test]] 65 | name = "basic" 66 | input = [ 67 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 68 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 69 | 0x0000, # VCO 70 | 0x0000, # VCC 71 | 0x0000, # VCE 72 | 0, # dummy 73 | ] 74 | 75 | [[test]] 76 | name = "with_vco" 77 | input = [ 78 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 79 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 80 | 0xFFFF, # VCO 81 | 0x0000, # VCC 82 | 0x0000, # VCE 83 | 0, # dummy 84 | ] 85 | 86 | [[test]] 87 | name = "with_vcc" 88 | input = [ 89 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 90 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 91 | 0x0000, # VCO 92 | 0xFFFF, # VCC 93 | 0x0000, # VCE 94 | 0, # dummy 95 | ] 96 | 97 | [[test]] 98 | name = "with_vce" 99 | input = [ 100 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 101 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 102 | 0x0000, # VCO 103 | 0x0000, # VCC 104 | 0xFFFF, # VCE 105 | 0, # dummy 106 | ] 107 | 108 | [[test]] 109 | name = "with_vco_vce" 110 | input = [ 111 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 112 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 113 | 0xFFFF, # VCO 114 | 0x0000, # VCC 115 | 0xFFFF, # VCE 116 | 0, # dummy 117 | ] 118 | 119 | [[test]] 120 | name = "with_vcc_vce" 121 | input = [ 122 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 123 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 124 | 0x0000, # VCO 125 | 0xFFFF, # VCC 126 | 0xFFFF, # VCE 127 | 0, # dummy 128 | ] 129 | 130 | [[test]] 131 | name = "with_vco_vcc" 132 | input = [ 133 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 134 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 135 | 0xFFFF, # VCO 136 | 0xFFFF, # VCC 137 | 0x0000, # VCE 138 | 0, # dummy 139 | ] 140 | 141 | [[test]] 142 | name = "with_vco_vcc_vce" 143 | input = [ 144 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 145 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 146 | 0xFFFF, # VCO 147 | 0xFFFF, # VCC 148 | 0xFFFF, # VCE 149 | 0, # dummy 150 | ] 151 | 152 | [[test]] 153 | name = "with_rand1" 154 | input = [ 155 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 156 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 157 | 0xAAAA, # VCO 158 | 0xAAAA, # VCC 159 | 0xAAAA, # VCE 160 | 0, # dummy 161 | ] 162 | 163 | [[test]] 164 | name = "with_rand2" 165 | input = [ 166 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 167 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 168 | 0x5555, # VCO 169 | 0x5555, # VCC 170 | 0x5555, # VCE 171 | 0, # dummy 172 | ] 173 | 174 | [[test]] 175 | name = "with_rand3" 176 | input = [ 177 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 178 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 179 | 0xAAAA, # VCO 180 | 0x5555, # VCC 181 | 0xAAAA, # VCE 182 | 0, # dummy 183 | ] 184 | -------------------------------------------------------------------------------- /tests/gengolden/vmacf.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmacf.golden -------------------------------------------------------------------------------- /tests/gengolden/vmacf.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmacf.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmacf.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:1_res", 8 | "v128:1_accum_lo", 9 | "v128:1_accum_md", 10 | "v128:1_accum_hi", 11 | "u32:1_vco", 12 | "u32:1_vcc", 13 | "u32:1_vce", 14 | "u32:1_padding", 15 | 16 | "v128:2_res", 17 | "v128:2_accum_lo", 18 | "v128:2_accum_md", 19 | "v128:2_accum_hi", 20 | "u32:2_vco", 21 | "u32:2_vcc", 22 | "u32:2_vce", 23 | "u32:2_padding", 24 | 25 | "v128:3_res", 26 | "v128:3_accum_lo", 27 | "v128:3_accum_md", 28 | "v128:3_accum_hi", 29 | "u32:3_vco", 30 | "u32:3_vcc", 31 | "u32:3_vce", 32 | "u32:3_padding", 33 | 34 | "v128:4_res", 35 | "v128:4_accum_lo", 36 | "v128:4_accum_md", 37 | "v128:4_accum_hi", 38 | "u32:4_vco", 39 | "u32:4_vcc", 40 | "u32:4_vce", 41 | "u32:4_padding", 42 | 43 | "v128:5_res", 44 | "v128:5_accum_lo", 45 | "v128:5_accum_md", 46 | "v128:5_accum_hi", 47 | "u32:5_vco", 48 | "u32:5_vcc", 49 | "u32:5_vce", 50 | "u32:5_padding", 51 | ] 52 | 53 | 54 | rsp_code = """ 55 | li a0,$0 56 | li a1,$800 57 | 58 | li s0, $4 // Loop 4 times 59 | 60 | Loop: 61 | lqv v0[e0],$00(a0) // $00: V0 62 | lqv v1[e0],$10(a0) // $10: V1 63 | vmacf v0,v1[e0] 64 | 65 | sqv v0[e0],$00(a1) 66 | 67 | vsar v0,v0[e10] 68 | sqv v0[e0],$10(a1) 69 | vsar v0,v0[e9] 70 | sqv v0[e0],$20(a1) 71 | 72 | vsar v0,v0[e8] 73 | sqv v0[e0],$30(a1) 74 | 75 | li t0,0 76 | cfc2 t0,vco 77 | sw t0,$40(a1) 78 | li t0,0 79 | cfc2 t0,vcc 80 | sw t0,$44(a1) 81 | li t0,0 82 | cfc2 t0,vce 83 | sw t0,$48(a1) 84 | 85 | subi s0,1 86 | bnez s0, Loop 87 | addi a1,$50 88 | 89 | // Last iteration: first dirty accumulator, so to test saturation 90 | lqv v0[e0],$00(a0) // $00: V0 91 | lqv v1[e0],$10(a0) // $10: V1 92 | vmudh v0,v1[e0] 93 | vmacf v0,v1[e0] 94 | 95 | sqv v0[e0],$00(a1) 96 | 97 | vsar v0,v0[e10] 98 | sqv v0[e0],$10(a1) 99 | vsar v0,v0[e9] 100 | sqv v0[e0],$20(a1) 101 | 102 | vsar v0,v0[e8] 103 | sqv v0[e0],$30(a1) 104 | 105 | li t0,0 106 | cfc2 t0,vco 107 | sw t0,$40(a1) 108 | li t0,0 109 | cfc2 t0,vcc 110 | sw t0,$44(a1) 111 | li t0,0 112 | cfc2 t0,vce 113 | sw t0,$48(a1) 114 | 115 | break // Set SP Status Halt, Broke & Check For Interrupt, Set SP Program Counter To $0000 116 | """ 117 | 118 | [[test]] 119 | name = "basic" 120 | input = [ 121 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 122 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 123 | ] 124 | 125 | # FIXME: add code to clean accumulator to be able to run more than one test 126 | 127 | [[test]] 128 | name = "negate" 129 | input = [ 130 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 131 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 132 | ] 133 | 134 | [[test]] 135 | name = "overflow" 136 | input = [ 137 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 138 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 139 | ] 140 | 141 | -------------------------------------------------------------------------------- /tests/gengolden/vmacu.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmacu.golden -------------------------------------------------------------------------------- /tests/gengolden/vmacu.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmacu.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmacu.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:1_res", 8 | "v128:1_accum_lo", 9 | "v128:1_accum_md", 10 | "v128:1_accum_hi", 11 | "u32:1_vco", 12 | "u32:1_vcc", 13 | "u32:1_vce", 14 | "u32:1_padding", 15 | 16 | "v128:2_res", 17 | "v128:2_accum_lo", 18 | "v128:2_accum_md", 19 | "v128:2_accum_hi", 20 | "u32:2_vco", 21 | "u32:2_vcc", 22 | "u32:2_vce", 23 | "u32:2_padding", 24 | 25 | "v128:3_res", 26 | "v128:3_accum_lo", 27 | "v128:3_accum_md", 28 | "v128:3_accum_hi", 29 | "u32:3_vco", 30 | "u32:3_vcc", 31 | "u32:3_vce", 32 | "u32:3_padding", 33 | 34 | "v128:4_res", 35 | "v128:4_accum_lo", 36 | "v128:4_accum_md", 37 | "v128:4_accum_hi", 38 | "u32:4_vco", 39 | "u32:4_vcc", 40 | "u32:4_vce", 41 | "u32:4_padding", 42 | ] 43 | 44 | 45 | rsp_code = """ 46 | li a0,$0 47 | li a1,$800 48 | 49 | li s0, $4 // Loop 4 times 50 | 51 | Loop: 52 | lqv v0[e0],$00(a0) // $00: V0 53 | lqv v1[e0],$10(a0) // $10: V1 54 | vmacu v0,v1[e0] 55 | 56 | sqv v0[e0],$00(a1) 57 | 58 | vsar v0,v0[e10] 59 | sqv v0[e0],$10(a1) 60 | vsar v0,v0[e9] 61 | sqv v0[e0],$20(a1) 62 | 63 | vsar v0,v0[e8] 64 | sqv v0[e0],$30(a1) 65 | 66 | li t0,0 67 | cfc2 t0,vco 68 | sw t0,$40(a1) 69 | li t0,0 70 | cfc2 t0,vcc 71 | sw t0,$44(a1) 72 | li t0,0 73 | cfc2 t0,vce 74 | sw t0,$48(a1) 75 | 76 | subi s0,1 77 | bnez s0, Loop 78 | addi a1,$50 79 | 80 | break // Set SP Status Halt, Broke & Check For Interrupt, Set SP Program Counter To $0000 81 | """ 82 | 83 | [[test]] 84 | name = "basic" 85 | input = [ 86 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 87 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 88 | ] 89 | 90 | # FIXME: add code to clean accumulator to be able to run more than one test 91 | 92 | [[test]] 93 | name = "negate" 94 | input = [ 95 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 96 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 97 | ] 98 | 99 | [[test]] 100 | name = "overflow" 101 | input = [ 102 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 103 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 104 | ] 105 | 106 | -------------------------------------------------------------------------------- /tests/gengolden/vmadh.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmadh.golden -------------------------------------------------------------------------------- /tests/gengolden/vmadh.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmadh.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmadh.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:res", 8 | "v128:accum_lo", 9 | "v128:accum_md", 10 | "v128:accum_hi", 11 | "u32:vco", 12 | "u32:vcc", 13 | "u32:vce", 14 | "u32:padding", 15 | ] 16 | 17 | rsp_code = """ 18 | li a0,$0 19 | li a1,$800 20 | 21 | lqv v0[e0],$00(a0) 22 | lqv v1[e0],$10(a0) 23 | 24 | vmadh v0,v1[e0] // V0*V1 25 | 26 | sqv v0[e0],$00(a1) 27 | 28 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 29 | sqv v0[e0],$10(a1) 30 | 31 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 32 | sqv v0[e0],$20(a1) 33 | 34 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 35 | sqv v0[e0],$30(a1) 36 | 37 | li t0,0 38 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 39 | sw t0,$40(a1) 40 | li t0,0 41 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 42 | sw t0,$44(a1) 43 | li t0,0 44 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 45 | sw t0,$48(a1) 46 | 47 | break 48 | """ 49 | 50 | [[test]] 51 | name = "basic" 52 | input = [ 53 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 54 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 55 | ] 56 | 57 | [[test]] 58 | name = "negate" 59 | input = [ 60 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 61 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 62 | ] 63 | 64 | [[test]] 65 | name = "overflow" 66 | input = [ 67 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 68 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 69 | ] 70 | 71 | -------------------------------------------------------------------------------- /tests/gengolden/vmadl.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmadl.golden -------------------------------------------------------------------------------- /tests/gengolden/vmadl.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmadl.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmadl.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:res", 8 | "v128:accum_lo", 9 | "v128:accum_md", 10 | "v128:accum_hi", 11 | "u32:vco", 12 | "u32:vcc", 13 | "u32:vce", 14 | "u32:padding", 15 | "v128:sat", 16 | ] 17 | 18 | rsp_code = """ 19 | li a0,$0 20 | li a1,$800 21 | 22 | lqv v0[e0],$00(a0) 23 | lqv v1[e0],$10(a0) 24 | 25 | vmadl v0,v1[e0] // V0*V1 26 | 27 | sqv v0[e0],$00(a1) 28 | 29 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 30 | sqv v0[e0],$10(a1) 31 | 32 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 33 | sqv v0[e0],$20(a1) 34 | 35 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 36 | sqv v0[e0],$30(a1) 37 | 38 | li t0,0 39 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 40 | sw t0,$40(a1) 41 | li t0,0 42 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 43 | sw t0,$44(a1) 44 | li t0,0 45 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 46 | sw t0,$48(a1) 47 | 48 | 49 | // Perform a vmadl after having filled in the accumulator with large values. 50 | // This tests the saturation. 51 | lqv v0[e0],$00(a0) 52 | lqv v1[e0],$10(a0) 53 | vmudh v0,v1[e0] 54 | vmadl v0,v1[e0] 55 | sqv v0[e0],$50(a1) 56 | 57 | break 58 | """ 59 | 60 | [[test]] 61 | name = "basic" 62 | input = [ 63 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 64 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 65 | ] 66 | 67 | [[test]] 68 | name = "negate" 69 | input = [ 70 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 71 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 72 | ] 73 | 74 | [[test]] 75 | name = "overflow" 76 | input = [ 77 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 78 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 79 | ] 80 | 81 | -------------------------------------------------------------------------------- /tests/gengolden/vmadm.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmadm.golden -------------------------------------------------------------------------------- /tests/gengolden/vmadm.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmadm.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmadm.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:res", 8 | "v128:accum_lo", 9 | "v128:accum_md", 10 | "v128:accum_hi", 11 | "u32:vco", 12 | "u32:vcc", 13 | "u32:vce", 14 | "u32:padding", 15 | "v128:sat", 16 | ] 17 | 18 | rsp_code = """ 19 | li a0,$0 20 | li a1,$800 21 | 22 | lqv v0[e0],$00(a0) 23 | lqv v1[e0],$10(a0) 24 | 25 | vmadm v0,v1[e0] // V0*V1 26 | 27 | sqv v0[e0],$00(a1) 28 | 29 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 30 | sqv v0[e0],$10(a1) 31 | 32 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 33 | sqv v0[e0],$20(a1) 34 | 35 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 36 | sqv v0[e0],$30(a1) 37 | 38 | li t0,0 39 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 40 | sw t0,$40(a1) 41 | li t0,0 42 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 43 | sw t0,$44(a1) 44 | li t0,0 45 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 46 | sw t0,$48(a1) 47 | 48 | 49 | // Perform a vmadm after having filled in the accumulator with large values. 50 | // This tests the saturation. 51 | lqv v0[e0],$00(a0) 52 | lqv v1[e0],$10(a0) 53 | vmudh v0,v1[e0] 54 | vmadm v0,v1[e0] 55 | sqv v0[e0],$50(a1) 56 | 57 | break 58 | """ 59 | 60 | [[test]] 61 | name = "basic" 62 | input = [ 63 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 64 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 65 | ] 66 | 67 | [[test]] 68 | name = "negate" 69 | input = [ 70 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 71 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 72 | ] 73 | 74 | [[test]] 75 | name = "overflow" 76 | input = [ 77 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 78 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 79 | ] 80 | 81 | -------------------------------------------------------------------------------- /tests/gengolden/vmadn.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmadn.golden -------------------------------------------------------------------------------- /tests/gengolden/vmadn.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmadn.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmadn.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:1_res", 8 | "v128:1_accum_lo", 9 | "v128:1_accum_md", 10 | "v128:1_accum_hi", 11 | "u32:1_vco", 12 | "u32:1_vcc", 13 | "u32:1_vce", 14 | "u32:1_padding", 15 | 16 | "v128:2_res", 17 | "v128:2_accum_lo", 18 | "v128:2_accum_md", 19 | "v128:2_accum_hi", 20 | "u32:2_vco", 21 | "u32:2_vcc", 22 | "u32:2_vce", 23 | "u32:2_padding", 24 | 25 | "v128:3_res", 26 | "v128:3_accum_lo", 27 | "v128:3_accum_md", 28 | "v128:3_accum_hi", 29 | "u32:3_vco", 30 | "u32:3_vcc", 31 | "u32:3_vce", 32 | "u32:3_padding", 33 | 34 | "v128:4_res", 35 | "v128:4_accum_lo", 36 | "v128:4_accum_md", 37 | "v128:4_accum_hi", 38 | "u32:4_vco", 39 | "u32:4_vcc", 40 | "u32:4_vce", 41 | "u32:4_padding", 42 | 43 | "v128:sat1", 44 | "v128:sat2", 45 | "v128:sat3", 46 | "v128:sat4", 47 | ] 48 | 49 | 50 | rsp_code = """ 51 | li a0,$0 52 | li a1,$800 53 | 54 | li s0, $4 // Loop 4 times 55 | 56 | Loop: 57 | lqv v0[e0],$00(a0) // $00: V0 58 | lqv v1[e0],$10(a0) // $10: V1 59 | vmadn v2,v1,v1[e0] 60 | 61 | sqv v2[e0],$00(a1) 62 | 63 | vsar v0,v0[e10] 64 | sqv v0[e0],$10(a1) 65 | vsar v0,v0[e9] 66 | sqv v0[e0],$20(a1) 67 | 68 | vsar v0,v0[e8] 69 | sqv v0[e0],$30(a1) 70 | 71 | li t0,0 72 | cfc2 t0,vco 73 | sw t0,$40(a1) 74 | li t0,0 75 | cfc2 t0,vcc 76 | sw t0,$44(a1) 77 | li t0,0 78 | cfc2 t0,vce 79 | sw t0,$48(a1) 80 | 81 | subi s0,1 82 | bnez s0, Loop 83 | addi a1,$50 84 | 85 | // Perform a vmadn after having filled in the accumulator with large values. 86 | // This tests the saturation. 87 | lqv v0[e0],$00(a0) 88 | lqv v1[e0],$10(a0) 89 | vmudh v0,v1[e0] 90 | vmadn v0,v1[e0] 91 | sqv v0[e0],$00(a1) 92 | vmadn v0,v1[e0] 93 | sqv v0[e0],$10(a1) 94 | vmadn v0,v1[e0] 95 | sqv v0[e0],$20(a1) 96 | vmadn v0,v1[e0] 97 | sqv v0[e0],$30(a1) 98 | 99 | break // Set SP Status Halt, Broke & Check For Interrupt, Set SP Program Counter To $0000 100 | """ 101 | 102 | [[test]] 103 | name = "crash" 104 | input = [ 105 | 0x0011_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 106 | 0xFFEE_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 107 | ] 108 | 109 | [[test]] 110 | name = "basic" 111 | input = [ 112 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 113 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 114 | ] 115 | 116 | # FIXME: add code to clean accumulator to be able to run more than one test 117 | 118 | [[test]] 119 | name = "negate" 120 | input = [ 121 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 122 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 123 | ] 124 | 125 | [[test]] 126 | name = "overflow" 127 | input = [ 128 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 129 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 130 | ] 131 | -------------------------------------------------------------------------------- /tests/gengolden/vmrg.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmrg.golden -------------------------------------------------------------------------------- /tests/gengolden/vmrg.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmrg.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmrg.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:vcc", 6 | "u32:vce", 7 | "u32:padding", 8 | ] 9 | 10 | output_desc = [ 11 | "v128:res", 12 | "v128:accum_lo", 13 | "v128:accum_md", 14 | "v128:accum_hi", 15 | "u32:vco", 16 | "u32:vcc", 17 | "u32:vce", 18 | "u32:padding", 19 | ] 20 | 21 | rsp_code = """ 22 | li a0,$0 23 | li a1,$800 24 | 25 | lw t0,$20(a0) 26 | ctc2 t0,vco 27 | lw t0,$24(a0) 28 | ctc2 t0,vcc 29 | lw t0,$28(a0) 30 | ctc2 t0,vce 31 | lqv v0[e0],$00(a0) 32 | lqv v1[e0],$10(a0) 33 | 34 | vxor v2,v2,v2 35 | vmrg v2,v0,v1[e0] 36 | 37 | sqv v2[e0],$00(a1) 38 | 39 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 40 | sqv v0[e0],$10(a1) 41 | 42 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 43 | sqv v0[e0],$20(a1) 44 | 45 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 46 | sqv v0[e0],$30(a1) 47 | 48 | li t0,0 49 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 50 | sw t0,$40(a1) 51 | li t0,0 52 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 53 | sw t0,$44(a1) 54 | li t0,0 55 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 56 | sw t0,$48(a1) 57 | 58 | break 59 | """ 60 | 61 | [[test]] 62 | name = "basic" 63 | input = [ 64 | 0x0400_7000, 0x7000_9FFF, 0x0000_3333, 0xFFFF_0001, # v0 65 | 0x0300_2000, 0xF000_9FFF, 0x0000_4444, 0x0002_0001, # v1 66 | 0xAAF2, # VCO 67 | 0xAAF2, # VCC 68 | 0xAAF2, # VCE 69 | 0, # dummy 70 | ] 71 | 72 | [[test]] 73 | name = "overflow1" 74 | input = [ 75 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 76 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 77 | 0xAAF2, # VCO 78 | 0xAAF2, # VCC 79 | 0xAAF2, # VCE 80 | 0, # dummy 81 | ] 82 | 83 | [[test]] 84 | name = "overflow2" 85 | input = [ 86 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 87 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 88 | 0x5555, # VCO 89 | 0x5555, # VCC 90 | 0x5555, # VCE 91 | 0, # dummy 92 | ] 93 | -------------------------------------------------------------------------------- /tests/gengolden/vmudh.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmudh.golden -------------------------------------------------------------------------------- /tests/gengolden/vmudh.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmudh.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmudh.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:res", 8 | "v128:accum_lo", 9 | "v128:accum_md", 10 | "v128:accum_hi", 11 | "u32:vco", 12 | "u32:vcc", 13 | "u32:vce", 14 | "u32:padding", 15 | ] 16 | 17 | rsp_code = """ 18 | li a0,$0 19 | li a1,$800 20 | 21 | lqv v0[e0],$00(a0) 22 | lqv v1[e0],$10(a0) 23 | 24 | vmudh v0,v1[e0] // V0*V1 25 | 26 | sqv v0[e0],$00(a1) 27 | 28 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 29 | sqv v0[e0],$10(a1) 30 | 31 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 32 | sqv v0[e0],$20(a1) 33 | 34 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 35 | sqv v0[e0],$30(a1) 36 | 37 | li t0,0 38 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 39 | sw t0,$40(a1) 40 | li t0,0 41 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 42 | sw t0,$44(a1) 43 | li t0,0 44 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 45 | sw t0,$48(a1) 46 | 47 | break 48 | """ 49 | 50 | [[test]] 51 | name = "basic" 52 | input = [ 53 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 54 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 55 | ] 56 | 57 | [[test]] 58 | name = "negate" 59 | input = [ 60 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 61 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 62 | ] 63 | 64 | [[test]] 65 | name = "overflow" 66 | input = [ 67 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 68 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 69 | ] 70 | 71 | -------------------------------------------------------------------------------- /tests/gengolden/vmudl.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmudl.golden -------------------------------------------------------------------------------- /tests/gengolden/vmudl.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmudl.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmudl.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:res", 8 | "v128:accum_lo", 9 | "v128:accum_md", 10 | "v128:accum_hi", 11 | "u32:vco", 12 | "u32:vcc", 13 | "u32:vce", 14 | "u32:padding", 15 | ] 16 | 17 | rsp_code = """ 18 | li a0,$0 19 | li a1,$800 20 | 21 | lqv v0[e0],$00(a0) 22 | lqv v1[e0],$10(a0) 23 | 24 | vmudl v0,v1[e0] // V0*V1 25 | 26 | sqv v0[e0],$00(a1) 27 | 28 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 29 | sqv v0[e0],$10(a1) 30 | 31 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 32 | sqv v0[e0],$20(a1) 33 | 34 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 35 | sqv v0[e0],$30(a1) 36 | 37 | li t0,0 38 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 39 | sw t0,$40(a1) 40 | li t0,0 41 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 42 | sw t0,$44(a1) 43 | li t0,0 44 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 45 | sw t0,$48(a1) 46 | 47 | break 48 | """ 49 | 50 | [[test]] 51 | name = "basic" 52 | input = [ 53 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 54 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 55 | ] 56 | 57 | [[test]] 58 | name = "negate" 59 | input = [ 60 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 61 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 62 | ] 63 | 64 | [[test]] 65 | name = "overflow" 66 | input = [ 67 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 68 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 69 | ] 70 | 71 | -------------------------------------------------------------------------------- /tests/gengolden/vmudm.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmudm.golden -------------------------------------------------------------------------------- /tests/gengolden/vmudm.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmudm.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmudm.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:res", 8 | "v128:accum_lo", 9 | "v128:accum_md", 10 | "v128:accum_hi", 11 | "u32:vco", 12 | "u32:vcc", 13 | "u32:vce", 14 | "u32:padding", 15 | ] 16 | 17 | rsp_code = """ 18 | li a0,$0 19 | li a1,$800 20 | 21 | lqv v0[e0],$00(a0) 22 | lqv v1[e0],$10(a0) 23 | 24 | vmudm v0,v1[e0] // V0*V1 25 | 26 | sqv v0[e0],$00(a1) 27 | 28 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 29 | sqv v0[e0],$10(a1) 30 | 31 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 32 | sqv v0[e0],$20(a1) 33 | 34 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 35 | sqv v0[e0],$30(a1) 36 | 37 | li t0,0 38 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 39 | sw t0,$40(a1) 40 | li t0,0 41 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 42 | sw t0,$44(a1) 43 | li t0,0 44 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 45 | sw t0,$48(a1) 46 | 47 | break 48 | """ 49 | 50 | [[test]] 51 | name = "basic" 52 | input = [ 53 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 54 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 55 | ] 56 | 57 | [[test]] 58 | name = "negate" 59 | input = [ 60 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 61 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 62 | ] 63 | 64 | [[test]] 65 | name = "overflow" 66 | input = [ 67 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 68 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 69 | ] 70 | 71 | -------------------------------------------------------------------------------- /tests/gengolden/vmudn.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmudn.golden -------------------------------------------------------------------------------- /tests/gengolden/vmudn.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmudn.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmudn.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:res", 8 | "v128:accum_lo", 9 | "v128:accum_md", 10 | "v128:accum_hi", 11 | "u32:vco", 12 | "u32:vcc", 13 | "u32:vce", 14 | "u32:padding", 15 | ] 16 | 17 | rsp_code = """ 18 | li a0,$0 19 | li a1,$800 20 | 21 | lqv v0[e0],$00(a0) 22 | lqv v1[e0],$10(a0) 23 | 24 | vmudn v0,v1[e0] // V0*V1 25 | 26 | sqv v0[e0],$00(a1) 27 | 28 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 29 | sqv v0[e0],$10(a1) 30 | 31 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 32 | sqv v0[e0],$20(a1) 33 | 34 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 35 | sqv v0[e0],$30(a1) 36 | 37 | li t0,0 38 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 39 | sw t0,$40(a1) 40 | li t0,0 41 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 42 | sw t0,$44(a1) 43 | li t0,0 44 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 45 | sw t0,$48(a1) 46 | 47 | break 48 | """ 49 | 50 | [[test]] 51 | name = "basic" 52 | input = [ 53 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 54 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 55 | ] 56 | 57 | [[test]] 58 | name = "negate" 59 | input = [ 60 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 61 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 62 | ] 63 | 64 | [[test]] 65 | name = "overflow" 66 | input = [ 67 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 68 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 69 | ] 70 | 71 | -------------------------------------------------------------------------------- /tests/gengolden/vmulf.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmulf.golden -------------------------------------------------------------------------------- /tests/gengolden/vmulf.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmulf.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmulf.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:res", 8 | "v128:accum_lo", 9 | "v128:accum_md", 10 | "v128:accum_hi", 11 | "u32:vco", 12 | "u32:vcc", 13 | "u32:vce", 14 | "u32:padding", 15 | ] 16 | 17 | rsp_code = """ 18 | li a0,$0 19 | li a1,$800 20 | 21 | lqv v0[e0],$00(a0) 22 | lqv v1[e0],$10(a0) 23 | 24 | vmulf v0,v1[e0] // V0*V1 25 | 26 | sqv v0[e0],$00(a1) 27 | 28 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 29 | sqv v0[e0],$10(a1) 30 | 31 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 32 | sqv v0[e0],$20(a1) 33 | 34 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 35 | sqv v0[e0],$30(a1) 36 | 37 | li t0,0 38 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 39 | sw t0,$40(a1) 40 | li t0,0 41 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 42 | sw t0,$44(a1) 43 | li t0,0 44 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 45 | sw t0,$48(a1) 46 | 47 | break 48 | """ 49 | 50 | [[test]] 51 | name = "basic" 52 | input = [ 53 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 54 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 55 | ] 56 | 57 | [[test]] 58 | name = "negate" 59 | input = [ 60 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 61 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 62 | ] 63 | 64 | [[test]] 65 | name = "overflow" 66 | input = [ 67 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 68 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 69 | ] 70 | 71 | -------------------------------------------------------------------------------- /tests/gengolden/vmulu.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmulu.golden -------------------------------------------------------------------------------- /tests/gengolden/vmulu.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vmulu.rsp -------------------------------------------------------------------------------- /tests/gengolden/vmulu.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | ] 5 | 6 | output_desc = [ 7 | "v128:res", 8 | "v128:accum_lo", 9 | "v128:accum_md", 10 | "v128:accum_hi", 11 | "u32:vco", 12 | "u32:vcc", 13 | "u32:vce", 14 | "u32:padding", 15 | ] 16 | 17 | rsp_code = """ 18 | li a0,$0 19 | li a1,$800 20 | 21 | lqv v0[e0],$00(a0) 22 | lqv v1[e0],$10(a0) 23 | 24 | vmulu v0,v1[e0] // V0*V1 25 | 26 | sqv v0[e0],$00(a1) 27 | 28 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 29 | sqv v0[e0],$10(a1) 30 | 31 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 32 | sqv v0[e0],$20(a1) 33 | 34 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 35 | sqv v0[e0],$30(a1) 36 | 37 | li t0,0 38 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 39 | sw t0,$40(a1) 40 | li t0,0 41 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 42 | sw t0,$44(a1) 43 | li t0,0 44 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 45 | sw t0,$48(a1) 46 | 47 | break 48 | """ 49 | 50 | [[test]] 51 | name = "basic" 52 | input = [ 53 | 0x1212_3434, 0x5656_7878, 0x9A9A_BCBC, 0xDEDE_F0F0, # v0 54 | 0xFDEC_BA98, 0x7654_3210, 0x0123_4567, 0x89AB_CDEF, # v1 55 | ] 56 | 57 | [[test]] 58 | name = "negate" 59 | input = [ 60 | 0x1234_5678, 0x89AB_CDEF, 0xFDEC_BA98, 0x8765_4321, # v0 61 | 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 62 | ] 63 | 64 | [[test]] 65 | name = "overflow" 66 | input = [ 67 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 68 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 69 | ] 70 | 71 | -------------------------------------------------------------------------------- /tests/gengolden/vne.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vne.golden -------------------------------------------------------------------------------- /tests/gengolden/vne.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vne.rsp -------------------------------------------------------------------------------- /tests/gengolden/vne.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:vcc", 6 | "u32:vce", 7 | "u32:padding", 8 | ] 9 | 10 | output_desc = [ 11 | "v128:res", 12 | "v128:accum_lo", 13 | "v128:accum_md", 14 | "v128:accum_hi", 15 | "u32:vco", 16 | "u32:vcc", 17 | "u32:vce", 18 | "u32:padding", 19 | ] 20 | 21 | rsp_code = """ 22 | li a0,$0 23 | li a1,$800 24 | 25 | vxor v2,v2 26 | lqv v0[e0],$00(a0) 27 | lqv v1[e0],$10(a0) 28 | 29 | lw t0,$20(a0) 30 | ctc2 t0,vco 31 | lw t0,$24(a0) 32 | ctc2 t0,vcc 33 | lw t0,$28(a0) 34 | ctc2 t0,vce 35 | 36 | vne v2,v0,v1 37 | 38 | sqv v2[e0],$00(a1) 39 | 40 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 41 | sqv v0[e0],$10(a1) 42 | 43 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 44 | sqv v0[e0],$20(a1) 45 | 46 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 47 | sqv v0[e0],$30(a1) 48 | 49 | li t0,0 50 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 51 | sw t0,$40(a1) 52 | li t0,0 53 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 54 | sw t0,$44(a1) 55 | li t0,0 56 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 57 | sw t0,$48(a1) 58 | 59 | break 60 | """ 61 | 62 | 63 | 64 | [[test]] 65 | name = "basic" 66 | input = [ 67 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 68 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 69 | 0x0000, # VCO 70 | 0x0000, # VCC 71 | 0x0000, # VCE 72 | 0, # dummy 73 | ] 74 | 75 | [[test]] 76 | name = "with_vco" 77 | input = [ 78 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 79 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 80 | 0xFFFF, # VCO 81 | 0x0000, # VCC 82 | 0x0000, # VCE 83 | 0, # dummy 84 | ] 85 | 86 | [[test]] 87 | name = "with_vcc" 88 | input = [ 89 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 90 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 91 | 0x0000, # VCO 92 | 0xFFFF, # VCC 93 | 0x0000, # VCE 94 | 0, # dummy 95 | ] 96 | 97 | [[test]] 98 | name = "with_vce" 99 | input = [ 100 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 101 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 102 | 0x0000, # VCO 103 | 0x0000, # VCC 104 | 0xFFFF, # VCE 105 | 0, # dummy 106 | ] 107 | 108 | [[test]] 109 | name = "with_vco_vce" 110 | input = [ 111 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 112 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 113 | 0xFFFF, # VCO 114 | 0x0000, # VCC 115 | 0xFFFF, # VCE 116 | 0, # dummy 117 | ] 118 | 119 | [[test]] 120 | name = "with_vcc_vce" 121 | input = [ 122 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 123 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 124 | 0x0000, # VCO 125 | 0xFFFF, # VCC 126 | 0xFFFF, # VCE 127 | 0, # dummy 128 | ] 129 | 130 | [[test]] 131 | name = "with_vco_vcc" 132 | input = [ 133 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 134 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 135 | 0xFFFF, # VCO 136 | 0xFFFF, # VCC 137 | 0x0000, # VCE 138 | 0, # dummy 139 | ] 140 | 141 | [[test]] 142 | name = "with_vco_vcc_vce" 143 | input = [ 144 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 145 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 146 | 0xFFFF, # VCO 147 | 0xFFFF, # VCC 148 | 0xFFFF, # VCE 149 | 0, # dummy 150 | ] 151 | 152 | [[test]] 153 | name = "with_rand1" 154 | input = [ 155 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 156 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 157 | 0xAAAA, # VCO 158 | 0xAAAA, # VCC 159 | 0xAAAA, # VCE 160 | 0, # dummy 161 | ] 162 | 163 | [[test]] 164 | name = "with_rand2" 165 | input = [ 166 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 167 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 168 | 0x5555, # VCO 169 | 0x5555, # VCC 170 | 0x5555, # VCE 171 | 0, # dummy 172 | ] 173 | 174 | [[test]] 175 | name = "with_rand3" 176 | input = [ 177 | 0x1000_2000, 0x3000_4000, 0x9000_A000, 0xB000_C000, # v0 178 | 0x1000_2001, 0x2FFF_4000, 0x9000_A001, 0xAFFF_C000, # v1 179 | 0xAAAA, # VCO 180 | 0x5555, # VCC 181 | 0xAAAA, # VCE 182 | 0, # dummy 183 | ] 184 | -------------------------------------------------------------------------------- /tests/gengolden/vrcp.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vrcp.golden -------------------------------------------------------------------------------- /tests/gengolden/vrcp.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vrcp.rsp -------------------------------------------------------------------------------- /tests/gengolden/vrcpl.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vrcpl.golden -------------------------------------------------------------------------------- /tests/gengolden/vrcpl.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vrcpl.rsp -------------------------------------------------------------------------------- /tests/gengolden/vrcpl.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | ] 4 | 5 | output_desc = [ 6 | "v128:rcp1", 7 | "v128:rcp2", 8 | ] 9 | 10 | rsp_code = """ 11 | li a0,$0 12 | li a1,$800 13 | 14 | vxor v0,v0 15 | vxor v1,v1 16 | vxor v2,v2 17 | 18 | lqv v0[e0],$00(a0) 19 | vrcp v1[e0],v0[e0] 20 | sqv v1[e0],$00(a1) 21 | sqv v2[e0],$10(a1) 22 | 23 | // Test VRCP after VRCPH 24 | vrcph v2[e0],v0[e1] 25 | vrcp v1[e1],v0[e0] 26 | 27 | // Test VRCPL after VRCPH 28 | vrcph v2[e1],v0[e2] 29 | vrcpl v1[e2],v0[e0] 30 | 31 | // Test VRCPL after VRCPL 32 | vrcpl v1[e3],v0[e0] 33 | vrcpl v1[e4],v0[e1] 34 | 35 | // Test VRCPH after VRCPH 36 | vrcph v2[e4],v0[e2] 37 | vrcph v2[e4],v0[e2] 38 | vrcpl v1[e5],v0[e0] 39 | 40 | // Test VRSQH after VRCPL 41 | // Test VRCPL after VRSQH 42 | vrsqh v2[e5],v0[e2] 43 | vrcpl v1[e6],v0[e2] 44 | vrsqh v2[e6],v0[e2] 45 | 46 | sqv v1[e0],$00(a1) 47 | sqv v2[e0],$10(a1) 48 | break 49 | """ 50 | 51 | 52 | [[test]] 53 | name = "basic" 54 | input = [ 55 | 0xA000_0010, 0x0010_0000, 0x0000_0000, 0x0000_0000, # v0 56 | ] 57 | 58 | -------------------------------------------------------------------------------- /tests/gengolden/vrsq.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vrsq.golden -------------------------------------------------------------------------------- /tests/gengolden/vrsq.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vrsq.rsp -------------------------------------------------------------------------------- /tests/gengolden/vsub.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vsub.golden -------------------------------------------------------------------------------- /tests/gengolden/vsub.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vsub.rsp -------------------------------------------------------------------------------- /tests/gengolden/vsub.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:padding", 6 | ] 7 | 8 | output_desc = [ 9 | "v128:res", 10 | "v128:accum_lo", 11 | "v128:accum_md", 12 | "v128:accum_hi", 13 | "u32:vco", 14 | "u32:vcc", 15 | "u32:vce", 16 | "u32:padding", 17 | ] 18 | 19 | rsp_code = """ 20 | li a0,$0 21 | li a1,$800 22 | 23 | lw t0,$20(a0) 24 | ctc2 t0,vco 25 | lqv v0[e0],$00(a0) 26 | lqv v1[e0],$10(a0) 27 | 28 | vxor v2,v2,v2 29 | vsub v2,v0,v1[e0] // V0-V1 30 | 31 | sqv v2[e0],$00(a1) 32 | 33 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 34 | sqv v0[e0],$10(a1) 35 | 36 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 37 | sqv v0[e0],$20(a1) 38 | 39 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 40 | sqv v0[e0],$30(a1) 41 | 42 | li t0,0 43 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 44 | sw t0,$40(a1) 45 | li t0,0 46 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 47 | sw t0,$44(a1) 48 | li t0,0 49 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 50 | sw t0,$48(a1) 51 | 52 | break 53 | """ 54 | 55 | [[test]] 56 | name = "basic" 57 | input = [ 58 | 0x0400_7000, 0x7000_9FFF, 0x0000_3333, 0xFFFF_0001, # v0 59 | 0x0300_2000, 0xF000_9FFF, 0x0000_4444, 0x0002_0001, # v1 60 | 0xAAF2, 0, # VCO 61 | ] 62 | 63 | [[test]] 64 | name = "overflow1" 65 | input = [ 66 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 67 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 68 | 0x0000, 0, # VCO 69 | ] 70 | 71 | [[test]] 72 | name = "overflow2" 73 | input = [ 74 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 75 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 76 | 0xFFFF, 0, # VCO 77 | ] 78 | 79 | [[test]] 80 | name = "overflow3" 81 | input = [ 82 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v0 83 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v1 84 | 0x0000, 0, # VCO 85 | ] 86 | 87 | [[test]] 88 | name = "overflow4" 89 | input = [ 90 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v0 91 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v1 92 | 0xFFFF, 0, # VCO 93 | ] 94 | -------------------------------------------------------------------------------- /tests/gengolden/vsubb.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vsubb.golden -------------------------------------------------------------------------------- /tests/gengolden/vsubb.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vsubb.rsp -------------------------------------------------------------------------------- /tests/gengolden/vsubb.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:padding", 6 | ] 7 | 8 | output_desc = [ 9 | "v128:res", 10 | "v128:accum_lo", 11 | "v128:accum_md", 12 | "v128:accum_hi", 13 | "u32:vco", 14 | "u32:vcc", 15 | "u32:vce", 16 | "u32:padding", 17 | ] 18 | 19 | rsp_code = """ 20 | li a0,$0 21 | li a1,$800 22 | 23 | lw t0,$20(a0) 24 | ctc2 t0,vco 25 | lqv v0[e0],$00(a0) 26 | lqv v1[e0],$10(a0) 27 | 28 | vxor v2,v2,v2 29 | vor v2,v0[e0] // make non-zero, so we check if it's modified 30 | vsubb v2,v0,v1[e0] 31 | 32 | sqv v2[e0],$00(a1) 33 | 34 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 35 | sqv v0[e0],$10(a1) 36 | 37 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 38 | sqv v0[e0],$20(a1) 39 | 40 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 41 | sqv v0[e0],$30(a1) 42 | 43 | li t0,0 44 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 45 | sw t0,$40(a1) 46 | li t0,0 47 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 48 | sw t0,$44(a1) 49 | li t0,0 50 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 51 | sw t0,$48(a1) 52 | 53 | break 54 | """ 55 | 56 | [[test]] 57 | name = "basic" 58 | input = [ 59 | 0x0400_7000, 0x7000_9FFF, 0x0000_3333, 0xFFFF_0001, # v0 60 | 0x0300_2000, 0xF000_9FFF, 0x0000_4444, 0x0002_0001, # v1 61 | 0xAAF2, 0, # VCO 62 | ] 63 | 64 | [[test]] 65 | name = "overflow1" 66 | input = [ 67 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 68 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 69 | 0x0000, 0, # VCO 70 | ] 71 | 72 | [[test]] 73 | name = "overflow2" 74 | input = [ 75 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 76 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 77 | 0xFFFF, 0, # VCO 78 | ] 79 | 80 | [[test]] 81 | name = "overflow3" 82 | input = [ 83 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v0 84 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v1 85 | 0x0000, 0, # VCO 86 | ] 87 | 88 | [[test]] 89 | name = "overflow4" 90 | input = [ 91 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v0 92 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v1 93 | 0xFFFF, 0, # VCO 94 | ] 95 | -------------------------------------------------------------------------------- /tests/gengolden/vsubc.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vsubc.golden -------------------------------------------------------------------------------- /tests/gengolden/vsubc.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vsubc.rsp -------------------------------------------------------------------------------- /tests/gengolden/vsubc.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:padding", 6 | ] 7 | 8 | output_desc = [ 9 | "v128:res", 10 | "v128:accum_lo", 11 | "v128:accum_md", 12 | "v128:accum_hi", 13 | "u32:vco", 14 | "u32:vcc", 15 | "u32:vce", 16 | "u32:padding", 17 | ] 18 | 19 | rsp_code = """ 20 | li a0,$0 21 | li a1,$800 22 | 23 | lw t0,$20(a0) 24 | ctc2 t0,vco 25 | lqv v0[e0],$00(a0) 26 | lqv v1[e0],$10(a0) 27 | 28 | vxor v2,v2,v2 29 | vsubc v2,v0,v1[e0] // V0-V1 30 | 31 | sqv v2[e0],$00(a1) 32 | 33 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 34 | sqv v0[e0],$10(a1) 35 | 36 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 37 | sqv v0[e0],$20(a1) 38 | 39 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 40 | sqv v0[e0],$30(a1) 41 | 42 | li t0,0 43 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 44 | sw t0,$40(a1) 45 | li t0,0 46 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 47 | sw t0,$44(a1) 48 | li t0,0 49 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 50 | sw t0,$48(a1) 51 | 52 | break 53 | """ 54 | 55 | [[test]] 56 | name = "basic" 57 | input = [ 58 | 0x0400_7000, 0x7000_9FFF, 0x0000_3333, 0xFFFF_0001, # v0 59 | 0x0300_2000, 0xF000_9FFF, 0x0000_4444, 0x0002_0001, # v1 60 | 0xAAF2, 0, # VCO 61 | ] 62 | 63 | [[test]] 64 | name = "overflow1" 65 | input = [ 66 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 67 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 68 | 0x0000, 0, # VCO 69 | ] 70 | 71 | [[test]] 72 | name = "overflow2" 73 | input = [ 74 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 75 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 76 | 0xFFFF, 0, # VCO 77 | ] 78 | 79 | [[test]] 80 | name = "overflow3" 81 | input = [ 82 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v0 83 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v1 84 | 0x0000, 0, # VCO 85 | ] 86 | 87 | [[test]] 88 | name = "overflow4" 89 | input = [ 90 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v0 91 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v1 92 | 0xFFFF, 0, # VCO 93 | ] 94 | -------------------------------------------------------------------------------- /tests/gengolden/vsucb.golden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vsucb.golden -------------------------------------------------------------------------------- /tests/gengolden/vsucb.rsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rasky/r64emu/5d95a7cacbcae483d1a3f4865297813843c1e26e/tests/gengolden/vsucb.rsp -------------------------------------------------------------------------------- /tests/gengolden/vsucb.toml: -------------------------------------------------------------------------------- 1 | input_desc = [ 2 | "v128:v0", 3 | "v128:v1", 4 | "u32:vco", 5 | "u32:padding", 6 | ] 7 | 8 | output_desc = [ 9 | "v128:res", 10 | "v128:accum_lo", 11 | "v128:accum_md", 12 | "v128:accum_hi", 13 | "u32:vco", 14 | "u32:vcc", 15 | "u32:vce", 16 | "u32:padding", 17 | ] 18 | 19 | rsp_code = """ 20 | li a0,$0 21 | li a1,$800 22 | 23 | lw t0,$20(a0) 24 | ctc2 t0,vco 25 | lqv v0[e0],$00(a0) 26 | lqv v1[e0],$10(a0) 27 | 28 | vxor v2,v2,v2 29 | vor v2,v0[e0] // make non-zero, so we check if it's modified 30 | vsucb v2,v0,v1[e0] 31 | 32 | sqv v2[e0],$00(a1) 33 | 34 | vsar v0,v0[e10] // VSAR E10 -> ACCUM_LO 35 | sqv v0[e0],$10(a1) 36 | 37 | vsar v0,v0[e9] // VSAR E9 -> ACCUM_MD 38 | sqv v0[e0],$20(a1) 39 | 40 | vsar v0,v0[e8] // VSAR E8 -> ACCUM_HI 41 | sqv v0[e0],$30(a1) 42 | 43 | li t0,0 44 | cfc2 t0,vco // T0 = RSP CP2 Control Register: VCO (Vector Carry Out) 45 | sw t0,$40(a1) 46 | li t0,0 47 | cfc2 t0,vcc // T0 = RSP CP2 Control Register: VCC (Vector Compare Code) 48 | sw t0,$44(a1) 49 | li t0,0 50 | cfc2 t0,vce // T0 = RSP CP2 Control Register: VCE (Vector Compare Extension) 51 | sw t0,$48(a1) 52 | 53 | break 54 | """ 55 | 56 | [[test]] 57 | name = "basic" 58 | input = [ 59 | 0x0400_7000, 0x7000_9FFF, 0x0000_3333, 0xFFFF_0001, # v0 60 | 0x0300_2000, 0xF000_9FFF, 0x0000_4444, 0x0002_0001, # v1 61 | 0xAAF2, 0, # VCO 62 | ] 63 | 64 | [[test]] 65 | name = "overflow1" 66 | input = [ 67 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 68 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 69 | 0x0000, 0, # VCO 70 | ] 71 | 72 | [[test]] 73 | name = "overflow2" 74 | input = [ 75 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v0 76 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v1 77 | 0xFFFF, 0, # VCO 78 | ] 79 | 80 | [[test]] 81 | name = "overflow3" 82 | input = [ 83 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v0 84 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v1 85 | 0x0000, 0, # VCO 86 | ] 87 | 88 | [[test]] 89 | name = "overflow4" 90 | input = [ 91 | 0x7FFF_7FFF, 0x8000_8001, 0xFFFF_FFFF, 0xFFFF_FFFF, # v0 92 | 0x7FFF_8000, 0x8000_8000, 0x8000_8000, 0x7FFF_7FFF, # v1 93 | 0xFFFF, 0, # VCO 94 | ] 95 | -------------------------------------------------------------------------------- /winsetup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script setup the Rust environment for correct Mac->Win 4 | # cross-compilation. 5 | 6 | # Install MingW compiler 7 | if !hash i686-w64-mingw32-c++ 2>/dev/null; then 8 | brew install mingw-w64 9 | fi 10 | 11 | # Install mingw target 12 | rustup target add x86_64-pc-windows-gnu 13 | 14 | # Overwrite some CRT files to prevent a linker error 15 | MINGW=$(brew --prefix mingw-w64) 16 | TOOLCHAIN=$(rustup default | cut -f 1 -d " ") 17 | echo $TOOLCHAIN 18 | cp "$MINGW/toolchain-x86_64/x86_64-w64-mingw32/lib/"{crt2.o,dllcrt2.o,libmsvcrt.a} \ 19 | ~/.rustup/toolchains/$TOOLCHAIN/lib/rustlib/x86_64-pc-windows-gnu/lib 20 | 21 | # Tell the toolchain to use the correct linker 22 | mkdir -p .cargo 23 | echo -e "[target.x86_64-pc-windows-gnu]\nlinker = 'x86_64-w64-mingw32-gcc'" > .cargo/config 24 | 25 | # Finish! 26 | echo "Rust Windows setup completed" 27 | echo "Now run: cargo build --release --target=x86_64-pc-windows-gnu" 28 | --------------------------------------------------------------------------------