diff --git a/Cargo.lock b/Cargo.lock index 0c59ed0..8c6eab3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,6 +51,11 @@ name = "bit_field" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bitfield" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "0.7.0" @@ -105,6 +110,14 @@ dependencies = [ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cmake" version = "0.1.31" @@ -757,6 +770,40 @@ name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "phf" +version = "0.7.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_macros" +version = "0.7.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pkg-config" version = "0.3.11" @@ -805,6 +852,7 @@ version = "0.1.0" dependencies = [ "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitfield 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -817,7 +865,10 @@ dependencies = [ "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "packed_simd 0.1.0 (git+https://github.com/rust-lang-nursery/packed_simd)", + "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_macros 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", "pretty-hex 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "slog-async 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "slog-term 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -843,6 +894,23 @@ dependencies = [ "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rayon" version = "1.0.2" @@ -1021,6 +1089,11 @@ dependencies = [ "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "slab" version = "0.3.0" @@ -1483,6 +1556,7 @@ dependencies = [ "checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" "checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9" "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" +"checksum bitfield 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdfb9275e899a694a3de63ef97a727c38f0be15765468c23f6e29e4bb461a46" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" @@ -1492,6 +1566,7 @@ dependencies = [ "checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d" "checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" "checksum chrono 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6962c635d530328acc53ac6a955e83093fedc91c5809dfac1fa60fa470830a37" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cmake 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "95470235c31c726d72bf2e1f421adc1e65b9d561bf5529612cbe1a72da1467b3" "checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" @@ -1564,6 +1639,10 @@ dependencies = [ "checksum openssl-sys 0.9.33 (registry+https://github.com/rust-lang/crates.io-index)" = "d8abc04833dcedef24221a91852931df2f63e3369ae003134e70aff3645775cc" "checksum packed_simd 0.1.0 (git+https://github.com/rust-lang-nursery/packed_simd)" = "" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2" +"checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" +"checksum phf_macros 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6cfe846605e58552e1324c566bfb5bd5ac032245c9ae05c859f042e81c03b5" +"checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930" "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" "checksum png 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48f397b84083c2753ba53c7b56ad023edb94512b2885ffe227c66ff7edb61868" "checksum pretty-hex 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "119929a2a3b731bb3d888f7a1b5dc3c1db28b6c134def5d99f7e16e2da16b8f7" @@ -1572,6 +1651,8 @@ dependencies = [ "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" +"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df7a791f788cb4c516f0e091301a29c2b71ef680db5e644a7d68835c8ae6dbfa" "checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" @@ -1593,6 +1674,7 @@ dependencies = [ "checksum serde 1.0.69 (registry+https://github.com/rust-lang/crates.io-index)" = "210e5a3b159c566d7527e9b22e44be73f2e0fcc330bb78fef4dbccb56d2e74c8" "checksum serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "84b8035cabe9b35878adec8ac5fe03d5f6bc97ff6edd7ccb96b44c1276ba390e" "checksum serde_urlencoded 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e703cef904312097cfceab9ce131ff6bbe09e8c964a0703345a5f49238757bc1" +"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" "checksum slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2f7bfce6405155042d42ec0e645efe43eddedd7be280063ce0623b120014e7f9" diff --git a/Cargo.toml b/Cargo.toml index ad3951c..df41242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,11 @@ crc = "^1.0.0" lazy_static = "1.0" bitflags = "1.0" bit_field = "0.9.0" +bitfield = "0.13.0" enum-map = "0.4.0" +phf = "0.7.21" +phf_macros = "0.7.22" +rand = "0.5.5" [dev-dependencies] image = "0.13" diff --git a/src/lib.rs b/src/lib.rs index 5308338..cc651ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ #![feature(attr_literals)] #![feature(stdsimd)] +#![feature(plugin)] +#![plugin(phf_macros)] +#![feature(test)] #[macro_use] extern crate slog; @@ -14,9 +17,21 @@ extern crate packed_simd; #[macro_use] extern crate bitflags; +extern crate bit_field; + +#[macro_use] +extern crate bitfield; + #[macro_use] extern crate error_chain; +extern crate phf; + +extern crate rand; + +#[cfg(test)] +extern crate test; + pub mod errors { error_chain!{ foreign_links { diff --git a/src/mips64/cp0.rs b/src/mips64/cp0.rs index ec3dbe8..079f4ab 100644 --- a/src/mips64/cp0.rs +++ b/src/mips64/cp0.rs @@ -1,21 +1,367 @@ use super::cpu::{Cop, Cop0, CpuContext, Exception}; +use super::segment::Segment; +use super::tlb; +use bit_field::BitField; +use rand::{FromEntropy, Rng, XorShiftRng}; use slog; +const EXC_LOC_COMMON: u32 = 0xBFC0_0000; +const EXC_LOC_BASE_0: u32 = 0x8000_0000; +const EXC_LOC_BASE_1: u32 = 0xBFC0_0000; +const EXC_LOC_OFF_TLB_MISS: u32 = 0x0000; +const EXC_LOC_OFF_XTLB_MISS: u32 = 0x0080; +const EXC_LOC_OFF_OTHER: u32 = 0x0180; + +// Only safe in single threaded environements! +static mut RNG: Option = None; +unsafe fn rng() -> &'static mut XorShiftRng { + if RNG.is_none() { + RNG = Some(XorShiftRng::from_entropy()); + } + + RNG.as_mut().unwrap() +} + +const CP0_REG_INDEX: usize = 0; +const CP0_REG_RANDOM: usize = 1; +const CP0_REG_ENTRY_LO0: usize = 2; +const CP0_REG_ENTRY_LO1: usize = 3; +const CP0_REG_CONTEXT: usize = 4; +const CP0_REG_PAGE_MASK: usize = 5; +const CP0_REG_WIRED: usize = 6; +const CP0_REG_BAD_VADDR: usize = 8; +const CP0_REG_COUNT: usize = 9; +const CP0_REG_ENTRY_HI: usize = 10; +const CP0_REG_COMPARE: usize = 11; +const CP0_REG_STATUS: usize = 12; +const CP0_REG_CAUSE: usize = 13; +const CP0_REG_EPC: usize = 14; +const CP0_REG_PRID: usize = 15; +const CP0_REG_CONFIG: usize = 16; +const CP0_REG_LL_ADDR: usize = 17; +const CP0_REG_WATCH_LO: usize = 18; +const CP0_REG_WATCH_HI: usize = 19; +const CP0_REG_X_CONTEXT: usize = 20; +const CP0_REG_PARITY_ERROR: usize = 26; +const CP0_REG_CACHE_ERROR: usize = 27; +const CP0_REG_TAG_LO: usize = 28; +const CP0_REG_TAG_HI: usize = 29; +const CP0_REG_ERROR_EPC: usize = 30; + +bitfield! { + #[derive(Default)] + pub struct StatusReg(u32); + impl Debug; + // Specifies and indicates global interrupt enable + // (0 - disable interrupts, 1 - enable interrupts) + ie, set_ie: 0; + + // Specifies and indicates exception level + // (0 - normal, 1 - exception) + pub exl, set_exl: 1; + + // Specifies and indicates error level + // (0 - normal, 1 - error) + pub erl, set_erl: 2; + + // Specifies and indicates mode bits + // (10 - User, 01 - Supervisor, 00 - Kernel) + pub ksu, set_ksu: 4, 3; + + // Enables 64-bit addressing and operations in User mode. When this bit is set, XTLB + // miss exception is generated on TLB misses in User mode addresses space. + // (0 - 32-bit, 1 - 64-bit) + pub ux, set_ux: 5; + + // Enables 64-bit addressing and operations in Supervisor mode. When this bit is set, XTLB + // miss exception is generated on TLB misses in Supervisor mode addresses space. + // (0 - 32-bit, 1 - 64-bit) + pub sx, set_sx: 6; + + // Enables 64-bit addressing in Kernel mode. When this bit is set, XTLB + // miss exception is generated on TLB misses in Kernel mode addresses space. + // (0 - 32-bit, 1 - 64-bit) + // 64-bit operation is always valid in Kernel mode. + pub kx, set_kx: 7; + + // Interrupt Mask field, enables external, internal, coprocessors or software interrupts. + im, set_im: 15, 8; + + // compat bit + ds_de, _: 16; + + // compat bit + ds_ce, _: 17; + + // CP0 condition bit + ds_ch, set_ds_ch: 18; + + // 0 - Indicates a Soft Reset or NMI has not occurred. + // 1 - Indicates a Soft Reset or NMI has occurred. + ds_sr, set_ds_sr: 20; + + // Indicates TLB shutdown has occurred (read-only) + ds_ts, set_ds_ts: 21; + + // Controls the location of TLB miss and general purpose exception vectors. + // 0 - normal + // 1 - bootstrap + ds_bev, set_ds_bev: 22; + + // Enables Instruction Trace Support. + ds_its, set_ds_its: 24; + + // Reverse-Endian bit, enables reverse of system endianness in User mode. + // (0 - disabled, 1 - reversed) + re, set_re: 25; + + // Enables additional floating-point registers + // (0 - 16 registers, 1 - 32 registers) + fr, set_fr: 26; + + // Enables low-power operation by reducing the internal clock frequency and the + // system interface clock frequency to one-quarter speed. + // (0 - normal, 1 - low power mode) + rp, set_rp: 27; + + // Controls the usability of each of the four coprocessor unit numbers. + // (1 - usable, 0 - unusable) + // CP0 is always usable when in Kernel mode, regardless of the setting of the + // CU0 bit. CP2 and CP3 are reserved for future expansion. + cu0, set_cu1: 28; + cu1, set_cu0: 29; + cu2, set_cu2: 30; + cu3, set_cu3: 31; +} + +impl StatusReg { + /// Returns if currently in 64bit mode. + pub fn is64(&self) -> bool { + self.ux() || self.sx() || self.kx() + } +} + +bitflags! { + struct EXC_CODE: u8 { + /// Exception Code - Interrupt + const INT = 0; + /// Exception Code - TLB Modification exception + const MOD = 1; + /// Exception Code - TLB Miss exception (load or instruction fetch) + const TLBL = 2; + /// Exception Code - TLB Miss exception (store) + const TLBS = 3; + /// Exception Code - Address Error exception (load or instruction fetch) + const ADEL = 4; + /// Exception Code - Address Error exception (store) + const ADES = 5; + /// Exception Code - Bus Error exception (instruction fetch) + const IBE = 6; + /// Exception Code - Bus Error exception (data reference: load or store) + const DBE = 7; + /// Exception Code - Syscall exception + const SYS = 8; + /// Exception Code - Breakpoint exception + const BP = 9; + /// Exception Code - Reserved Instruction exception + const RI = 10; + /// Exception Code - Coprocessor Unusable exception + const CPU = 11; + /// Exception Code - Arithmetic Overflow exception + const OV = 12; + /// Exception Code - Trap exception + const TR = 13; + /// Exception Code - Floating-Point exception + const FPE = 15; + /// Exception Code - Watch exception + const WATCH = 23; + } +} + +bitfield! { + #[derive(Default)] + pub struct CauseReg(u32); + impl Debug; + + /// Exception Code + u8, exc_code, set_exc_code: 6, 2; + /// Interrupt pending? + u8, ip, set_ip: 15, 8; + /// Coprocessor unit, only defined for coprocessor unusable exceptions. + u8, ce, set_ce: 29, 28; + /// Exception occured in branch delay slot? + bd, set_bd: 31; +} + +bitfield! { + #[derive(Default)] + pub struct ContextReg(u64); + impl Debug; + + u32, bad_vpn2, set_bad_vpn2: 22, 4; + pte_base, _: 63, 23; +} + +bitfield! { + #[derive(Default)] + pub struct XContextReg(u64); + impl Debug; + + u32, bad_vpn2, set_bad_vpn2: 30, 4; + u8, r, set_r: 32, 31; + pte_base, _: 63, 33; +} + +bitfield! { + #[derive(Default)] + pub struct EntryHiReg(u64); + impl Debug; + + u8, asid, set_asid: 8, 0; + u32, vpn2, set_vpn2: 39, 13; + u8, r, set_r: 63, 62; +} + +/// System Control Coprocessor (CP0) pub struct Cp0 { - reg_status: u64, - reg_cause: u64, + /// [tlb] #0 Index + reg_index: u32, + /// [tbl] #2 Entry Lo0 (even) + reg_entry_lo0: u64, + /// [tlb] #3 Entry Lo1 (odd) + reg_entry_lo1: u64, + /// [tlb/exception] #4 Context + /// Used in handling TLB Miss exceptions. + /// Contains PTEBase || BadVPN2 on exception || 0000 + reg_context: ContextReg, + /// [tlb] #5 Page Mask + reg_page_mask: u32, + /// [tlb] #6 Wired + /// Protects parts of the tlb from being overwritten in TLBWR. + reg_wired: u32, + /// [exception] #8 Bad Virtual Address + /// Read only. Holds the last virtual address, which errored out. + reg_bad_vaddr: u64, + /// [exception] #9 Count + /// Running counter at half the clock speed of PClock. + reg_count: u64, + /// [tlb] #10 Entry Hi + reg_entry_hi: EntryHiReg, + /// [exception] #11 Compare + /// Used for comparision with the count register to generate a timer interrupt. + reg_compare: u64, + /// [exception] #12 Status + /// Hold various status informations, see the definition of `StatusReg` for details. + reg_status: StatusReg, + /// [exception] #13 Cause + /// Hold the cause of the last exception occured + reg_cause: CauseReg, + /// [exception] #14 Exception Program Counter + /// Contains either the vaddr of the instruction that was the direct cause + /// or the vaddr of the immediately preceding branch or jump instruction + /// (if the direct cause was inside the branch delay slot). + /// This is the address where execution is resumed after processing. + reg_epc: u64, + /// Reg #15 + reg_pr_id: u64, + // Reg #16 + reg_config: u64, + // Reg #17 + reg_ll_addr: u64, + /// [exception] #18 Watch Lo + reg_watch_lo: u64, + /// [exception] #19 Watch Hi + reg_watch_hi: u64, + /// [exception] #20 XContext + /// 64bit version of Context. + reg_x_context: XContextReg, + /// [exception] #26 Parity Error + /// Unused. + reg_parity_error: u64, + /// [exception] #27 Cache Error + /// Read only. Unused. + reg_cache_error: u64, + // Reg #28 + reg_tag_lo: u64, + // Reg #29 + reg_tag_hi: u64, + /// [exception] #30 Error Exception Program Counter + /// Similar to EPC, also used to store the PC on {Cold|Soft} Reset and NMI. + reg_error_epc: u64, + tlb: tlb::Tlb, logger: slog::Logger, } impl Cp0 { pub fn new(logger: slog::Logger) -> Box { + let mut reg_status = StatusReg::default(); + // set defaults for cold reset + reg_status.set_erl(true); + reg_status.set_ds_bev(true); + Box::new(Cp0 { - reg_status: 0, - reg_cause: 0, + reg_index: 0, + reg_entry_lo0: 0, + reg_entry_lo1: 0, + reg_context: ContextReg::default(), + reg_page_mask: 0, + reg_wired: 0, + reg_bad_vaddr: 0, + reg_count: 0, + reg_entry_hi: EntryHiReg::default(), + reg_compare: 0, + reg_status, + reg_cause: CauseReg::default(), + reg_epc: 0, + reg_pr_id: 0, + reg_config: 0, + reg_ll_addr: 0, + reg_watch_lo: 0, + reg_watch_hi: 0, + reg_x_context: XContextReg::default(), + reg_parity_error: 0, + reg_cache_error: 0, + reg_tag_lo: 0, + reg_tag_hi: 0, + reg_error_epc: 0, + + tlb: tlb::Tlb::default(), logger: logger, }) } + + /// Fetches the matching memory segment for the given virtual address. + fn get_segment(&self, vaddr: u64) -> &Segment { + Segment::from_vaddr(vaddr, &self.reg_status) + } + + fn exception_setup(&mut self, ctx: &mut CpuContext, exc: Exception) { + if let Some(cause) = EXC_CODE::from_bits(exc.as_u8()) { + // These are all the exceptions that are not RESET and NMI. + + self.reg_cause.set_exc_code(cause.bits() as u8); + + if ctx.branch_pc != 0 { + // In a branch delay, use the preceeding branch as resume point. + self.reg_epc = (ctx.branch_pc - 4) as u64; + self.reg_cause.set_bd(true); + } else { + self.reg_epc = ctx.pc as u64; + } + } + } + + fn write_regs_tlb_exception(&mut self, vaddr: u64) { + let vpn2 = tlb::calc_vpn2(vaddr); + self.reg_bad_vaddr = vaddr; + self.reg_context.set_bad_vpn2(vpn2); + self.reg_entry_hi.set_vpn2(vpn2); + if self.reg_status.is64() { + self.reg_x_context.set_bad_vpn2(vpn2); + self.reg_x_context.set_r(vaddr.get_bits(31..33) as u8); + } + } } impl Cop0 for Cp0 { @@ -23,7 +369,122 @@ impl Cop0 for Cp0 { false } - fn exception(&mut self, _ctx: &mut CpuContext, _exc: Exception) {} + fn exception(&mut self, ctx: &mut CpuContext, exc: Exception) { + let next_pc: u32; + + match exc { + Exception::INT => unimplemented!("INT Exception"), + Exception::MOD => unimplemented!("TLB MOD Exception"), + Exception::TLBLMiss | Exception::TLBSMiss => { + self.exception_setup(ctx, exc); + + let base = if self.reg_status.exl() { + EXC_LOC_COMMON + } else if self.reg_status.ds_bev() { + EXC_LOC_BASE_1 + } else { + EXC_LOC_BASE_0 + }; + let offset = if self.reg_status.is64() { + // 64bit + EXC_LOC_OFF_XTLB_MISS + } else { + // 32bit + EXC_LOC_OFF_TLB_MISS + }; + + next_pc = base + offset; + } + Exception::TLBLInvalid | Exception::TLBSInvalid => { + self.exception_setup(ctx, exc); + next_pc = EXC_LOC_COMMON + EXC_LOC_OFF_OTHER; + } + Exception::ADEL => unimplemented!("ADEL Exception"), + Exception::ADES => unimplemented!("ADES Exception"), + Exception::IBE => unimplemented!("IBE Exception"), + Exception::DBE => unimplemented!("DBE Exception"), + Exception::SYS => unimplemented!("SYS Exception"), + Exception::BP => unimplemented!("BP Exception"), + Exception::RI => unimplemented!("RI Exception"), + Exception::TR => unimplemented!("TR Exception"), + Exception::FPE => unimplemented!("FPE Exception"), + Exception::WATCH => unimplemented!("WATCH Exception"), + + // Special exceptions that are not specified in the Cause register + Exception::RESET => { + next_pc = EXC_LOC_COMMON; + + self.reg_status.set_ds_ts(false); + self.reg_status.set_ds_sr(false); + self.reg_status.set_rp(false); + self.reg_status.set_erl(true); + self.reg_status.set_ds_bev(true); + + // TODO: set RegConfig BE = 1 + // TODO: set RegConfig EP(3:0) = 0 + // TODO: set RegConfig EC(2:0) = DivMode(2:0) + } + Exception::SOFTRESET => { + next_pc = if !self.reg_status.erl() { + self.reg_error_epc as u32 + } else { + EXC_LOC_COMMON + }; + + self.reg_status.set_ds_ts(false); + self.reg_status.set_rp(false); + self.reg_status.set_erl(true); + self.reg_status.set_ds_bev(true); + self.reg_status.set_ds_sr(true); + } + Exception::NMI => { + next_pc = self.reg_error_epc as u32; + + self.reg_status.set_ds_ts(false); + self.reg_status.set_erl(true); + self.reg_status.set_ds_bev(true); + self.reg_status.set_ds_sr(true); + } + } + + ctx.pc = next_pc; + } + + fn translate_addr(&mut self, vaddr: u64) -> Result { + { + let segment = self.get_segment(vaddr); + + if !segment.mapped { + return Ok((vaddr - segment.start) as u32); + } + } + + info!(self.logger, "reading tlb mapped address region"; "vaddr" => format!("{:#0x}", vaddr)); + let asid = self.reg_entry_hi.asid(); + let index = self.tlb.probe(vaddr, asid); + + if let Some(index) = index { + { + let entry = self.tlb.read(index); + let page_mask = (entry.page_mask | 0x1FFF) >> 1; + // this selects the first bit after the page mask (bit 13 for 4KB page size) + let is_odd = vaddr & (page_mask as u64 + 1) != 0; + if (!is_odd && entry.valid0()) || (is_odd && entry.valid1()) { + if is_odd { + return Ok(entry.pfn1() | (vaddr as u32 & page_mask)); + } else { + return Ok(entry.pfn0() | (vaddr as u32 & page_mask)); + } + } + } + + self.write_regs_tlb_exception(vaddr); + Err(Exception::TLBLInvalid) + } else { + self.write_regs_tlb_exception(vaddr); + Err(Exception::TLBLMiss) + } + } } struct C0op<'a> { @@ -51,22 +512,104 @@ impl<'a> C0op<'a> { fn rt32(&self) -> u32 { self.rt64() as u32 } + fn fmt(&self) -> usize { + (self.opcode & 0x1f) as usize + } } impl Cop for Cp0 { fn reg(&self, idx: usize) -> u128 { match idx { - 12 => self.reg_status as u128, - 13 => self.reg_cause as u128, - _ => unimplemented!(), + CP0_REG_INDEX => self.reg_index as u128, + CP0_REG_RANDOM => { + // The value is technicaly generated as backwords counter, + // which is decremented on every cpu cycle. + // But even cen64 uses a random number generator for this, so we should + // be good to do this too for now. + // The important part is that we generate a number in the range of + // [wired, 32). + + let val: u32 = unsafe { rng().gen_range(self.reg_wired.into(), 32) }; + val as u128 + } + CP0_REG_ENTRY_LO0 => self.reg_entry_lo0 as u128, + CP0_REG_ENTRY_LO1 => self.reg_entry_lo1 as u128, + CP0_REG_CONTEXT => self.reg_context.0 as u128, + CP0_REG_PAGE_MASK => self.reg_page_mask as u128, + CP0_REG_WIRED => self.reg_wired as u128, + CP0_REG_BAD_VADDR => self.reg_bad_vaddr as u128, + CP0_REG_COUNT => { + error!(self.logger, "(read) reg count is not yet implemented"); + 0 + } + CP0_REG_ENTRY_HI => self.reg_entry_hi.0 as u128, + CP0_REG_COMPARE => self.reg_compare as u128, + CP0_REG_STATUS => self.reg_status.0 as u128, + CP0_REG_CAUSE => self.reg_cause.0 as u128, + CP0_REG_EPC => self.reg_epc as u128, + CP0_REG_PRID => self.reg_pr_id as u128, + CP0_REG_CONFIG => self.reg_config as u128, + CP0_REG_LL_ADDR => self.reg_ll_addr as u128, + CP0_REG_WATCH_LO => { + error!(self.logger, "(read) watch lo is not yet implemented"); + self.reg_watch_lo as u128 + } + CP0_REG_WATCH_HI => { + error!(self.logger, "(read) watch hi is not yet implemented"); + self.reg_watch_hi as u128 + } + CP0_REG_X_CONTEXT => self.reg_x_context.0 as u128, + CP0_REG_PARITY_ERROR => self.reg_parity_error as u128, + CP0_REG_CACHE_ERROR => self.reg_cache_error as u128, + CP0_REG_TAG_LO => self.reg_tag_lo as u128, + CP0_REG_TAG_HI => self.reg_tag_hi as u128, + CP0_REG_ERROR_EPC => self.reg_error_epc as u128, + _ => { + warn!(self.logger, "CP0 read reg: unknown register"; "reg" => idx); + 0 + } } } fn set_reg(&mut self, idx: usize, val: u128) { match idx { - 12 => self.reg_status = val as u64, - 13 => self.reg_cause = val as u64, - _ => unimplemented!(), + CP0_REG_INDEX => self.reg_index = val as u32, + CP0_REG_RANDOM => panic!("reg random is readonly"), + CP0_REG_ENTRY_LO0 => self.reg_entry_lo0 = val as u64, + CP0_REG_ENTRY_LO1 => self.reg_entry_lo1 = val as u64, + CP0_REG_CONTEXT => self.reg_context.0 = val as u64, + CP0_REG_PAGE_MASK => self.reg_page_mask = val as u32, + CP0_REG_WIRED => self.reg_wired = val as u32, + CP0_REG_BAD_VADDR => panic!("reg bad vaddr is readonly"), + CP0_REG_COUNT => { + error!(self.logger, "(write) reg count is not yet implemented"); + self.reg_count = val as u64; + } + CP0_REG_ENTRY_HI => self.reg_entry_hi.0 = val as u64, + CP0_REG_COMPARE => self.reg_compare = val as u64, + CP0_REG_STATUS => self.reg_status.0 = val as u32, + CP0_REG_CAUSE => self.reg_cause.0 = val as u32, + CP0_REG_EPC => self.reg_epc = val as u64, + CP0_REG_PRID => self.reg_pr_id = val as u64, + CP0_REG_CONFIG => self.reg_config = val as u64, + CP0_REG_LL_ADDR => self.reg_ll_addr = val as u64, + CP0_REG_WATCH_LO => { + error!(self.logger, "(write) watch lo is not yet implemented"); + self.reg_watch_lo = val as u64; + } + CP0_REG_WATCH_HI => { + error!(self.logger, "(write) watch hi is not yet implemented"); + self.reg_watch_hi = val as u64; + } + CP0_REG_X_CONTEXT => self.reg_x_context.0 = val as u64, + CP0_REG_PARITY_ERROR => self.reg_parity_error = val as u64, + CP0_REG_CACHE_ERROR => panic!("reg cache error is readonly"), + CP0_REG_TAG_LO => self.reg_tag_lo = val as u64, + CP0_REG_TAG_HI => self.reg_tag_hi = val as u64, + CP0_REG_ERROR_EPC => self.reg_error_epc = val as u64, + _ => { + warn!(self.logger, "CP0 write reg: unknown register"; "reg" => idx); + } } } @@ -79,36 +622,83 @@ impl Cop for Cp0 { match op.func() { 0x00 => { // MFC0 - match op.rd() { - 12 => { - op.cpu.regs[op.rt()] = op.cop0.reg_status; - } - 13 => { - op.cpu.regs[op.rt()] = op.cop0.reg_cause; - } - _ => warn!( - op.cop0.logger, - "unimplemented COP0 read32"; - "reg" => op.rd() - ), - } + op.cpu.regs[op.rt()] = op.cop0.reg(op.rd()) as u64; } 0x04 => { // MTC0 - write32 - let sel = op.sel(); - match op.rd() { - 12 if sel == 0 => { - op.cop0.reg_status = op.rt64(); - op.cpu.tight_exit = true; + if op.sel() == 0 { + let rd = op.rd(); + let rt = op.rt64() as u128; + op.cop0.set_reg(rd, rt); + } else { + warn!( + op.cop0.logger, + "unimplemented COP0 write32, sel is not 0"; + "sel" => op.sel() + ); + } + } + 0x10 => { + // TLB + match op.fmt() { + 0x1 => { + // TLBR + + // TODO: this could fail, should handle this somehow + let entry = op.cop0.tlb.read((op.cop0.reg_index & 0x3F) as usize); + + // information is written into the various registers + op.cop0.reg_entry_hi.0 = entry.hi(); + op.cop0.reg_entry_lo0 = entry.lo0; + op.cop0.reg_entry_lo1 = entry.lo1; + op.cop0.reg_page_mask = entry.page_mask & 0x1FFF_E000; + } + 0x2 => { + // TLBWI + let index = (op.cop0.reg_index & 0x3F) as usize; + let entry_hi = op.cop0.reg_entry_hi.0; + + info!(op.cop0.logger, "TLBWI"; "index" => index, "entry_hi" => format!("{:#0x}", entry_hi)); + + op.cop0.tlb.write( + index, + op.cop0.reg_page_mask, + entry_hi, + op.cop0.reg_entry_lo0, + op.cop0.reg_entry_lo1, + ); } - 13 if sel == 0 => { - op.cop0.reg_cause = op.rt64(); - op.cpu.tight_exit = true; + 0x6 => { + // TLBWR + let index = op.cop0.reg(CP0_REG_RANDOM) as usize; + let entry_hi = op.cop0.reg_entry_hi.0; + + info!(op.cop0.logger, "TLBWR"; "index" => index, "entry_hi" => entry_hi); + + op.cop0.tlb.write( + index, + op.cop0.reg_page_mask, + entry_hi, + op.cop0.reg_entry_lo0, + op.cop0.reg_entry_lo1, + ); } - _ => warn!( - op.cop0.logger, - "unimplemented COP0 write32"; - "reg" => op.rd() + 0x18 => { + // ERET + info!(op.cop0.logger, "COP0 ERET"; "erl" => op.cop0.reg_status.erl(), "epc" => op.cop0.reg_epc); + + if op.cop0.reg_status.erl() { + op.cpu.set_pc(op.cop0.reg_error_epc as u32); + op.cop0.reg_status.set_erl(false); + } else { + op.cpu.set_pc(op.cop0.reg_epc as u32); + op.cop0.reg_status.set_exl(false); + } + } + _ => panic!( + "unimplemented COP0 opcode (TLB section): func={:x?} fmt={:x?}", + op.func(), + op.fmt(), ), } } diff --git a/src/mips64/cpu.rs b/src/mips64/cpu.rs index 8e6be97..d67e1d9 100644 --- a/src/mips64/cpu.rs +++ b/src/mips64/cpu.rs @@ -1,9 +1,10 @@ extern crate emu; -use self::emu::bus::be::{Bus, MemIoR}; +use self::emu::bus::be::{Bus, DevPtr, MemIoR, Reg32}; use self::emu::bus::MemInt; use self::emu::int::Numerics; use self::emu::sync; +use bit_field::BitField; use slog; use std::cell::RefCell; use std::rc::Rc; @@ -45,20 +46,55 @@ pub trait Cop { } pub enum Exception { - INT = 0x00, // Interrupt - MOD = 0x01, // TLB modification exception - TLBL = 0x02, // TLB load/fetch - TLBS = 0x03, // TLB store - ADEL = 0x04, // Address error (load/fetch) - ADES = 0x05, // Address error (store) - SYS = 0x08, // Syscall - BP = 0x09, // Breakpoint - RI = 0x0A, // Reserved instruction + INT = 0x00, // Interrupt + MOD = 0x01, // TLB modification exception + ADEL = 0x04, // Address error (load/fetch) + ADES = 0x05, // Address error (store) + IBE = 0x06, // Bus Error exception (instruction fetch) + DBE = 0x07, // Bus Error exception (data reference: load or store) + SYS = 0x08, // Syscall + BP = 0x09, // Breakpoint + RI = 0x0A, // Reserved instruction + TR = 0x0B, // Trap exception + FPE = 0x0F, // Floating point exception + WATCH = 0x17, // Watch exception + + // Sepcial codes, to signal miss vs invalid tlb exceptions + TLBLInvalid = 0xE0, // TLB Load Invalid + TLBLMiss = 0xE1, // TLB Load Miss + TLBSInvalid = 0xE2, // TLB Store Invalid + TLBSMiss = 0xE3, // TLB Store Miss // Special exceptions that are not specified in the Cause register - RESET = 0x100, - SOFTRESET = 0x101, - NMI = 0x102, + RESET = 0xF0, + SOFTRESET = 0xF1, + NMI = 0xF2, +} + +impl Exception { + pub fn as_u8(&self) -> u8 { + match self { + Exception::INT => 0x00, + Exception::MOD => 0x01, + Exception::ADEL => 0x04, + Exception::ADES => 0x05, + Exception::IBE => 0x06, + Exception::DBE => 0x07, + Exception::SYS => 0x08, + Exception::BP => 0x09, + Exception::RI => 0x0A, + Exception::TR => 0x0B, + Exception::FPE => 0x0F, + Exception::WATCH => 0x17, + Exception::TLBLInvalid => 0x02, + Exception::TLBLMiss => 0x02, + Exception::TLBSInvalid => 0x03, + Exception::TLBSMiss => 0x03, + Exception::RESET => 0xF0, + Exception::SOFTRESET => 0xF1, + Exception::NMI => 0xF2, + } + } } struct Lines { @@ -75,6 +111,9 @@ pub trait Cop0: Cop { /// Trigger the specified excepion. fn exception(&mut self, ctx: &mut CpuContext, exc: Exception); + + /// Translate a virtual address into the physical counter part. + fn translate_addr(&mut self, vaddr: u64) -> Result; } pub struct CpuContext { @@ -88,7 +127,176 @@ pub struct CpuContext { lines: Lines, } +#[derive(DeviceBE)] +pub struct RegsMI { + // (W): [6:0] init length (R): [6:0] init length + // [7] clear init mode [7] init mode + // [8] set init mode [8] ebus test mode + // [9/10] clr/set ebus test mode [9] RDRAM reg mode + // [11] clear DP interrupt + // [12] clear RDRAM reg + // [13] set RDRAM reg mode + #[reg(bank = 0, offset = 0x00, rwmask = 0x3FFF, wcb, rcb)] + init_mode: Reg32, + + // (R): [7:0] io + // [15:8] rac + // [23:16] rdp + // [31:24] rsp + #[reg(bank = 0, offset = 0x04, rwmask = 0, readonly)] + version: Reg32, + + // (R): [0] SP intr + // [1] SI intr + // [2] AI intr + // [3] VI intr + // [4] PI intr + // [5] DP intr + #[reg(bank = 0, offset = 0x08, rwmask = 0x3F, readonly)] + interrupt: Reg32, + + // (W): [0/1] clear/set SP mask (R): [0] SP intr mask + // [2/3] clear/set SI mask [1] SI intr mask + // [4/5] clear/set AI mask [2] AI intr mask + // [6/7] clear/set VI mask [3] VI intr mask + // [8/9] clear/set PI mask [4] PI intr mask + // [10/11] clear/set DP mask [5] DP intr mask + #[reg(bank = 0, offset = 0x0C, rwmask = 0xFFF, wcb, rcb)] + interrupt_mask: Reg32, +} + +impl Default for RegsMI { + fn default() -> Self { + let mi = RegsMI { + init_mode: Reg32::default(), + version: Reg32::default(), + interrupt: Reg32::default(), + interrupt_mask: Reg32::default(), + }; + + // defaults from cen64 + mi.version.set(0x01010101); + mi.init_mode.set(0x80); + + mi + } +} + +impl RegsMI { + fn cb_write_init_mode(&mut self, old: u32, new: u32) { + let mut res = old; + + // init length + res.set_bits(0..7, new.get_bits(0..7)); + + // clear init mode + if new.get_bit(7) { + res.set_bit(7, false); + } + + // set init mode + if new.get_bit(8) { + res.set_bit(7, true); + } + + // clear ebus test mode + if new.get_bit(9) { + res.set_bit(8, false); + } + + // set ebus test mode + if new.get_bit(10) { + res.set_bit(8, true); + } + + // clear DP interrupt + if new.get_bit(11) { + self.interrupt.set(*self.interrupt.get().set_bit(5, false)); + } + + // clear RDRAM reg mode + if new.get_bit(12) { + res.set_bit(9, false); + } + + // set RDRAM reg mode + if new.get_bit(13) { + res.set_bit(9, true); + } + + self.init_mode.set(res); + } + + fn cb_read_init_mode(&self, old: u32) -> u32 { + old.get_bits(0..10) + } + + fn cb_write_interrupt_mask(&mut self, old: u32, new: u32) { + let mut res = old; + + // clear SP mask + if new.get_bit(0) { + res.set_bit(0, false); + } + // set SP mask + if new.get_bit(1) { + res.set_bit(0, true); + } + + // clear SI mask + if new.get_bit(2) { + res.set_bit(1, false); + } + // set SI mask + if new.get_bit(3) { + res.set_bit(1, true); + } + + // clear AI mask + if new.get_bit(4) { + res.set_bit(2, false); + } + // set AI mask + if new.get_bit(5) { + res.set_bit(2, true); + } + + // clear VI mask + if new.get_bit(6) { + res.set_bit(3, false); + } + // set VI mask + if new.get_bit(7) { + res.set_bit(3, true); + } + + // clear PI mask + if new.get_bit(8) { + res.set_bit(3, false); + } + // set PI mask + if new.get_bit(9) { + res.set_bit(4, true); + } + + // clear DP mask + if new.get_bit(10) { + res.set_bit(5, false); + } + // set DP mask + if new.get_bit(11) { + res.set_bit(5, true); + } + } + + fn cb_read_interrupt_mask(&self, old: u32) -> u32 { + old.get_bits(0..6) + } +} + pub struct Cpu { + pub regs_mi: DevPtr, + ctx: CpuContext, cop0: Option>, @@ -282,6 +490,7 @@ impl Cpu { until: 0, last_fetch_addr: 0xFFFF_FFFF, last_fetch_mem: MemIoR::default(), + regs_mi: DevPtr::new(RegsMI::default()), }; } @@ -313,6 +522,10 @@ impl Cpu { self.exception(Exception::RESET); } + pub fn soft_rest(&mut self) { + self.exception(Exception::SOFTRESET); + } + fn exception(&mut self, exc: Exception) { if let Some(ref mut cop0) = self.cop0 { cop0.exception(&mut self.ctx, exc); @@ -323,9 +536,10 @@ impl Cpu { unimplemented!(); } - fn op(&mut self, opcode: u32) { + fn op(&mut self, opcode: u32) -> Result<(), Exception> { self.ctx.clock += 1; let mut op = Mipsop { opcode, cpu: self }; + match op.op() { // SPECIAL 0x00 => match op.special() { @@ -462,66 +676,140 @@ impl Cpu { 0x18 => check_overflow_add!(op, *op.mrt64(), op.irs64(), op.sximm64()), // DADDI 0x19 => *op.mrt64() = (op.irs64() + op.sximm64()) as u64, // DADDIU - 0x20 => *op.mrt64() = op.cpu.read::(op.ea()).sx64(), // LB - 0x21 => *op.mrt64() = op.cpu.read::(op.ea()).sx64(), // LH - 0x22 => *op.mrt64() = op.cpu.lwl(op.ea(), op.rt32()).sx64(), // LWL - 0x23 => *op.mrt64() = op.cpu.read::(op.ea()).sx64(), // LW - 0x24 => *op.mrt64() = op.cpu.read::(op.ea()) as u64, // LBU - 0x25 => *op.mrt64() = op.cpu.read::(op.ea()) as u64, // LHU - 0x26 => *op.mrt64() = op.cpu.lwr(op.ea(), op.rt32()).sx64(), // LWR - 0x27 => *op.mrt64() = op.cpu.read::(op.ea()) as u64, // LWU - 0x28 => op.cpu.write::(op.ea(), op.rt32() as u8), // SB - 0x29 => op.cpu.write::(op.ea(), op.rt32() as u16), // SH - 0x2A => op.cpu.write::(op.ea(), op.cpu.swl(op.ea(), op.rt32())), // SWL - 0x2B => op.cpu.write::(op.ea(), op.rt32()), // SW - 0x2E => op.cpu.write::(op.ea(), op.cpu.swr(op.ea(), op.rt32())), // SWR - 0x2F => {} // CACHE + 0x20 => { + // LB + let ea = op.ea(); + *op.mrt64() = op.cpu.read::(ea)?.sx64() + } + 0x21 => { + // LH + let ea = op.ea(); + *op.mrt64() = op.cpu.read::(ea)?.sx64() + } + 0x22 => { + // LWL + let ea = op.ea(); + let rt = op.rt32(); + *op.mrt64() = op.cpu.lwl(ea, rt)?.sx64() + } + 0x23 => { + // LW + let ea = op.ea(); + *op.mrt64() = op.cpu.read::(ea)?.sx64() + } + 0x24 => { + // LBU + let ea = op.ea(); + *op.mrt64() = op.cpu.read::(ea)? as u64 + } + 0x25 => { + // LHU + let ea = op.ea(); + *op.mrt64() = op.cpu.read::(ea)? as u64 + } + 0x26 => { + // LWR + let ea = op.ea(); + let rt = op.rt32(); + *op.mrt64() = op.cpu.lwr(ea, rt)?.sx64() + } + 0x27 => { + let ea = op.ea(); + *op.mrt64() = op.cpu.read::(ea)? as u64 + } + 0x28 => { + // SB + let ea = op.ea(); + let rt = op.rt32() as u8; + op.cpu.write::(ea, rt)? + } + 0x29 => { + // SH + let ea = op.ea(); + let rt = op.rt32() as u16; + op.cpu.write::(ea, rt)? + } + 0x2A => { + // SWL + let ea = op.ea(); + let rt = op.rt32(); + let res = op.cpu.swl(ea, rt)?; + op.cpu.write::(ea, res)?; + } + 0x2B => { + // SW + let ea = op.ea(); + let rt = op.rt32(); + op.cpu.write::(ea, rt)?; + } + 0x2E => { + // SWR + let ea = op.ea(); + let rt = op.rt32(); + let res = op.cpu.swr(ea, rt); + op.cpu.write::(ea, rt)?; + } + 0x2F => {} // CACHE 0x31 => if_cop!(op, cop1, cop1.lwc(op.opcode, &op.cpu.ctx, &op.cpu.bus)), // LWC1 0x32 => if_cop!(op, cop2, cop2.lwc(op.opcode, &op.cpu.ctx, &op.cpu.bus)), // LWC2 0x35 => if_cop!(op, cop1, cop1.ldc(op.opcode, &op.cpu.ctx, &op.cpu.bus)), // LDC1 0x36 => if_cop!(op, cop2, cop2.ldc(op.opcode, &op.cpu.ctx, &op.cpu.bus)), // LDC2 - 0x37 => *op.mrt64() = op.cpu.read::(op.ea()), // LD + 0x37 => { + // LD + let ea = op.ea(); + *op.mrt64() = op.cpu.read::(ea)? + } 0x39 => if_cop!(op, cop1, cop1.swc(op.opcode, &op.cpu.ctx, &op.cpu.bus)), // SWC1 0x3A => if_cop!(op, cop2, cop2.swc(op.opcode, &op.cpu.ctx, &op.cpu.bus)), // SWC2 0x3D => if_cop!(op, cop1, cop1.sdc(op.opcode, &op.cpu.ctx, &op.cpu.bus)), // SDC1 0x3E => if_cop!(op, cop2, cop2.sdc(op.opcode, &op.cpu.ctx, &op.cpu.bus)), // SDC2 - 0x3F => op.cpu.write::(op.ea(), op.rt64()), // SD + 0x3F => { + // SD + let ea = op.ea(); + let rt = op.rt64(); + op.cpu.write::(ea, rt)? + } _ => panic!( - "unimplemented opcode: func=0x{:x?}, pc={}", + "unimplemented opcode: func=0x{:x?}, {:#b}, pc={} {:x?} {:#034b}", + op.op(), op.op(), - op.cpu.ctx.pc.hex() + op.cpu.ctx.pc.hex(), + op.opcode, + op.opcode, ), } + + Ok(()) } - fn lwl(&self, addr: u32, reg: u32) -> u32 { - let mem = self.read::(addr); + fn lwl(&mut self, addr: u32, reg: u32) -> Result { + let mem = self.read::(addr)?; let shift = (addr & 3) * 8; let mask = (1 << shift) - 1; - (reg & mask) | ((mem << shift) & !mask) + Ok((reg & mask) | ((mem << shift) & !mask)) } - fn lwr(&self, addr: u32, reg: u32) -> u32 { - let mem = self.read::(addr); + fn lwr(&mut self, addr: u32, reg: u32) -> Result { + let mem = self.read::(addr)?; let shift = (!addr & 3) * 8; let mask = ((1u64 << (32 - shift)) - 1) as u32; - (reg & !mask) | ((mem >> shift) & mask) + Ok((reg & !mask) | ((mem >> shift) & mask)) } - fn swl(&self, addr: u32, reg: u32) -> u32 { - let mem = self.read::(addr); + fn swl(&mut self, addr: u32, reg: u32) -> Result { + let mem = self.read::(addr)?; let shift = (addr & 3) * 8; let mask = ((1u64 << (32 - shift)) - 1) as u32; - (mem & !mask) | ((reg >> shift) & mask) + Ok((mem & !mask) | ((reg >> shift) & mask)) } - fn swr(&self, addr: u32, reg: u32) -> u32 { - let mem = self.read::(addr); + fn swr(&mut self, addr: u32, reg: u32) -> Result { + let mem = self.read::(addr)?; let shift = (!addr & 3) * 8; let mask = (1 << shift) - 1; - (mem & mask) | ((reg << shift) & !mask) + Ok((mem & mask) | ((reg << shift) & !mask)) } fn fetch(&mut self, addr: u32) -> &MemIoR { @@ -533,16 +821,23 @@ impl Cpu { &self.last_fetch_mem } - fn read(&self, addr: u32) -> U { - self.bus - .borrow() - .read::(addr & 0x1FFF_FFFF & !(U::SIZE as u32 - 1)) + fn read(&mut self, vaddr: u32) -> Result { + let paddr = self.translate_addr(vaddr as i32 as u64)? & !(U::SIZE as u32 - 1); + Ok(self.bus.borrow().read::(paddr)) } - fn write(&self, addr: u32, val: U) { - self.bus - .borrow() - .write::(addr & 0x1FFF_FFFF & !(U::SIZE as u32 - 1), val); + fn write(&mut self, vaddr: u32, val: U) -> Result<(), Exception> { + let paddr = self.translate_addr(vaddr as i32 as u64)? & !(U::SIZE as u32 - 1); + self.bus.borrow().write::(paddr, val); + Ok(()) + } + + fn translate_addr(&mut self, vaddr: u64) -> Result { + if let Some(ref mut cop0) = self.cop0 { + cop0.translate_addr(vaddr) + } else { + panic!("missing COP0"); + } } pub fn run(&mut self, until: i64) { @@ -567,7 +862,10 @@ impl Cpu { self.ctx.tight_exit = false; while let Some(op) = iter.next() { self.ctx.pc += 4; - self.op(op); + match self.op(op) { + Ok(_) => {} + Err(exc) => self.exception(exc), + } if self.ctx.clock >= self.until || self.ctx.tight_exit { break; } @@ -578,7 +876,10 @@ impl Cpu { let op = iter.next().unwrap_or_else(|| self.fetch(pc).read()); self.ctx.pc = self.ctx.branch_pc; self.ctx.branch_pc = 0; - self.op(op); + match self.op(op) { + Ok(_) => {} + Err(exc) => self.exception(exc), + } } } } @@ -677,3 +978,54 @@ impl sync::Subsystem for Box { self.ctx.clock } } + +#[cfg(test)] +mod tests { + use super::emu::bus::{Bus, DevPtr}; + use super::slog; + use super::slog::Drain; + use super::*; + use bit_field::BitField; + extern crate slog_term; + use std; + + fn logger() -> slog::Logger { + let decorator = slog_term::PlainSyncDecorator::new(std::io::stdout()); + let drain = slog_term::FullFormat::new(decorator).build().fuse(); + slog::Logger::root(drain, o!()) + } + + #[test] + fn test_regs_mi() { + let mut bus = Bus::new(logger()); + let mi = DevPtr::new(RegsMI::default()); + + bus.map_device(0x00, &mi, 0).unwrap(); + + // setting everything to 0 + bus.write::(0x00, 0x00); + assert_eq!(bus.read::(0x00), 0); + + // setting init mode + let val = *0u32.set_bit(8, true); + bus.write::(0x00, val); + assert_eq!(bus.read::(0x00).get_bit(7), true); + + // clear init mode + bus.write::(0x00, *0u32.set_bit(7, true)); + assert_eq!(bus.read::(0x00).get_bit(7), false); + + // setting rdram reg mode + let val = *0u32.set_bit(13, true); + bus.write::(0x00, val); + assert_eq!(bus.read::(0x00).get_bit(9), true); + + // clear rdram reg mode + bus.write::(0x00, *0u32.set_bit(12, true)); + assert_eq!(bus.read::(0x00).get_bit(9), false); + + // write init mode + bus.write::(0x00, *0u32.set_bits(0..7, 0xF)); + assert_eq!(bus.read::(0x00).get_bits(0..7), 0xF); + } +} diff --git a/src/mips64/fpu.rs b/src/mips64/fpu.rs index 231b3b3..dfeb64d 100644 --- a/src/mips64/fpu.rs +++ b/src/mips64/fpu.rs @@ -216,6 +216,7 @@ impl Cop for Fpu { let fmt = (opcode >> 21) & 0x1F; match fmt { 8 => { + // TODO: what fmt version is this? let tgt = cpu.pc + ((opcode & 0xffff) as i16 as i32 as u32) * 4; let cc = ((opcode >> 18) & 3) as usize; let nd = opcode & (1 << 17) != 0; @@ -225,7 +226,10 @@ impl Cop for Fpu { } 16 => self.fop::(cpu, opcode), 17 => self.fop::(cpu, opcode), - _ => panic!("unimplemented COP1 fmt: fmt={:x?}", fmt), + 20 => panic!("unimplemented COP1 fmt: 20 => W"), + 21 => panic!("unimplemented COP1 fmt: 21 => L"), + 18 | 19 | 22...31 => warn!(self.logger, "reserved COP1 fmt:"; "fmt" => fmt), + _ => warn!(self.logger, "unknown COP1 fmt:"; "fmt" => fmt), } } } diff --git a/src/mips64/mod.rs b/src/mips64/mod.rs index ada6fe3..561bf47 100644 --- a/src/mips64/mod.rs +++ b/src/mips64/mod.rs @@ -3,6 +3,8 @@ extern crate num; mod cp0; mod cpu; mod fpu; +mod segment; +mod tlb; pub use self::cp0::Cp0; pub use self::cpu::{Cop, Cop0, Cpu, CpuContext, Exception}; diff --git a/src/mips64/segment.rs b/src/mips64/segment.rs new file mode 100644 index 0000000..4ffeea0 --- /dev/null +++ b/src/mips64/segment.rs @@ -0,0 +1,321 @@ +use super::cp0::StatusReg; +use bit_field::BitField; +use phf; + +macro_rules! seg { + ($name:ident, $start:expr, $length:expr, $cached:expr, $mapped:expr) => { + const $name: Segment = Segment { + start: $start, + length: $length, + cached: $cached, + mapped: $mapped, + }; + }; +} + +pub struct Segment { + pub start: u64, + pub length: u64, + + /// Is the memory cached? + pub cached: bool, + /// Is this segement TLB mapped? + pub mapped: bool, +} + +// User Mode + +seg!( + USEG, + 0x0000_0000_0000_0000, + 0x0000_0000_8000_0000, + true, + true +); +seg!( + XUSEG, + 0x0000_0000_0000_0000, + 0x0000_0100_0000_0000, + true, + true +); + +// Supervisor Mode +seg!( + SUSEG, + 0x0000_0000_0000_0000, + 0x0000_0000_8000_0000, + true, + true +); +seg!( + SSEG, + 0xFFFF_FFFF_C000_0000, + 0x0000_0000_2000_0000, + true, + true +); + +seg!( + XSUSEG, + 0x0000_0000_0000_0000, + 0x0000_0100_0000_0000, + true, + true +); +seg!( + XSSEG, + 0x4000_0000_0000_0000, + 0x0000_0100_0000_0000, + true, + true +); +seg!( + CSSEG, + 0xFFFF_FFFF_C000_0000, + 0x0000_0000_1FFF_FFFF, + true, + true +); + +static SEGMENTS: phf::OrderedMap<[u8; 4], &Segment> = phf_ordered_map! { + // -- User + // 32bit + [0b10, 0, 0, 0] => &USEG, + // 64bit + [0b10, 1, 0, 0] => &XUSEG, + + // -- Supervisor + // 32bit + [0b01, 0, 0, 0] => &SUSEG, + [0b01, 0, 1, 0] => &SSEG, + // 64bit + [0b01, 1, 0b00, 0] => &XSUSEG, + [0b01, 1, 0b01, 0] => &XSSEG, + [0b01, 1, 0b11, 0] => &CSSEG, + + // -- Kernel + // 32bit + [0b00, 0, 0b000, 0] => &KUSEG, + [0b00, 0, 0b001, 0] => &KUSEG, + [0b00, 0, 0b010, 0] => &KUSEG, + [0b00, 0, 0b011, 0] => &KUSEG, + [0b00, 0, 0b100, 0] => &KSEG0, + [0b00, 0, 0b101, 0] => &KSEG1, + [0b00, 0, 0b110, 0] => &KSSEG, + [0b00, 0, 0b111, 0] => &KSEG3, + + // 64bit + [0b00, 1, 0b00, 0] => &XKUSEG, + [0b00, 1, 0b01, 0] => &XKSSEG, + [0b00, 1, 0b10, 0] => &XKPHYS0, + [0b00, 1, 0b10, 1] => &XKPHYS1, + [0b00, 1, 0b10, 2] => &XKPHYS2, + [0b00, 1, 0b10, 3] => &XKPHYS3, + [0b00, 1, 0b10, 4] => &XKPHYS4, + [0b00, 1, 0b10, 5] => &XKPHYS5, + [0b00, 1, 0b10, 6] => &XKPHYS6, + [0b00, 1, 0b10, 7] => &XKPHYS7, + [0b00, 1, 0b11, 4] => &XKSEG, + [0b00, 1, 0b11, 0] => &CKSEG0, + [0b00, 1, 0b11, 1] => &CKSEG1, + [0b00, 1, 0b11, 2] => &CKSSEG, + [0b00, 1, 0b11, 3] => &CKSEG3, +}; + +// Placeholder, in arrays +seg!(NULL, 0, 0, false, false); + +// Kernel Mode +seg!( + KUSEG, + 0x0000_0000_0000_0000, + 0x0000_0000_8000_0000, + true, + true +); +seg!( + KSEG0, + 0xFFFF_FFFF_8000_0000, + 0x0000_0000_2000_0000, + true, + false +); +seg!( + KSEG1, + 0xFFFF_FFFF_A000_0000, + 0x0000_0000_2000_0000, + true, + false +); +seg!( + KSSEG, + 0xFFFF_FFFF_C000_0000, + 0x0000_0000_2000_0000, + true, + true +); +seg!( + KSEG3, + 0xFFFF_FFFF_E000_0000, + 0x0000_0000_2000_0000, + true, + true +); + +seg!( + XKUSEG, + 0x0000_0000_0000_0000, + 0x0000_0100_0000_0000, + true, + true +); +seg!( + XKSSEG, + 0x4000_0000_0000_0000, + 0x0000_0100_0000_0000, + true, + true +); +seg!( + XKPHYS0, + 0x8000_0000_0000_0000, + 0x0000_0001_0000_0000, + true, + false +); +seg!( + XKPHYS1, + 0x8800_0000_0000_0000, + 0x0000_0001_0000_0000, + true, + false +); +seg!( + XKPHYS2, + 0x9000_0000_0000_0000, + 0x0000_0001_0000_0000, + false, + false +); +seg!( + XKPHYS3, + 0x9800_0000_0000_0000, + 0x0000_0001_0000_0000, + true, + false +); +seg!( + XKPHYS4, + 0xA000_0000_0000_0000, + 0x0000_0001_0000_0000, + true, + false +); +seg!( + XKPHYS5, + 0xA800_0000_0000_0000, + 0x0000_0001_0000_0000, + true, + false +); +seg!( + XKPHYS6, + 0xB000_0000_0000_0000, + 0x0000_0001_0000_0000, + true, + false +); +seg!( + XKPHYS7, + 0xB800_0000_0000_0000, + 0x0000_0001_0000_0000, + true, + false +); +seg!( + XKSEG, + 0xC000_0000_0000_0000, + 0x0000_0100_0000_0000, + true, + true +); +seg!( + CKSEG0, + 0xFFFF_FFFF_8000_0000, + 0x0000_0000_2000_0000, + true, + false +); +seg!( + CKSEG1, + 0xFFFF_FFFF_A000_0000, + 0x0000_0000_2000_0000, + true, + false +); +seg!( + CKSSEG, + 0xFFFF_FFFF_C000_0000, + 0x0000_0000_2000_0000, + true, // cache depends on C bit in the TLB entry + true +); +seg!( + CKSEG3, + 0xFFFF_FFFF_E000_0000, + 0x0000_0000_2000_0000, + true, // cache depends on C bit in the TLB entry + true +); + +impl Segment { + #[inline(never)] + pub fn from_vaddr(vaddr: u64, status: &StatusReg) -> &Segment { + let ksu = status.ksu() as u8; + let exl = status.exl(); + let erl = status.erl(); + // let user_mode = ksu == 0b10 && !exl && !erl; + let supervisor_mode = ksu == 0b01 && !exl && !erl; + let kernel_mode = ksu == 0b00 || exl || erl; + let use64 = status.ux() | status.sx() | status.kx(); + + // TODO: make the below stuff simpler & faster + let info = if kernel_mode { + if use64 { + let b1 = vaddr.get_bits(62..64) as u8; + let b2 = if b1 == 0b11 { + if vaddr < CKSEG0.start { + 4u8 + } else if vaddr < CKSEG1.start { + 0u8 + } else if vaddr < CKSSEG.start { + 1u8 + } else if vaddr < CKSEG3.start { + 2u8 + } else { + 3u8 + } + } else { + vaddr.get_bits(59..62) as u8 + }; + + (b1, b2) + } else { + (vaddr.get_bits(29..32) as u8, 0u8) + } + } else if supervisor_mode { + if use64 { + (vaddr.get_bits(62..64) as u8, 0u8) + } else { + (vaddr.get_bit(32) as u8, 0u8) + } + } else { + (0u8, 0u8) + }; + + SEGMENTS + .get(&[ksu, use64 as u8, info.0, info.1]) + .expect("invalid address access") + } +} diff --git a/src/mips64/tlb.rs b/src/mips64/tlb.rs new file mode 100644 index 0000000..a4ea8ca --- /dev/null +++ b/src/mips64/tlb.rs @@ -0,0 +1,225 @@ +use bit_field::BitField; + +// Available Page Masks +const PAGE_MASK_4_KB: u32 = 0b0000_0000_0000_0000_0000_0000; +const PAGE_MASK_16_KB: u32 = 0b0000_0000_0011_0000_0000_0000; +const PAGE_MASK_64_KB: u32 = 0b0000_0000_1111_0000_0000_0000; +const PAGE_MASK_256_KB: u32 = 0b0000_0011_1111_0000_0000_0000; +const PAGE_MASK_1_MB: u32 = 0b0000_1111_1111_0000_0000_0000; +const PAGE_MASK_4_MB: u32 = 0b0011_1111_1111_0000_0000_0000; +const PAGE_MASK_16_MB: u32 = 0b1111_1111_1111_0000_0000_0000; + +#[derive(Debug, Clone, Default)] +pub struct TlbEntry { + /// Page Mask + pub page_mask: u32, + /// Virtual Page Number + pub vpn2: u32, + /// Address Space Identifier + pub asid: u8, + /// Global flag + pub global: bool, + /// EntryLo0 + pub lo0: u64, + /// EntryLo1 + pub lo1: u64, +} + +impl TlbEntry { + /// Returns the the entry_hi for this entry. + #[inline] + pub fn hi(&self) -> u64 { + ((self.vpn2 & 0x1800_0000) as u64) << 35 + | ((self.vpn2 & 0x07FF_FFFF) as u64) << 13 + | (self.global as u64) << 12 + | self.asid as u64 + } + + /// Physical Page Number (0) + #[inline] + pub fn pfn0(&self) -> u32 { + ((self.lo0 << 6) & 0xFFFF_F000) as u32 + } + + /// Physical Page Number (1) + #[inline] + pub fn pfn1(&self) -> u32 { + ((self.lo1 << 6) & 0xFFFF_F000) as u32 + } + + #[inline] + pub fn dirty0(&self) -> bool { + self.lo0.get_bit(2) + } + + #[inline] + pub fn dirty1(&self) -> bool { + self.lo1.get_bit(2) + } + + #[inline] + pub fn valid(&self) -> bool { + self.valid0() || self.valid1() + } + + #[inline] + pub fn valid0(&self) -> bool { + self.lo0.get_bit(1) + } + + #[inline] + pub fn valid1(&self) -> bool { + self.lo1.get_bit(1) + } +} + +#[derive(Debug)] +pub struct Tlb(Box<[TlbEntry]>); + +impl Default for Tlb { + fn default() -> Self { + Tlb(vec![TlbEntry::default(); 32].into_boxed_slice()) + } +} + +impl Tlb { + /// Probes for a matching entry and returns the index if a match is found. + /// None if no match is found. + pub fn probe(&self, vaddr: u64, vasid: u8) -> Option { + let vpn2 = calc_vpn2(vaddr); + + for (i, entry) in self.0.iter().enumerate() { + let asid_match = entry.global || entry.asid == vasid; + let vpn_match = entry.vpn2 == vpn2 & !(entry.page_mask >> 13); + + if asid_match && vpn_match { + return Some(i); + } + } + + None + } + + /// Reads a specific TLB index. + pub fn read(&self, index: usize) -> &TlbEntry { + &self.0[index] + } + + /// Writes an entry at the specified index to the TLB. + pub fn write( + &mut self, + index: usize, + page_mask: u32, + entry_hi: u64, + entry_lo0: u64, + entry_lo1: u64, + ) { + let entry = &mut self.0[index]; + + entry.page_mask = page_mask; + entry.vpn2 = calc_vpn2(entry_hi); + + entry.asid = entry_hi as u8; + entry.global = entry_lo0.get_bit(0) && entry_lo1.get_bit(0); + + entry.lo0 = entry_lo0; + entry.lo1 = entry_lo1; + } +} + +pub fn calc_vpn2(addr: u64) -> u32 { + (addr >> 35 & 0x1800_0000) as u32 | (addr >> 13 & 0x07FF_FFFF) as u32 +} + +#[cfg(test)] +mod tests { + use super::*; + use test::Bencher; + + #[test] + fn test_tlb() { + let mut tlb = Tlb::default(); + + tlb.write( + 1, + PAGE_MASK_4_KB, + 0b1110_0000_0000_0000_0000_0000_1111_0000_1001_1001, + 0b0000_0000_0000_0000_1000_0000_0000_0010, // valid, not global + 0b0000_0000_0000_0000_0100_0000_0000_0010, // valid, not global + ); + tlb.write( + 2, + PAGE_MASK_4_KB, + 0b1111_0000_0000_0000_0000_0000_1111_0000_1001_1001, + 0b0000_0000_0000_0000_1000_0000_0000_0011, // global and valid + 0b0000_0000_0000_0000_0100_0000_0000_0011, // global and valid + ); + + let entry = tlb.read(1); + assert!(!entry.global); + assert!(entry.valid()); + assert_eq!(entry.asid, 0b1001_1001); + assert_eq!(entry.pfn0(), 0b0000_0010_0000_0000_0000_0000_0000); + assert_eq!(entry.pfn1(), 0b0000_0001_0000_0000_0000_0000_0000); + assert_eq!(entry.vpn2, 0b0111_0000_0000_0000_0000_0000_0111); + + assert_eq!( + entry.hi(), + 0b1110_0000_0000_0000_0000_0000_1110_0000_1001_1001, + ); + + assert_eq!( + tlb.probe( + 0b0000_1110_0000_0000_0000_0000_0000_1111_0000_0000_0001, + 0b1001_1001, + ), + Some(1) + ); + // non matching asid + assert_eq!( + tlb.probe( + 0b0000_1110_0000_0000_0000_0000_0000_1111_0000_0000_0001, + 0b1001_1000, + ), + None + ); + // non matching vpn2 + assert_eq!( + tlb.probe( + 0b0000_1110_0000_0000_0000_0000_0000_0111_0000_0000_0001, + 0b1001_1001, + ), + None + ); + + // non matching asid - with global + assert_eq!( + tlb.probe( + 0b0000_1111_0000_0000_0000_0000_0000_1111_0000_0000_0001, + 0b1001_1000, + ), + Some(2) + ); + } + + #[bench] + fn bench_tlb_probe_match(b: &mut Bencher) { + let mut tlb = Tlb::default(); + + tlb.write( + 13, + PAGE_MASK_4_KB, + 0b1110_0000_0000_0000_0000_0000_1111_0000_1001_1001, + 0b0000_0000_0000_0000_1000_0000_0000_0010, // valid, not global + 0b0000_0000_0000_0000_0100_0000_0000_0010, // valid, not global + ); + + // Current baseline 13 ns/iter + b.iter(|| { + tlb.probe( + 0b0000_1110_0000_0000_0000_0000_0000_1111_0000_0000_0001, + 0b1001_1001, + ) + }); + } +} diff --git a/src/n64.rs b/src/n64.rs index 1bc265f..9d988a9 100644 --- a/src/n64.rs +++ b/src/n64.rs @@ -70,6 +70,7 @@ impl N64 { bus.map_device(0x0404_0000, &sp, 1)?; bus.map_device(0x0408_0000, &sp, 2)?; bus.map_device(0x0410_0000, &dp, 0)?; + bus.map_device(0x0430_0000, &cpu.borrow().regs_mi, 0)?; bus.map_device(0x0440_0000, &vi, 0)?; bus.map_device(0x0450_0000, &ai, 0)?; bus.map_device(0x0460_0000, &pi, 0)?; @@ -79,6 +80,8 @@ impl N64 { bus.map_device(0x1FC0_0000, &pi, 1)?; } + // 0x0020_5005 -> RDRAM range 2 + // 0x000E_0204 -> RDRAM range 1, written to const MAIN_CLOCK: i64 = 187488000; // TODO: guessed let mut sync = sync::Sync::new(sync::Config { diff --git a/src/pi.rs b/src/pi.rs index f7cc9d1..eee37e3 100644 --- a/src/pi.rs +++ b/src/pi.rs @@ -1,5 +1,6 @@ extern crate emu; extern crate slog; +use bit_field::BitField; use emu::bus::be::{Bus, Mem, MemFlags, Reg32}; use emu::int::Numerics; use errors::*; @@ -37,8 +38,8 @@ pub struct Pi { // (R) [0] DMA busy (W): [0] reset controller // [1] IO busy (and abort current op) - // [2] error [1] clear intr - #[reg(bank = 0, offset = 0x10, rwmask = 0, wcb)] + // [2] error [1] clear intr + #[reg(bank = 0, offset = 0x10, rwmask = 0, wcb, rcb)] dma_status: Reg32, // [7:0] domain 1 device latency @@ -82,7 +83,7 @@ impl Pi { let mut contents = vec![]; File::open(pifrom)?.read_to_end(&mut contents)?; - Ok(Pi { + let pi = Pi { logger, bus, rom: Mem::from_buffer(contents, MemFlags::READACCESS), @@ -101,7 +102,12 @@ impl Pi { dom2_pulse_width: Reg32::default(), dom2_page_size: Reg32::default(), dom2_release: Reg32::default(), - }) + }; + + // TODO: is this correct? + pi.dma_status.set(0x3); + + Ok(pi) } fn cb_read_magic(&self, val: u32) -> u32 { @@ -116,17 +122,31 @@ impl Pi { } } - fn cb_write_dma_status(&mut self, _old: u32, new: u32) { - info!(self.logger, "write dma status"; o!("val" => format!("{:x}", new))); + fn cb_write_dma_status(&mut self, old: u32, new: u32) { + // reset PI + if new.get_bit(0) { + self.dma_status.set(0) + } + + // clear interrupt + if new.get_bit(1) { + // TODO: + warn!(self.logger, "unimplemented: clear PI interrupt"); + } + } + + fn cb_read_dma_status(&self, val: u32) -> u32 { + val.get_bits(0..3) } fn cb_write_dma_wr_len(&mut self, _old: u32, val: u32) { let mut raddr = self.dma_rom_addr.get(); let mut waddr = self.dma_ram_addr.get(); - info!(self.logger, "DMA xfer"; o!( - "src" => raddr.hex(), - "dst" => waddr.hex(), - "len" => val+1)); + info!(self.logger, "DMA xfer (write)"; + "src" => raddr.hex(), + "dst" => waddr.hex(), + "len" => val+1 + ); let bus = self.bus.borrow(); let mut i = 0; @@ -138,9 +158,30 @@ impl Pi { } self.dma_rom_addr.set(raddr); self.dma_ram_addr.set(waddr); + + // TODO: signal rcp interrupt } - fn cb_write_dma_rd_len(&mut self, _old: u32, _new: u32) { - unimplemented!() + fn cb_write_dma_rd_len(&mut self, _old: u32, val: u32) { + let mut raddr = self.dma_ram_addr.get(); + let mut waddr = self.dma_rom_addr.get(); + info!(self.logger, "DMA xfer (read)"; + "src" => raddr.hex(), + "dst" => waddr.hex(), + "len" => val+1 + ); + + let bus = self.bus.borrow(); + let mut i = 0; + while i < val + 1 { + bus.write::(waddr, bus.read::(raddr)); + raddr = raddr + 4; + waddr = waddr + 4; + i += 4; + } + self.dma_rom_addr.set(raddr); + self.dma_ram_addr.set(waddr); + + // TODO: signal rcp interrupt } } diff --git a/src/ri.rs b/src/ri.rs index 220fc9c..93d50a2 100644 --- a/src/ri.rs +++ b/src/ri.rs @@ -10,7 +10,7 @@ pub struct Ri { bank = 0, size = 4194304, offset = 0x0000_0000, - vsize = 0x03F0_0000 + vsize = 0x03F0_0000, )] rdram: Mem, @@ -90,7 +90,7 @@ pub struct Ri { impl Ri { pub fn new(logger: slog::Logger) -> Ri { - let mut ri = Ri { + let ri = Ri { rdram: Mem::default(), reg_rdram_config: Reg32::default(), diff --git a/src/sp.rs b/src/sp.rs index 8e95851..976f9da 100644 --- a/src/sp.rs +++ b/src/sp.rs @@ -43,6 +43,9 @@ pub struct Sp { #[reg(bank = 2, offset = 0x0, rwmask = 0xFFF, wcb, rcb)] reg_rsp_pc: Reg32, + #[reg(bank = 2, offset = 0x4, wcb, rcb)] + reg_rsp_imem_bist: Reg32, + #[reg(bank = 1, offset = 0x00, rwmask = 0x1FF8)] reg_dma_rsp_addr: Reg32, @@ -58,9 +61,15 @@ pub struct Sp { #[reg(bank = 1, offset = 0x10, init = 0x1, wcb)] reg_status: Reg32, + #[reg(bank = 1, offset = 0x14, init = 0, rwmask = 0x1, readonly)] + reg_dma_full: Reg32, + #[reg(bank = 1, offset = 0x18, init = 0, rwmask = 0x1, readonly)] reg_dma_busy: Reg32, + #[reg(bank = 1, offset = 0x1C, init = 0, rwmask = 0x1, wcb)] + reg_semaphore: Reg32, + logger: slog::Logger, main_bus: Rc>>, @@ -80,6 +89,7 @@ impl Sp { main_bus, dmem: Mem::default(), imem: Mem::default(), + reg_rsp_imem_bist: Reg32::default(), reg_status: Reg32::default(), reg_dma_busy: Reg32::default(), reg_dma_rsp_addr: Reg32::default(), @@ -87,6 +97,8 @@ impl Sp { reg_dma_wr_len: Reg32::default(), reg_dma_rd_len: Reg32::default(), reg_rsp_pc: Reg32::default(), + reg_dma_full: Reg32::default(), + reg_semaphore: Reg32::default(), core_bus: bus, core_cpu: cpu, @@ -237,7 +249,8 @@ impl Sp { skip_dst: usize, ) { let bus = self.main_bus.borrow(); - for _ in 0..count { + for i in 0..count { + info!(self.logger, "dma xfer inner"; "i" => i, "count" => count); let src_hwio = bus.fetch_read::(src); let dst_hwio = bus.fetch_write::(dst); let src_mem = src_hwio.mem().unwrap(); @@ -303,6 +316,19 @@ impl Sp { fn cb_read_reg_rsp_pc(&self, _old: u32) -> u32 { self.core_cpu.borrow().ctx().get_pc() & 0xFFF } + + fn cb_write_reg_semaphore(&mut self, _old: u32, _val: u32) { + self.reg_semaphore.set(0); + } + + fn cb_write_reg_rsp_imem_bist(&mut self, _old: u32, val: u32) { + error!(self.logger, "RSP reg imem bist (write) not yet implemented"; "val" => val); + } + + fn cb_read_reg_rsp_imem_bist(&self, _old: u32) -> u32 { + error!(self.logger, "RSP reg imem bist (read) not yet implemented"); + 0 + } } pub struct SpCop0 { @@ -336,6 +362,10 @@ impl mips64::Cop0 for SpCop0 { _ => unimplemented!(), } } + fn translate_addr(&mut self, vaddr: u64) -> ::std::result::Result { + // TODO: verify this is the right way + Ok(vaddr as u32 & 0x1FFF_FFFF) + } } impl mips64::Cop for SpCop0 {