├── Cargo.lock ├── Cargo.toml └── src ├── action.rs ├── bigsur.rs ├── gpr.rs ├── i460gx.rs ├── lib.rs ├── main.rs ├── merced.rs └── mmio.rs /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.0.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.2.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 16 | 17 | [[package]] 18 | name = "bitvec" 19 | version = "0.19.5" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" 22 | dependencies = [ 23 | "funty", 24 | "radium", 25 | "tap", 26 | "wyz", 27 | ] 28 | 29 | [[package]] 30 | name = "cfg-if" 31 | version = "1.0.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 34 | 35 | [[package]] 36 | name = "crossterm" 37 | version = "0.19.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" 40 | dependencies = [ 41 | "bitflags", 42 | "crossterm_winapi", 43 | "lazy_static", 44 | "libc", 45 | "mio", 46 | "parking_lot", 47 | "signal-hook", 48 | "winapi", 49 | ] 50 | 51 | [[package]] 52 | name = "crossterm_winapi" 53 | version = "0.7.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" 56 | dependencies = [ 57 | "winapi", 58 | ] 59 | 60 | [[package]] 61 | name = "funty" 62 | version = "1.1.0" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" 65 | 66 | [[package]] 67 | name = "instant" 68 | version = "0.1.10" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" 71 | dependencies = [ 72 | "cfg-if", 73 | ] 74 | 75 | [[package]] 76 | name = "lazy_static" 77 | version = "1.4.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 80 | 81 | [[package]] 82 | name = "libc" 83 | version = "0.2.99" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" 86 | 87 | [[package]] 88 | name = "lock_api" 89 | version = "0.4.4" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" 92 | dependencies = [ 93 | "scopeguard", 94 | ] 95 | 96 | [[package]] 97 | name = "log" 98 | version = "0.4.14" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 101 | dependencies = [ 102 | "cfg-if", 103 | ] 104 | 105 | [[package]] 106 | name = "mio" 107 | version = "0.7.13" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" 110 | dependencies = [ 111 | "libc", 112 | "log", 113 | "miow", 114 | "ntapi", 115 | "winapi", 116 | ] 117 | 118 | [[package]] 119 | name = "miow" 120 | version = "0.3.7" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 123 | dependencies = [ 124 | "winapi", 125 | ] 126 | 127 | [[package]] 128 | name = "ntapi" 129 | version = "0.3.6" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 132 | dependencies = [ 133 | "winapi", 134 | ] 135 | 136 | [[package]] 137 | name = "num-traits" 138 | version = "0.2.14" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 141 | dependencies = [ 142 | "autocfg", 143 | ] 144 | 145 | [[package]] 146 | name = "parking_lot" 147 | version = "0.11.1" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 150 | dependencies = [ 151 | "instant", 152 | "lock_api", 153 | "parking_lot_core", 154 | ] 155 | 156 | [[package]] 157 | name = "parking_lot_core" 158 | version = "0.8.3" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" 161 | dependencies = [ 162 | "cfg-if", 163 | "instant", 164 | "libc", 165 | "redox_syscall", 166 | "smallvec", 167 | "winapi", 168 | ] 169 | 170 | [[package]] 171 | name = "proc-macro2" 172 | version = "1.0.28" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" 175 | dependencies = [ 176 | "unicode-xid", 177 | ] 178 | 179 | [[package]] 180 | name = "quote" 181 | version = "1.0.9" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 184 | dependencies = [ 185 | "proc-macro2", 186 | ] 187 | 188 | [[package]] 189 | name = "radium" 190 | version = "0.5.3" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" 193 | 194 | [[package]] 195 | name = "redox_syscall" 196 | version = "0.2.10" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 199 | dependencies = [ 200 | "bitflags", 201 | ] 202 | 203 | [[package]] 204 | name = "scopeguard" 205 | version = "1.1.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 208 | 209 | [[package]] 210 | name = "serde" 211 | version = "1.0.127" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" 214 | 215 | [[package]] 216 | name = "serde_derive" 217 | version = "1.0.127" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" 220 | dependencies = [ 221 | "proc-macro2", 222 | "quote", 223 | "syn", 224 | ] 225 | 226 | [[package]] 227 | name = "signal-hook" 228 | version = "0.1.17" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" 231 | dependencies = [ 232 | "libc", 233 | "mio", 234 | "signal-hook-registry", 235 | ] 236 | 237 | [[package]] 238 | name = "signal-hook-registry" 239 | version = "1.4.0" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 242 | dependencies = [ 243 | "libc", 244 | ] 245 | 246 | [[package]] 247 | name = "smallvec" 248 | version = "1.6.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 251 | 252 | [[package]] 253 | name = "sudbury" 254 | version = "0.1.0" 255 | dependencies = [ 256 | "bitflags", 257 | "yaxpeax-arch", 258 | "yaxpeax-ia64", 259 | ] 260 | 261 | [[package]] 262 | name = "syn" 263 | version = "1.0.74" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" 266 | dependencies = [ 267 | "proc-macro2", 268 | "quote", 269 | "unicode-xid", 270 | ] 271 | 272 | [[package]] 273 | name = "tap" 274 | version = "1.0.1" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 277 | 278 | [[package]] 279 | name = "unicode-xid" 280 | version = "0.2.2" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 283 | 284 | [[package]] 285 | name = "winapi" 286 | version = "0.3.9" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 289 | dependencies = [ 290 | "winapi-i686-pc-windows-gnu", 291 | "winapi-x86_64-pc-windows-gnu", 292 | ] 293 | 294 | [[package]] 295 | name = "winapi-i686-pc-windows-gnu" 296 | version = "0.4.0" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 299 | 300 | [[package]] 301 | name = "winapi-x86_64-pc-windows-gnu" 302 | version = "0.4.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 305 | 306 | [[package]] 307 | name = "wyz" 308 | version = "0.2.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" 311 | 312 | [[package]] 313 | name = "yaxpeax-arch" 314 | version = "0.2.5" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "b0c90868b68ae34284714134e827062757881ab642e5746ecdcd567ffd582ff7" 317 | dependencies = [ 318 | "crossterm", 319 | "num-traits", 320 | "serde", 321 | "serde_derive", 322 | ] 323 | 324 | [[package]] 325 | name = "yaxpeax-ia64" 326 | version = "0.2.1" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "d3ea0f2b5728fb7eb1861532cd755ff7dd993843778d548df70a4aca95f9914e" 329 | dependencies = [ 330 | "bitvec", 331 | "yaxpeax-arch", 332 | ] 333 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sudbury" 3 | version = "0.1.0" 4 | authors = ["Dan Ravensloft "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | bitflags = "1.2" 11 | yaxpeax-arch = "0.2" 12 | yaxpeax-ia64 = "0.2" 13 | -------------------------------------------------------------------------------- /src/action.rs: -------------------------------------------------------------------------------- 1 | pub enum Action { 2 | Continue, 3 | BranchTaken, 4 | } -------------------------------------------------------------------------------- /src/bigsur.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | 3 | use yaxpeax_ia64::DecodeError; 4 | 5 | use crate::action::Action; 6 | use crate::merced::Merced; 7 | use crate::mmio::Mmio; 8 | 9 | pub struct BigSurState { 10 | rom: Box<[u8]>, 11 | } 12 | 13 | impl Mmio for BigSurState { 14 | fn read1(&self, address: u64) -> u8 { 15 | todo!() 16 | } 17 | 18 | fn read8(&self, address: u64) -> u64 { 19 | todo!() 20 | } 21 | 22 | fn read16(&self, address: u64) -> u128 { 23 | match address { 24 | // BIOS ROM. 25 | 0xFFC0_0000 ..= 0xFFFF_FFFF => { 26 | let address = (address - 0xFFC0_0000) as usize; 27 | u128::from_le_bytes(self.rom[address..address + 16].try_into().unwrap()) 28 | }, 29 | _ => unimplemented!("read16 from {:016x}", address), 30 | } 31 | } 32 | } 33 | 34 | pub struct BigSur { 35 | state: BigSurState, 36 | cpu: Merced, 37 | } 38 | 39 | impl BigSur { 40 | pub fn new(rom: Box<[u8]>) -> Self { 41 | let state = BigSurState { 42 | rom 43 | }; 44 | 45 | let mut bigsur = Self { 46 | state, 47 | cpu: Merced::new(), 48 | }; 49 | 50 | bigsur.cpu.reset_exception(); 51 | bigsur 52 | } 53 | 54 | pub fn run(&mut self) -> Result<(), DecodeError> { 55 | loop { 56 | let ip = self.cpu.ip(); 57 | let trace = self.cpu.build_trace(ip, &mut self.state)?; 58 | 59 | for f in trace { 60 | if let Action::BranchTaken = f() { 61 | break; 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/gpr.rs: -------------------------------------------------------------------------------- 1 | pub const AR_PFS: usize = 64; 2 | pub const AR_LC: usize = 65; 3 | pub const AR_EC: usize = 66; 4 | 5 | pub const AR_PFS_PFM: u64 = 0x3FFFFFFFFF; 6 | pub const AR_PFS_PEC: u64 = 0x3F << 52; 7 | pub const AR_PFS_PPL: u64 = 0x3 << 62; 8 | 9 | pub const CFM_SOF: u64 = 0x7F; 10 | pub const CFM_SOL: u64 = 0x7F << 7; 11 | pub const CFM_SOR: u64 = 0xF << 14; 12 | pub const CFM_RRB: u64 = 0xFFFFF << 18; 13 | pub const CFM_RRB_GR: u64 = 0x7F << 18; 14 | pub const CFM_RRB_FR: u64 = 0x7F << 25; 15 | pub const CFM_RRB_PR: u64 = 0x3F << 32; 16 | 17 | pub const PSR_CPL: u64 = 3 << 32; 18 | pub const PSR_BN: u64 = 1 << 44; 19 | 20 | pub const CR_IPSR: usize = 16; 21 | pub const CR_IIP: usize = 19; 22 | 23 | pub struct Regs { 24 | /// General-purpose registers. 25 | /// r0 reads as zero, writes trap. 26 | gpr: [u64; 128], 27 | /// General-purpose register not-a-thing (NaT) bits. 28 | /// r0 is always-false since r0 the register is a constant. 29 | gpr_nat: [bool; 128], 30 | /// Banked general-purpose registers 16-31. 31 | gpr_bank: [u64; 16], 32 | /// Banked general-purpose registers 16-31 not-a-thing (NaT) bits. 33 | gpr_bank_nat: [bool; 16], 34 | /// Floating-point registers. 35 | /// f0 reads as 0.0, writes trap. 36 | /// f1 reads as 1.0, writes trap. 37 | fpr: [f64; 128], 38 | /// Predicate registers. 39 | /// p0 reads as true, writes ignored. 40 | pr: [bool; 64], 41 | /// Branch registers. 42 | br: [u64; 8], 43 | /// Instruction pointer. 44 | ip: u64, 45 | /// Application registers. 46 | ar: [u64; 128], 47 | /// Current frame marker. 48 | cfm: u64, 49 | /// Processor status register. 50 | psr: u64, 51 | /// Control registers. 52 | cr: [u64; 128], 53 | /// CPU identification registers. 54 | cpuid: [u64; 5], 55 | /// Machine-specific registers. Yep. 56 | msr: [u64; 4096], 57 | } 58 | 59 | impl Regs { 60 | pub fn new() -> Self { 61 | let gpr = [0_u64; 128]; 62 | let gpr_nat = [false; 128]; 63 | let gpr_bank = [0_u64; 16]; 64 | let gpr_bank_nat = [false; 16]; 65 | let mut fpr = [0.0_f64; 128]; 66 | let pr = [true; 64]; 67 | let br = [0; 8]; 68 | let ip = 0; 69 | let ar = [0; 128]; 70 | let cfm = 0; 71 | let psr = 0; 72 | let cr = [0; 128]; 73 | let mut cpuid = [0; 5]; 74 | let msr = [0; 4096]; 75 | 76 | // f1 is a constant 1.0. 77 | fpr[1] = 1.0_f64; 78 | 79 | cpuid[0] = u64::from_le_bytes([b'S', b'u', b'd', b'b', b'u', b'r', b'y', 0]); 80 | cpuid[3] = 4; 81 | 82 | eprintln!("Gpr size: {}", std::mem::size_of::()); 83 | 84 | Self { gpr, gpr_nat, gpr_bank, gpr_bank_nat, fpr, pr, br, ip, ar, cfm, psr, cr, cpuid, msr } 85 | } 86 | 87 | pub fn read_gpr(&self, index: usize) -> (u64, bool) { 88 | if self.psr & PSR_BN == 0 || index < 16 || index > 31 { 89 | (self.gpr[index], self.gpr_nat[index]) 90 | } else { 91 | (self.gpr_bank[index-16], self.gpr_bank_nat[index-16]) 92 | } 93 | } 94 | 95 | pub fn write_gpr(&mut self, index: usize, reg: u64, nat: bool) -> Result<(), ()> { 96 | if index == 0 { 97 | return Err(()); 98 | } 99 | if self.psr & PSR_BN == 0 || index < 16 || index > 31 { 100 | self.gpr[index] = reg; 101 | self.gpr_nat[index] = nat; 102 | } else { 103 | self.gpr_bank[index-16] = reg; 104 | self.gpr_bank_nat[index-16] = nat; 105 | } 106 | Ok(()) 107 | } 108 | 109 | pub fn read_fpr(&self, index: usize) -> f64 { 110 | self.fpr[index] 111 | } 112 | 113 | pub fn write_fpr(&mut self, index: usize, reg: f64) -> Result<(), ()> { 114 | if index <= 1 { 115 | return Err(()); 116 | } 117 | self.fpr[index] = reg; 118 | Ok(()) 119 | } 120 | 121 | pub fn read_pr(&self, index: usize) -> bool { 122 | self.pr[index] 123 | } 124 | 125 | pub fn read_all_pr(&self) -> u64 { 126 | let mut x = 0_u64; 127 | for pr in &self.pr { 128 | x = (x << 1) | (*pr as u64); 129 | } 130 | x 131 | } 132 | 133 | pub fn write_all_pr(&mut self, reg: u64) { 134 | for bit in 0_usize..=63 { 135 | self.pr[bit] = (reg & (1 << bit)) != 0; 136 | } 137 | } 138 | 139 | pub fn write_pr(&mut self, index: usize, reg: bool) { 140 | if index == 0 { 141 | return; 142 | } 143 | self.pr[index] = reg; 144 | } 145 | 146 | pub fn read_br(&self, index: usize) -> u64 { 147 | self.br[index] 148 | } 149 | 150 | pub fn write_br(&mut self, index: usize, reg: u64) { 151 | self.br[index] = reg; 152 | } 153 | 154 | pub fn read_ip(&self) -> u64 { 155 | self.ip 156 | } 157 | 158 | pub fn write_ip(&mut self, reg: u64) { 159 | assert!(reg & 0xF == 0); 160 | self.ip = reg; 161 | } 162 | 163 | pub fn read_ar(&self, index: usize) -> Result { 164 | // ar18 = BSPSTORE 165 | Ok(self.ar[index]) 166 | } 167 | 168 | pub fn write_ar(&mut self, index: usize, reg: u64) -> Result<(), ()> { 169 | self.ar[index] = reg; 170 | Ok(()) 171 | } 172 | 173 | pub fn read_cfm(&self) -> u64 { 174 | self.cfm 175 | } 176 | 177 | pub fn write_cfm(&mut self, reg: u64) { 178 | self.cfm = reg; 179 | } 180 | 181 | pub fn read_psr(&self) -> u64 { 182 | self.psr 183 | } 184 | 185 | pub fn write_psr(&mut self, reg: u64) { 186 | self.psr = reg; 187 | } 188 | 189 | pub fn read_cr(&self, index: usize) -> Result { 190 | Ok(self.cr[index]) 191 | } 192 | 193 | pub fn write_cr(&mut self, index: usize, reg: u64) -> Result<(), ()> { 194 | // assert_eq!(index, 64); 195 | self.cr[index] = reg; 196 | Ok(()) 197 | } 198 | 199 | pub fn read_cpuid(&self, index: usize) -> u64 { 200 | self.cpuid[index] 201 | } 202 | 203 | pub fn read_msr(&self, index: usize) -> u64 { 204 | match index { 205 | 0x5dd | 0x600 | 0x602 | 0x60e | 0x615 => self.msr[index], 206 | // msr 0x612, bit 6: "make machine work bit. set to 1 to make machine work" 207 | 0x612 => self.msr[index] | (1 << 6), 208 | _ => unimplemented!("read from msr {:03x}", index), 209 | } 210 | } 211 | 212 | pub fn write_msr(&mut self, index: usize, reg: u64) { 213 | match index { 214 | 0x020 | 0x042 | 0x0e3 | 0x181 | 0x1e8 | 0x1e9 | 0x450 | 0x4c4 | 0x5dd | 0x600 | 0x601 | 0x602 | 0x60d | 0x612 | 0x615 => self.msr[index] = reg, 215 | _ => unimplemented!("write to msr {:03x}", index), 216 | } 217 | } 218 | 219 | pub fn bank_switch(&mut self, bank: u8) { 220 | assert!(bank == 0 || bank == 1); 221 | self.psr &= !PSR_BN; 222 | self.psr |= (bank as u64) << 44; 223 | } 224 | } -------------------------------------------------------------------------------- /src/i460gx.rs: -------------------------------------------------------------------------------- 1 | pub struct I460GX { 2 | undocumented_sac_feb00cb0: u32, 3 | bsp_select: u32, 4 | } 5 | 6 | impl I460GX { 7 | pub fn new() -> Self { 8 | Self { 9 | undocumented_sac_feb00cb0: 0, 10 | bsp_select: 1 << 7, 11 | } 12 | } 13 | 14 | pub fn read_sac_feb00cb0(&self) -> u32 { 15 | self.undocumented_sac_feb00cb0 16 | } 17 | 18 | pub fn write_sac_feb00cb0(&mut self, reg: u32) { 19 | self.undocumented_sac_feb00cb0 = reg; 20 | } 21 | 22 | pub fn read_bsp_select(&self) -> u32 { 23 | self.bsp_select 24 | } 25 | 26 | pub fn write_bsp_select(&mut self, reg: u32) { 27 | self.bsp_select = reg; 28 | } 29 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod gpr; 2 | mod i460gx; 3 | 4 | use std::convert::TryInto; 5 | use yaxpeax_arch::Decoder; 6 | 7 | pub enum Action { 8 | Continue, 9 | BranchTaken, 10 | } 11 | 12 | /// An IA64 CPU. 13 | pub struct Cpu { 14 | regs: gpr::Regs, 15 | rom: Vec, 16 | i460gx: i460gx::I460GX, 17 | } 18 | 19 | impl Cpu { 20 | pub fn new(rom: Vec) -> Self { 21 | assert_eq!(rom.len(), 4 * 1024 * 1024); 22 | 23 | //const SALE_ENTRY_PTR: u64 = 0xFFFF_FFE8; 24 | let mut ia64 = Self { 25 | regs: gpr::Regs::new(), 26 | rom, 27 | i460gx: i460gx::I460GX::new(), 28 | }; 29 | /*let sale_entry = u64::from_le_bytes(ia64.read8(SALE_ENTRY_PTR)); 30 | 31 | // GR20 (bank 1): SALE_ENTRY state parameter: 32 | // 0 = RESET, 1 = MACHINE_CHECK, 2 = INIT, 3 = RECOVERY_CHECK 33 | ia64.regs.bank_switch(1); 34 | ia64.regs.write_gpr(20, 3, false).unwrap(); 35 | 36 | // CFM: all fields zero except for SOF which is 96. 37 | ia64.regs.write_cfm(96);*/ 38 | 39 | //ia64.regs.write_ip(sale_entry); 40 | 41 | // So, there are four entry points defined by the Itanium architecture: 42 | // PALE_RESET: the boot vector 43 | // PALE_CHECK: for machine check events 44 | // PALE_INIT: for initialisation events 45 | // PALE_PMI: for performance monitor events 46 | // These appear to be 0xFFFF_FF80 to 0xFFFF_FFB0, but which is which? What order are they in? 47 | ia64.regs.write_ip(0xFFFF_FFB0); 48 | ia64 49 | } 50 | 51 | pub fn read1(&self, addr: u64) -> u8 { 52 | let addr = addr & 0xFFFF_FFFF; 53 | match addr { 54 | // ROM 55 | 0xFFC0_0000 ..= 0xFFFF_FFFF => { 56 | let addr = (addr - 0xFFC0_0000) as usize; 57 | self.rom[addr] 58 | }, 59 | _ => unimplemented!("unrecognised read1 from 0x{:016x}", addr), 60 | } 61 | } 62 | 63 | pub fn read2(&self, addr: u64) -> [u8; 2] { 64 | let addr = addr & 0xFFFF_FFFF; 65 | match addr { 66 | // ROM 67 | 0xFFC0_0000 ..= 0xFFFF_FFFF => { 68 | let addr = (addr - 0xFFC0_0000) as usize; 69 | self.rom[addr..addr+2].try_into().unwrap() 70 | }, 71 | _ => unimplemented!("unrecognised read2 from 0x{:016x}", addr), 72 | } 73 | } 74 | 75 | pub fn read4(&self, addr: u64) -> [u8; 4] { 76 | let addr = addr & 0xFFFF_FFFF; 77 | match addr { 78 | // 460GX SAC undocumented register 79 | 0xFEB0_0CB0 => { 80 | self.i460gx.read_sac_feb00cb0().to_le_bytes() 81 | }, 82 | // 460GX BSP selector 83 | 0xFEB0_0CC0 => { 84 | self.i460gx.read_bsp_select().to_le_bytes() 85 | }, 86 | // ROM 87 | 0xFFC0_0000 ..= 0xFFFF_FFFF => { 88 | let addr = (addr - 0xFFC0_0000) as usize; 89 | self.rom[addr..addr+4].try_into().unwrap() 90 | }, 91 | _ => unimplemented!("unrecognised read4 from 0x{:016x}", addr), 92 | } 93 | } 94 | 95 | pub fn write4(&mut self, addr: u64, data: u32) { 96 | let addr = addr & 0xFFFF_FFFF; 97 | match addr { 98 | // 460GX SAC undocumented register 99 | 0xFEB0_0CB0 => { 100 | self.i460gx.write_sac_feb00cb0(data); 101 | }, 102 | _ => unimplemented!("unrecognised write4 to 0x{:016x}", addr), 103 | } 104 | } 105 | 106 | pub fn read8(&self, addr: u64) -> [u8; 8] { 107 | let addr = addr & 0xFFFF_FFFF; 108 | match addr { 109 | // ROM 110 | 0xFFC0_0000 ..= 0xFFFF_FFFF => { 111 | let addr = (addr - 0xFFC0_0000) as usize; 112 | self.rom[addr..addr+8].try_into().unwrap() 113 | }, 114 | _ => unimplemented!("unrecognised read8 from 0x{:016x}", addr), 115 | } 116 | } 117 | 118 | pub fn read16(&self, addr: u64) -> [u8; 16] { 119 | let addr = addr & 0xFFFF_FFFF; 120 | match addr { 121 | // ROM 122 | 0xFFC0_0000 ..= 0xFFFF_FFFF => { 123 | let addr = (addr - 0xFFC0_0000) as usize; 124 | self.rom[addr..addr+16].try_into().unwrap() 125 | }, 126 | _ => unimplemented!("unrecognised read16 from 0x{:016x}", addr), 127 | } 128 | } 129 | 130 | pub fn step_instruction(&mut self, instruction: &yaxpeax_ia64::Instruction) -> Action { 131 | use yaxpeax_ia64::{Opcode, Operand}; 132 | 133 | let pred = self.regs.read_pr(instruction.predicate().into()); 134 | 135 | let read_source = |regs: &gpr::Regs, source: &Operand| -> (u64, bool) { 136 | match source { 137 | Operand::ApplicationRegister(yaxpeax_ia64::ApplicationRegister(ar)) => (regs.read_ar(*ar as usize).unwrap(), false), 138 | Operand::BranchRegister(yaxpeax_ia64::BranchRegister(br)) => (regs.read_br(*br as usize), false), 139 | Operand::ControlRegister(yaxpeax_ia64::ControlRegister(cr)) => (regs.read_cr(*cr as usize).unwrap(), false), 140 | Operand::FloatRegister(yaxpeax_ia64::FloatRegister(fpr)) => (regs.read_fpr(*fpr as usize).to_bits(), false), 141 | Operand::GPRegister(yaxpeax_ia64::GPRegister(gpr)) => regs.read_gpr(*gpr as usize), 142 | Operand::ImmI64(imm) => (*imm as u64, false), 143 | Operand::ImmU64(imm) => (*imm, false), 144 | Operand::Indirection(yaxpeax_ia64::IndirectionReg::Cpuid, yaxpeax_ia64::GPRegister(gpr)) => (regs.read_cpuid(regs.read_gpr(*gpr as usize).0 as usize), false), 145 | Operand::Indirection(yaxpeax_ia64::IndirectionReg::Msr, yaxpeax_ia64::GPRegister(gpr)) => (regs.read_msr(regs.read_gpr(*gpr as usize).0 as usize), false), 146 | Operand::IP => (regs.read_ip(), false), 147 | Operand::Memory(yaxpeax_ia64::GPRegister(gpr)) => regs.read_gpr(*gpr as usize), 148 | Operand::PR => (regs.read_all_pr(), false), 149 | Operand::PSR => (regs.read_psr(), false), 150 | _ => todo!("source: {:?}", source), 151 | } 152 | }; 153 | 154 | let write_dest = |regs: &mut gpr::Regs, dest: &Operand, reg: u64, nat: bool| { 155 | match dest { 156 | Operand::ApplicationRegister(yaxpeax_ia64::ApplicationRegister(index)) => { 157 | assert!(!nat); 158 | eprintln!("ar{} <= {:016x}", index, reg); 159 | regs.write_ar(*index as usize, reg).unwrap(); 160 | } 161 | Operand::BranchRegister(yaxpeax_ia64::BranchRegister(index)) => { 162 | assert!(!nat); 163 | eprintln!("b{} <= {:016x}", index, reg); 164 | regs.write_br(*index as usize, reg); 165 | }, 166 | Operand::ControlRegister(yaxpeax_ia64::ControlRegister(index)) => { 167 | assert!(!nat); 168 | eprintln!("cr{} <= {:016x}", index, reg); 169 | regs.write_cr(*index as usize, reg).unwrap(); 170 | }, 171 | Operand::FloatRegister(yaxpeax_ia64::FloatRegister(index)) => { 172 | assert!(!nat); 173 | eprintln!("f{} <= {:016x}", index, reg); 174 | regs.write_fpr(*index as usize, f64::from_bits(reg)).unwrap(); 175 | } 176 | Operand::GPRegister(yaxpeax_ia64::GPRegister(index)) | Operand::Memory(yaxpeax_ia64::GPRegister(index)) => { 177 | eprintln!("r{} <= {:016x} {}", index, reg, if nat { "(NaT)" } else { "" }); 178 | regs.write_gpr(*index as usize, reg, nat).unwrap(); 179 | }, 180 | Operand::Indirection(yaxpeax_ia64::IndirectionReg::Msr, yaxpeax_ia64::GPRegister(index)) => { 181 | regs.write_msr(regs.read_gpr(*index as usize).0 as usize, reg); 182 | }, 183 | Operand::PredicateRegister(yaxpeax_ia64::PredicateRegister(index)) => { 184 | assert!(!nat); 185 | eprintln!("p{} <= {}", index, reg == 1); 186 | regs.write_pr(*index as usize, reg == 1); 187 | }, 188 | Operand::PR => { 189 | assert!(!nat); 190 | regs.write_all_pr(reg); 191 | }, 192 | Operand::PSR_l => { 193 | assert!(!nat); 194 | let psr = regs.read_psr() & 0xFFFF_FFFF_0000_0000; 195 | regs.write_psr(psr | (reg & 0xFFFF_FFFF)); 196 | }, 197 | _ => todo!("dest: {:?}", dest), 198 | } 199 | }; 200 | 201 | match instruction.opcode() { 202 | Opcode::Add | Opcode::Adds | Opcode::Addl => { 203 | if !pred { return Action::Continue; } 204 | let operands = instruction.operands(); 205 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 206 | let (source2, nat2) = read_source(&self.regs, &operands[2]); 207 | let reg = ((source1 as i64) + (source2 as i64)) as u64; 208 | write_dest(&mut self.regs, &operands[0], reg, nat1 || nat2); 209 | Action::Continue 210 | }, 211 | Opcode::Alloc => { 212 | assert_eq!(instruction.predicate(), 0, "Illegal Operation: predicated alloc"); 213 | let operands = instruction.operands(); 214 | let pfs = read_source(&self.regs, &operands[1]).0; 215 | let sof = read_source(&self.regs, &operands[2]).0; 216 | let sol = read_source(&self.regs, &operands[3]).0; 217 | let sor = read_source(&self.regs, &operands[4]).0; 218 | let cfm = self.regs.read_cfm(); 219 | assert!(sor == (cfm & gpr::CFM_SOR) >> 14 || cfm & gpr::CFM_RRB == 0); 220 | write_dest(&mut self.regs, &operands[0], pfs, false); 221 | self.regs.write_cfm(sof | sol << 7 | sor << 14 | (cfm & gpr::CFM_RRB)); 222 | Action::Continue 223 | }, 224 | Opcode::And => { 225 | if !pred { return Action::Continue; } 226 | let operands = instruction.operands(); 227 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 228 | let (source2, nat2) = read_source(&self.regs, &operands[2]); 229 | write_dest(&mut self.regs, &operands[0], source1 & source2, nat1 || nat2); 230 | Action::Continue 231 | }, 232 | Opcode::Br_call => { 233 | if !pred { return Action::Continue; } 234 | let target = match instruction.operands()[1] { 235 | Operand::ImmI64(imm) => ((self.regs.read_ip() as i64) + imm) as u64, 236 | _ => todo!("br_call source: {:?}", instruction.operands()[0]), 237 | }; 238 | let cfm = self.regs.read_cfm(); 239 | let ec = self.regs.read_ar(gpr::AR_EC).unwrap() & 0x3F; 240 | let cpl = (self.regs.read_psr() & gpr::PSR_CPL) >> 32; 241 | let pfs = cfm | ec << 52 | cpl << 62; 242 | let sof = cfm & gpr::CFM_SOF; 243 | let sol = (cfm & gpr::CFM_SOL) >> 7; 244 | let ip = self.regs.read_ip(); 245 | self.regs.write_ar(gpr::AR_PFS, pfs).unwrap(); 246 | write_dest(&mut self.regs, &instruction.operands()[0], ip + 16, false); 247 | self.regs.write_cfm(sof - sol); 248 | self.regs.write_ip(target); 249 | Action::BranchTaken 250 | }, 251 | Opcode::Br_cond => { 252 | if !pred { return Action::Continue; } 253 | let target = match instruction.operands()[0] { 254 | Operand::ImmI64(imm) => ((self.regs.read_ip() as i64) + imm) as u64, 255 | Operand::BranchRegister(yaxpeax_ia64::BranchRegister(br)) => self.regs.read_br(br as usize), 256 | _ => todo!("br_cond source: {:?}", instruction.operands()[0]), 257 | }; 258 | assert_ne!(self.regs.read_ip(), target, "possible infinite loop?"); 259 | self.regs.write_ip(target); 260 | Action::BranchTaken 261 | }, 262 | Opcode::Br_cloop => { 263 | assert!(pred); 264 | let target = match instruction.operands()[0] { 265 | Operand::ImmI64(imm) => ((self.regs.read_ip() as i64) + imm) as u64, 266 | _ => todo!("br_cond source: {:?}", instruction.operands()[0]), 267 | }; 268 | let lc = self.regs.read_ar(gpr::AR_LC).unwrap(); 269 | if lc > 0 { 270 | self.regs.write_ar(gpr::AR_LC, lc - 1).unwrap(); 271 | self.regs.write_ip(target); 272 | Action::BranchTaken 273 | } else { 274 | Action::Continue 275 | } 276 | }, 277 | Opcode::Br_ret => { 278 | if !pred { return Action::Continue; } 279 | let target = match instruction.operands()[0] { 280 | Operand::BranchRegister(yaxpeax_ia64::BranchRegister(br)) => self.regs.read_br(br as usize), 281 | _ => todo!("br_ret source: {:?}", instruction.operands()[1]), 282 | }; 283 | let pfs = self.regs.read_ar(gpr::AR_PFS).unwrap(); 284 | let cfm = pfs & gpr::AR_PFS_PFM; 285 | let ec = (pfs & gpr::AR_PFS_PEC) >> 52; 286 | let cpl = pfs & gpr::AR_PFS_PPL; 287 | let psr = (self.regs.read_psr() & !gpr::PSR_CPL) | cpl; 288 | self.regs.write_ar(gpr::AR_EC, ec).unwrap(); 289 | self.regs.write_cfm(cfm); 290 | self.regs.write_ip(target); 291 | self.regs.write_psr(psr); 292 | Action::BranchTaken 293 | }, 294 | Opcode::Break_m => { 295 | if !pred { return Action::Continue; } 296 | panic!("Break Instruction"); 297 | }, 298 | Opcode::Bsw_0 => { 299 | self.regs.bank_switch(0); 300 | Action::Continue 301 | }, 302 | Opcode::Bsw_1 => { 303 | self.regs.bank_switch(1); 304 | Action::Continue 305 | }, 306 | Opcode::Clrrb => { 307 | let cfm = self.regs.read_cfm(); 308 | self.regs.write_cfm(cfm & !gpr::CFM_RRB_FR & !gpr::CFM_RRB_GR & !gpr::CFM_RRB_PR); 309 | Action::Continue 310 | }, 311 | Opcode::Cmp_eq => { 312 | assert!(pred); 313 | let operands = instruction.operands(); 314 | assert_ne!(operands[0], operands[1], "Illegal Operation"); 315 | let (source1, nat1) = read_source(&self.regs, &operands[2]); 316 | let (source2, nat2) = read_source(&self.regs, &operands[3]); 317 | assert!(!nat1); 318 | assert!(!nat2); 319 | let eq = source1 == source2; 320 | write_dest(&mut self.regs, &operands[0], eq as u64, false); 321 | write_dest(&mut self.regs, &operands[1], !eq as u64, false); 322 | Action::Continue 323 | }, 324 | Opcode::Cmp_eq_or_andcm => { 325 | assert!(pred); 326 | let operands = instruction.operands(); 327 | assert_ne!(operands[0], operands[1], "Illegal Operation"); 328 | let (source1, nat1) = read_source(&self.regs, &operands[2]); 329 | let (source2, nat2) = read_source(&self.regs, &operands[3]); 330 | assert!(!nat1); 331 | assert!(!nat2); 332 | let eq = source1 == source2; 333 | if eq { 334 | write_dest(&mut self.regs, &operands[0], 1, false); 335 | write_dest(&mut self.regs, &operands[1], 0, false); 336 | } 337 | Action::Continue 338 | }, 339 | Opcode::Cmp_eq_unc => { 340 | let operands = instruction.operands(); 341 | assert_ne!(operands[0], operands[1], "Illegal Operation"); 342 | write_dest(&mut self.regs, &operands[0], 0, false); 343 | write_dest(&mut self.regs, &operands[1], 0, false); 344 | if !pred { return Action::Continue; } 345 | let (source1, nat1) = read_source(&self.regs, &operands[2]); 346 | let (source2, nat2) = read_source(&self.regs, &operands[3]); 347 | assert!(!nat1); 348 | assert!(!nat2); 349 | let eq = source1 == source2; 350 | write_dest(&mut self.regs, &operands[0], eq as u64, false); 351 | write_dest(&mut self.regs, &operands[1], !eq as u64, false); 352 | Action::Continue 353 | }, 354 | Opcode::Cmp_lt => { 355 | assert!(pred); 356 | let operands = instruction.operands(); 357 | assert_ne!(operands[0], operands[1], "Illegal Operation"); 358 | let (source1, nat1) = read_source(&self.regs, &operands[2]); 359 | let (source2, nat2) = read_source(&self.regs, &operands[3]); 360 | assert!(!nat1); 361 | assert!(!nat2); 362 | let lt = source1 < source2; 363 | write_dest(&mut self.regs, &operands[0], lt as u64, false); 364 | write_dest(&mut self.regs, &operands[1], !lt as u64, false); 365 | Action::Continue 366 | }, 367 | Opcode::Cmp_ne_and => { 368 | assert!(pred); 369 | let operands = instruction.operands(); 370 | assert_ne!(operands[0], operands[1], "Illegal Operation"); 371 | let (source1, nat1) = read_source(&self.regs, &operands[2]); 372 | let (source2, nat2) = read_source(&self.regs, &operands[3]); 373 | assert!(!nat1); 374 | assert!(!nat2); 375 | let ne = source1 != source2; 376 | if !ne { 377 | write_dest(&mut self.regs, &operands[0], 0, false); 378 | write_dest(&mut self.regs, &operands[1], 0, false); 379 | } 380 | Action::Continue 381 | }, 382 | Opcode::Cmp_ne_or => { 383 | if !pred { return Action::Continue; } 384 | let operands = instruction.operands(); 385 | assert_ne!(operands[0], operands[1], "Illegal Operation"); 386 | let (source1, nat1) = read_source(&self.regs, &operands[2]); 387 | let (source2, nat2) = read_source(&self.regs, &operands[3]); 388 | assert!(!nat1); 389 | assert!(!nat2); 390 | let ne = source1 != source2; 391 | if ne { 392 | write_dest(&mut self.regs, &operands[0], 1, false); 393 | write_dest(&mut self.regs, &operands[1], 1, false); 394 | } 395 | Action::Continue 396 | }, 397 | Opcode::Dep => { 398 | if !pred { return Action::Continue; } 399 | let operands = instruction.operands(); 400 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 401 | let (source2, nat2) = read_source(&self.regs, &operands[2]); 402 | let pos = read_source(&self.regs, &operands[3]).0; 403 | let len = read_source(&self.regs, &operands[4]).0; 404 | let mask = (1 << (len + 1)) - 1; 405 | let source1 = (source1 & mask) << pos; 406 | let source2 = source2 & !(mask << pos); 407 | write_dest(&mut self.regs, &operands[0], source1 | source2, nat1 || nat2); 408 | Action::Continue 409 | }, 410 | Opcode::Dep_z => { 411 | if !pred { return Action::Continue; } 412 | let operands = instruction.operands(); 413 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 414 | let pos = read_source(&self.regs, &operands[2]).0; 415 | let len = read_source(&self.regs, &operands[3]).0; 416 | let mask = (1 << (len + 1)) - 1; 417 | let source1 = (source1 & mask) << pos; 418 | write_dest(&mut self.regs, &operands[0], source1, nat1); 419 | Action::Continue 420 | }, 421 | Opcode::Extr => { 422 | let operands = instruction.operands(); 423 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 424 | assert!(!nat1); 425 | let source1 = source1 as i64; 426 | let pos = read_source(&self.regs, &operands[2]).0 as u32; 427 | let len = read_source(&self.regs, &operands[3]).0 as u32; 428 | let mask = (1_i64.wrapping_shl(len + pos + 1)) - 1; 429 | let source1 = (source1 & mask) >> pos; 430 | write_dest(&mut self.regs, &operands[0], source1 as u64, nat1); 431 | Action::Continue 432 | }, 433 | Opcode::Extr_u => { 434 | let operands = instruction.operands(); 435 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 436 | assert!(!nat1); 437 | let pos = read_source(&self.regs, &operands[2]).0 as u32; 438 | let len = read_source(&self.regs, &operands[3]).0 as u32; 439 | let mask = (1_u64.wrapping_shl(len + pos + 1)) - 1; 440 | let source1 = (source1 & mask) >> pos; 441 | write_dest(&mut self.regs, &operands[0], source1, nat1); 442 | Action::Continue 443 | }, 444 | Opcode::Flushrs => { 445 | eprintln!("flushrs stubbed"); 446 | Action::Continue 447 | }, 448 | Opcode::Fmerge_s => { 449 | if !pred { return Action::Continue; } 450 | let operands = instruction.operands(); 451 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 452 | let (source2, nat2) = read_source(&self.regs, &operands[2]); 453 | assert!(!nat1 && !nat2); 454 | let source1 = f64::from_bits(source1); 455 | let source2 = f64::from_bits(source2); 456 | write_dest(&mut self.regs, &operands[0], source2.copysign(source1).to_bits(), false); 457 | Action::Continue 458 | }, 459 | Opcode::Ld1 => { 460 | if !pred { return Action::Continue; } 461 | let operands = instruction.operands(); 462 | assert!(operands.len() == 2 || operands.len() == 3); 463 | let (source, nat) = read_source(&self.regs, &operands[1]); 464 | assert!(!nat, "Register NaT Consumption"); 465 | let data = u64::from(self.read1(source)); 466 | write_dest(&mut self.regs, &operands[0], data, false); 467 | if operands.len() == 3 { 468 | let offset = read_source(&self.regs, &operands[2]).0; 469 | let reg = ((source as i64) + (offset as i64)) as u64; 470 | write_dest(&mut self.regs, &operands[1], reg, false); 471 | } 472 | Action::Continue 473 | }, 474 | Opcode::Ld2 => { 475 | if !pred { return Action::Continue; } 476 | let operands = instruction.operands(); 477 | assert!(operands.len() == 2 || operands.len() == 3); 478 | let (source, nat) = read_source(&self.regs, &operands[1]); 479 | assert!(!nat, "Register NaT Consumption"); 480 | let data = u16::from_le_bytes(self.read2(source)) as u64; 481 | write_dest(&mut self.regs, &operands[0], data, false); 482 | if operands.len() == 3 { 483 | let offset = read_source(&self.regs, &operands[2]).0; 484 | let reg = ((source as i64) + (offset as i64)) as u64; 485 | write_dest(&mut self.regs, &operands[1], reg, false); 486 | } 487 | Action::Continue 488 | }, 489 | Opcode::Ld4_acq => { 490 | if !pred { return Action::Continue; } 491 | let operands = instruction.operands(); 492 | assert!(operands.len() == 2 || operands.len() == 3); 493 | let (source, nat) = read_source(&self.regs, &operands[1]); 494 | assert!(!nat, "Register NaT Consumption"); 495 | let data = u32::from_le_bytes(self.read4(source)); 496 | write_dest(&mut self.regs, &operands[0], data.into(), false); 497 | if operands.len() == 3 { 498 | let offset = read_source(&self.regs, &operands[2]).0; 499 | let reg = ((source as i64) + (offset as i64)) as u64; 500 | write_dest(&mut self.regs, &operands[1], reg, false); 501 | } 502 | Action::Continue 503 | }, 504 | Opcode::Ld8 => { 505 | if !pred { return Action::Continue; } 506 | let operands = instruction.operands(); 507 | assert!(operands.len() == 2 || operands.len() == 3); 508 | let (source, nat) = read_source(&self.regs, &operands[1]); 509 | assert!(!nat, "Register NaT Consumption"); 510 | let data = u64::from_le_bytes(self.read8(source)); 511 | write_dest(&mut self.regs, &operands[0], data, false); 512 | if operands.len() == 3 { 513 | let offset = read_source(&self.regs, &operands[2]).0; 514 | let reg = ((source as i64) + (offset as i64)) as u64; 515 | write_dest(&mut self.regs, &operands[1], reg, false); 516 | } 517 | Action::Continue 518 | }, 519 | Opcode::Mf_a => Action::Continue, 520 | Opcode::Mov | Opcode::Movl | Opcode::Mov_i | Opcode::Mov_m => { 521 | if !pred { return Action::Continue; } 522 | let operands = instruction.operands(); 523 | let (source, nat) = match operands.len() { 524 | 2 => read_source(&self.regs, &operands[1]), 525 | 3 => { 526 | assert!(matches!(operands[0], Operand::PR)); 527 | let (source, nat) = read_source(&self.regs, &operands[1]); 528 | let mask = read_source(&self.regs, &operands[2]).0; 529 | let pr = self.regs.read_all_pr(); 530 | ((pr & !mask) | (source & mask), nat) 531 | }, 532 | _ => unimplemented!(), 533 | }; 534 | write_dest(&mut self.regs, &operands[0], source, nat); 535 | Action::Continue 536 | }, 537 | Opcode::Mov_mwh_ih => { 538 | if !pred { return Action::Continue; } 539 | let operands = instruction.operands(); 540 | let (source, nat) = read_source(&self.regs, &operands[1]); 541 | assert!(!nat, "Register NaT Consumption"); 542 | write_dest(&mut self.regs, &operands[0], source, false); 543 | Action::Continue 544 | }, 545 | Opcode::Nop_b | Opcode::Nop_f | Opcode::Nop_i | Opcode::Nop_m | Opcode::Nop_x => Action::Continue, 546 | Opcode::Or => { 547 | if !pred { return Action::Continue; } 548 | let operands = instruction.operands(); 549 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 550 | let (source2, nat2) = read_source(&self.regs, &operands[2]); 551 | write_dest(&mut self.regs, &operands[0], source1 | source2, nat1 || nat2); 552 | Action::Continue 553 | }, 554 | Opcode::Rfi => { 555 | assert!(pred); 556 | let ipsr = self.regs.read_cr(gpr::CR_IPSR).unwrap(); 557 | let iip = self.regs.read_cr(gpr::CR_IIP).unwrap(); 558 | self.regs.write_psr(ipsr); 559 | self.regs.write_ip(iip); 560 | Action::BranchTaken 561 | }, 562 | Opcode::Rsm => { 563 | assert!(pred); 564 | let operands = instruction.operands(); 565 | let source = !(read_source(&self.regs, &operands[0]).0 & 0xFFFFFF); 566 | let psr = self.regs.read_psr(); 567 | self.regs.write_psr(psr & source); 568 | Action::Continue 569 | }, 570 | Opcode::St4_rel => { 571 | assert!(pred); 572 | let operands = instruction.operands(); 573 | assert_eq!(operands.len(), 2); 574 | let (addr, nat1) = read_source(&self.regs, &operands[0]); 575 | let (data, nat2) = read_source(&self.regs, &operands[1]); 576 | assert!(!nat1 && !nat2); 577 | self.write4(addr, data as u32); 578 | Action::Continue 579 | }, 580 | Opcode::Sub => { 581 | if !pred { return Action::Continue; } 582 | let operands = instruction.operands(); 583 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 584 | let (source2, nat2) = read_source(&self.regs, &operands[2]); 585 | write_dest(&mut self.regs, &operands[0], source1 - source2, nat1 || nat2); 586 | Action::Continue 587 | }, 588 | Opcode::SubMinusOne => { 589 | if !pred { return Action::Continue; } 590 | let operands = instruction.operands(); 591 | let (source1, nat1) = read_source(&self.regs, &operands[1]); 592 | let (source2, nat2) = read_source(&self.regs, &operands[2]); 593 | write_dest(&mut self.regs, &operands[0], source1 - source2 - 1, nat1 || nat2); 594 | Action::Continue 595 | }, 596 | Opcode::Srlz_i | Opcode::Srlz_d | Opcode::Sync_i => Action::Continue, 597 | Opcode::Tbit_z => { 598 | assert!(pred); 599 | let operands = instruction.operands(); 600 | assert_ne!(operands[0], operands[1], "Illegal Operation"); 601 | let (source1, nat1) = read_source(&self.regs, &operands[2]); 602 | let (source2, nat2) = read_source(&self.regs, &operands[3]); 603 | assert!(!nat1); 604 | assert!(!nat2); 605 | let eq = (source1 >> source2) & 1 == 0; 606 | write_dest(&mut self.regs, &operands[0], eq as u64, false); 607 | write_dest(&mut self.regs, &operands[1], !eq as u64, false); 608 | Action::Continue 609 | }, 610 | _ => todo!("{:?} {0}", instruction), 611 | } 612 | } 613 | 614 | pub fn step_bundle(&mut self) { 615 | let ip = self.regs.read_ip(); 616 | let bytes = self.read16(ip); 617 | let decoder = yaxpeax_ia64::InstDecoder::default(); 618 | let bundle = decoder.decode(bytes.iter().copied()).unwrap(); 619 | print!("{:016x}: ", ip); 620 | for byte in &bytes { 621 | print!("{:02x}", byte); 622 | } 623 | println!(" {}", bundle); 624 | 625 | let mut branch_taken = false; 626 | for instruction in bundle.instructions() { 627 | match self.step_instruction(instruction) { 628 | Action::Continue => {}, 629 | Action::BranchTaken => { 630 | eprintln!("*** BRANCH ***"); 631 | branch_taken = true; 632 | break; 633 | } 634 | } 635 | } 636 | 637 | if !branch_taken { 638 | self.regs.write_ip(ip + 16); 639 | } 640 | } 641 | } 642 | 643 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fs::File, io::Read}; 2 | 3 | // CPUs. 4 | mod merced; 5 | 6 | // Systems. 7 | mod bigsur; 8 | 9 | // Utility traits. 10 | mod action; 11 | mod mmio; 12 | 13 | fn main() -> Result<(), Box> { 14 | let mut rom_file = File::open("itanium-bios.bin").unwrap(); 15 | let mut rom = Vec::new(); 16 | rom_file.read_to_end(&mut rom).unwrap(); 17 | let rom = rom.into_boxed_slice(); 18 | let mut bigsur = bigsur::BigSur::new(rom); 19 | bigsur.run()?; 20 | Ok(()) 21 | } -------------------------------------------------------------------------------- /src/merced.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | 3 | use bitflags::bitflags; 4 | use yaxpeax_arch::{Decoder, U8Reader}; 5 | use yaxpeax_ia64::{ApplicationRegister, BranchRegister, DecodeError, GPRegister, IndirectionReg, InstDecoder, InstructionBundle, Opcode, Operand, PredicateRegister}; 6 | 7 | use crate::{action::Action, mmio::Mmio}; 8 | 9 | bitflags! { 10 | struct Psr: u64 { 11 | const BE = 1 << 1; 12 | const UP = 1 << 2; 13 | const AC = 1 << 3; 14 | const MFL = 1 << 4; 15 | const MFH = 1 << 5; 16 | const IC = 1 << 13; 17 | const I = 1 << 14; 18 | const PK = 1 << 15; 19 | const DT = 1 << 17; 20 | const DFL = 1 << 18; 21 | const DFH = 1 << 19; 22 | const SP = 1 << 20; 23 | const PP = 1 << 21; 24 | const DI = 1 << 22; 25 | const SI = 1 << 23; 26 | } 27 | } 28 | 29 | pub struct Merced { 30 | gpr: [Cell>; 128], 31 | ar: [Cell; 128], 32 | br: [Cell; 8], 33 | pr: [Cell; 64], 34 | cpuid: [Cell; 5], 35 | msr: [Cell; 2048], 36 | psr: Cell, 37 | ip: Cell, 38 | } 39 | 40 | impl Merced { 41 | pub fn new() -> Self { 42 | // const is needed to copy the cell to initialise gpr. 43 | #[allow(clippy::declare_interior_mutable_const, dead_code)] 44 | const CELL_SOME_ZERO: Cell> = Cell::new(Some(0)); 45 | #[allow(clippy::declare_interior_mutable_const, dead_code)] 46 | const CELL_ZERO: Cell = Cell::new(0); 47 | #[allow(clippy::declare_interior_mutable_const, dead_code)] 48 | const CELL_FALSE: Cell = Cell::new(false); 49 | 50 | Self { 51 | gpr: [CELL_SOME_ZERO; 128], 52 | ar: [CELL_ZERO; 128], 53 | br: [CELL_ZERO; 8], 54 | pr: [CELL_FALSE; 64], 55 | cpuid: [CELL_ZERO; 5], 56 | msr: [CELL_ZERO; 2048], 57 | psr: Cell::new(Psr::empty()), 58 | ip: CELL_ZERO, 59 | } 60 | } 61 | 62 | pub fn reset_exception(&mut self) { 63 | const PALE_RESET: u64 = 0xFFFF_FFB0; 64 | 65 | // PR0 is always one for unconditional execution. 66 | self.pr[0].set(true); 67 | 68 | // Minimum number of CPUID registers must be 4 (zero-indexed). 69 | self.cpuid[0].set(4); 70 | 71 | self.ip.set(PALE_RESET); 72 | } 73 | 74 | fn build_cmp(&self, predicate: usize, operands: &[Operand], cmp: Box bool>) -> Box Action + '_> { 75 | let dest1 = operands[0]; 76 | let dest2 = operands[1]; 77 | let src1 = operands[2]; 78 | let src2 = operands[3]; 79 | 80 | if let Operand::PredicateRegister(PredicateRegister(dest1)) = dest1 { 81 | if let Operand::PredicateRegister(PredicateRegister(dest2)) = dest2 { 82 | if let Operand::ImmI64(src1) = src1 { 83 | if let Operand::GPRegister(GPRegister(src2)) = src2 { 84 | let f: Box Action> = Box::new(move || { 85 | let (result1, result2) = if let Some(src2) = self.gpr[src2 as usize].get() { 86 | let cmp = cmp(src1, src2 as i64); 87 | (cmp, !cmp) 88 | } else { 89 | (false, false) 90 | }; 91 | if self.pr[predicate].get() { 92 | if dest1 != 0 { 93 | self.pr[dest1 as usize].set(result1); 94 | } 95 | if dest2 != 0 { 96 | self.pr[dest2 as usize].set(result2); 97 | } 98 | } 99 | Action::Continue 100 | }); 101 | return f; 102 | } 103 | } else if let Operand::GPRegister(GPRegister(src1)) = src1 { 104 | if let Operand::GPRegister(GPRegister(src2)) = src2 { 105 | let f: Box Action> = Box::new(move || { 106 | let (result1, result2) = if let Some(src1) = self.gpr[src1 as usize].get() { 107 | if let Some(src2) = self.gpr[src2 as usize].get() { 108 | let cmp = cmp(src1 as i64, src2 as i64); 109 | (cmp, !cmp) 110 | } else { 111 | (false, false) 112 | } 113 | } else { 114 | (false, false) 115 | }; 116 | 117 | if self.pr[predicate].get() { 118 | if dest1 != 0 { 119 | self.pr[dest1 as usize].set(result1); 120 | } 121 | if dest2 != 0 { 122 | self.pr[dest2 as usize].set(result2); 123 | } 124 | } 125 | Action::Continue 126 | }); 127 | return f; 128 | } 129 | } 130 | } 131 | } 132 | 133 | todo!("build_cmp: {:?}", operands); 134 | } 135 | 136 | pub fn build_trace(&self, mut ip: u64, mmio: &mut MMIO) -> Result Action + '_>>, DecodeError> { 137 | let ia64 = InstDecoder::default(); 138 | let mut bundle = InstructionBundle::default(); 139 | 140 | let mut trace = Vec::new(); 141 | 142 | println!("*** BEGIN TRACE AT {:016x} ***", ip); 143 | let mut continue_decoding = true; 144 | while continue_decoding { 145 | let insn = mmio.read16(ip).to_le_bytes(); 146 | ia64.decode_into(&mut bundle, &mut U8Reader::new(&insn))?; 147 | 148 | for byte in &insn { 149 | print!("{:02x} ", byte) 150 | } 151 | println!(": {}", bundle); 152 | 153 | for insn in bundle.instructions() { 154 | let operands = insn.operands(); 155 | match insn.opcode() { 156 | Opcode::Addl => { 157 | let dest = operands[0]; 158 | let src1 = operands[1]; 159 | let src2 = operands[2]; 160 | 161 | if let Operand::GPRegister(GPRegister(dest)) = dest { 162 | if let Operand::ImmI64(src1) = src1 { 163 | if let Operand::GPRegister(GPRegister(src2)) = src2 { 164 | let predicate = insn.predicate() as usize; 165 | let f: Box Action> = Box::new(move || { 166 | let result = self.gpr[src2 as usize].get().map(|src2| (src1 + (src2 as i64)) as u64); 167 | if self.pr[predicate].get() { 168 | self.gpr[dest as usize].set(result); 169 | } 170 | Action::Continue 171 | }); 172 | trace.push(f); 173 | } else { 174 | todo!("{} ({:?})", insn, insn); 175 | } 176 | } else { 177 | todo!("{} ({:?})", insn, insn); 178 | } 179 | } else { 180 | todo!("{} ({:?})", insn, insn); 181 | } 182 | } 183 | Opcode::Br_cond => { 184 | let offset = operands[0]; 185 | 186 | if let Operand::ImmI64(imm) = offset { 187 | // IP-relative branch. 188 | let predicate = insn.predicate() as usize; 189 | let f = Box::new(move || { 190 | if self.pr[predicate].get() { 191 | let ip = ((ip as i64) + imm) as u64; 192 | self.ip.set(ip); 193 | println!("*** BRANCH TO {:016x} ***", ip); 194 | Action::BranchTaken 195 | } else { 196 | self.ip.set(ip); 197 | Action::Continue 198 | } 199 | }); 200 | trace.push(f); 201 | } else { 202 | todo!("{} ({:?})", insn, insn); 203 | } 204 | 205 | continue_decoding = false; 206 | }, 207 | Opcode::Cmp_eq => { 208 | trace.push(self.build_cmp(insn.predicate().into(), operands, Box::new(|src1, src2| src1 == src2))); 209 | }, 210 | Opcode::Cmp_lt => { 211 | trace.push(self.build_cmp(insn.predicate().into(), operands, Box::new(|src1, src2| src1 < src2))); 212 | }, 213 | Opcode::Dep => { 214 | let dest = operands[0]; 215 | let src1 = operands[1]; 216 | let src2 = operands[2]; 217 | let pos = operands[3]; 218 | let len = operands[4]; 219 | 220 | if let Operand::GPRegister(GPRegister(dest)) = dest { 221 | if let Operand::GPRegister(GPRegister(src1)) = src1 { 222 | if let Operand::GPRegister(GPRegister(src2)) = src2 { 223 | if let Operand::ImmU64(pos) = pos { 224 | if let Operand::ImmU64(len) = len { 225 | let predicate = insn.predicate() as usize; 226 | let mask = (1 << (len + 1)) - 1; 227 | let f = Box::new(move || { 228 | let result = match (self.gpr[src1 as usize].get(), self.gpr[src2 as usize].get()) { 229 | (Some(src1), Some(src2)) => { 230 | let src1 = (src1 & mask) << pos; 231 | let src2 = (src2 & !mask) << pos; 232 | Some(src1 | src2) 233 | }, 234 | (_, _) => None, 235 | }; 236 | if self.pr[predicate].get() { 237 | self.gpr[dest as usize].set(result); 238 | } 239 | Action::Continue 240 | }); 241 | trace.push(f); 242 | } else { 243 | todo!("{} ({:?})", insn, insn); 244 | } 245 | } else { 246 | todo!("{} ({:?})", insn, insn); 247 | } 248 | } else { 249 | todo!("{} ({:?})", insn, insn); 250 | } 251 | } else if let Operand::ImmU64(src1) = src1 { 252 | if let Operand::GPRegister(GPRegister(src2)) = src2 { 253 | if let Operand::ImmU64(pos) = pos { 254 | if let Operand::ImmU64(len) = len { 255 | let predicate = insn.predicate() as usize; 256 | let mask = (1 << (len + 1)) - 1; 257 | let f = Box::new(move || { 258 | let result = match (self.gpr[src1 as usize].get(), self.gpr[src2 as usize].get()) { 259 | (Some(src1), Some(src2)) => { 260 | let src1 = (src1 & mask) << pos; 261 | let src2 = (src2 & !mask) << pos; 262 | Some(src1 | src2) 263 | }, 264 | (_, _) => None, 265 | }; 266 | if self.pr[predicate].get() { 267 | self.gpr[dest as usize].set(result); 268 | } 269 | Action::Continue 270 | }); 271 | trace.push(f); 272 | } else { 273 | todo!("{} ({:?})", insn, insn); 274 | } 275 | } else { 276 | todo!("{} ({:?})", insn, insn); 277 | } 278 | } else { 279 | todo!("{} ({:?})", insn, insn); 280 | } 281 | } else { 282 | todo!("{} ({:?})", insn, insn); 283 | } 284 | } else { 285 | todo!("{} ({:?})", insn, insn); 286 | } 287 | }, 288 | Opcode::Extr_u => { 289 | let dest = operands[0]; 290 | let src = operands[1]; 291 | let pos = operands[2]; 292 | let len = operands[3]; 293 | 294 | if let Operand::GPRegister(GPRegister(dest)) = dest { 295 | if let Operand::GPRegister(GPRegister(src)) = src { 296 | if let Operand::ImmU64(pos) = pos { 297 | if let Operand::ImmU64(len) = len { 298 | let predicate = insn.predicate() as usize; 299 | let mask = (1_u64.wrapping_shl(len as u32 + pos as u32 + 1)) - 1; 300 | let f = Box::new(move || { 301 | if self.pr[predicate].get() { 302 | self.gpr[dest as usize].set(self.gpr[src as usize].get().map(|src| (src & mask) >> pos)); 303 | } 304 | Action::Continue 305 | }); 306 | trace.push(f); 307 | } else { 308 | todo!("{} ({:?})", insn, insn); 309 | } 310 | } else { 311 | todo!("{} ({:?})", insn, insn); 312 | } 313 | } else { 314 | todo!("{} ({:?})", insn, insn); 315 | } 316 | } else { 317 | todo!("{} ({:?})", insn, insn); 318 | } 319 | } 320 | Opcode::Mov => { 321 | let operands = insn.operands(); 322 | let dest = operands[0]; 323 | let src = operands[1]; 324 | 325 | let predicate = insn.predicate() as usize; 326 | 327 | if let Operand::GPRegister(GPRegister(gpr)) = dest { 328 | if let Operand::ImmI64(imm) = src { 329 | let f = Box::new(move || { 330 | if self.pr[predicate].get() { 331 | self.gpr[gpr as usize].set(Some(imm as u64)); 332 | } 333 | Action::Continue 334 | }); 335 | trace.push(f); 336 | } else if let Operand::GPRegister(GPRegister(src)) = src { 337 | let f = Box::new(move || { 338 | if self.pr[predicate].get() { 339 | self.gpr[gpr as usize].set(self.gpr[src as usize].get()); 340 | } 341 | Action::Continue 342 | }); 343 | trace.push(f); 344 | } else if let Operand::BranchRegister(BranchRegister(src)) = src { 345 | let f = Box::new(move || { 346 | if self.pr[predicate].get() { 347 | self.gpr[gpr as usize].set(Some(self.br[src as usize].get())); 348 | } 349 | Action::Continue 350 | }); 351 | trace.push(f); 352 | } else if let Operand::IP = src { 353 | let f = Box::new(move || { 354 | if self.pr[predicate].get() { 355 | self.gpr[gpr as usize].set(Some(ip)); 356 | } 357 | Action::Continue 358 | }); 359 | trace.push(f); 360 | } else if let Operand::Indirection(IndirectionReg::Cpuid, GPRegister(src)) = src { 361 | let f = Box::new(move || { 362 | if self.pr[predicate].get() { 363 | let src = self.gpr[src as usize].get().expect("NaT consumption"); 364 | self.gpr[gpr as usize].set(Some(self.cpuid[src as usize].get())); 365 | } 366 | Action::Continue 367 | }); 368 | trace.push(f); 369 | } else if let Operand::Indirection(IndirectionReg::Msr, GPRegister(src)) = src { 370 | let f = Box::new(move || { 371 | if self.pr[predicate].get() { 372 | let src = self.gpr[src as usize].get().expect("NaT consumption"); 373 | self.gpr[gpr as usize].set(Some(self.msr[src as usize].get())); 374 | } 375 | Action::Continue 376 | }); 377 | trace.push(f); 378 | } else { 379 | todo!("{} ({:?})", insn, insn); 380 | } 381 | } else if let Operand::Indirection(IndirectionReg::Msr, GPRegister(dest)) = dest { 382 | if let Operand::GPRegister(GPRegister(src)) = src { 383 | let f = Box::new(move || { 384 | if self.pr[predicate].get() { 385 | let dest = self.gpr[dest as usize].get().expect("NaT consumption"); 386 | self.msr[dest as usize].set(self.gpr[src as usize].get().expect("NaT consumption")); 387 | } 388 | Action::Continue 389 | }); 390 | trace.push(f); 391 | } 392 | } else { 393 | todo!("{} ({:?})", insn, insn); 394 | } 395 | }, 396 | Opcode::Mov_m => { 397 | let dest = operands[0]; 398 | let src = operands[1]; 399 | 400 | let predicate = insn.predicate() as usize; 401 | 402 | if let Operand::ApplicationRegister(ApplicationRegister(dest)) = dest { 403 | if let Operand::GPRegister(GPRegister(src)) = src { 404 | let f = Box::new(move || { 405 | if self.pr[predicate].get() { 406 | self.ar[dest as usize].set(self.gpr[src as usize].get().expect("NaT consumption")); 407 | } 408 | Action::Continue 409 | }); 410 | trace.push(f); 411 | } else { 412 | todo!("{} ({:?})", insn, insn); 413 | } 414 | } else if let Operand::GPRegister(GPRegister(dest)) = dest { 415 | if let Operand::ApplicationRegister(ApplicationRegister(src)) = src { 416 | let f = Box::new(move || { 417 | if self.pr[predicate].get() { 418 | self.gpr[dest as usize].set(Some(self.ar[src as usize].get())); 419 | } 420 | Action::Continue 421 | }); 422 | trace.push(f); 423 | } 424 | } else { 425 | todo!("{} ({:?})", insn, insn); 426 | } 427 | }, 428 | Opcode::Mov_mwh_ih => { 429 | let dest = operands[0]; 430 | let src = operands[1]; 431 | 432 | let predicate = insn.predicate() as usize; 433 | 434 | if let Operand::BranchRegister(BranchRegister(dest)) = dest { 435 | if let Operand::GPRegister(GPRegister(src)) = src { 436 | let f = Box::new(move || { 437 | if self.pr[predicate].get() { 438 | self.br[dest as usize].set(self.gpr[src as usize].get().expect("NaT consumption")); 439 | } 440 | Action::Continue 441 | }); 442 | trace.push(f); 443 | } else { 444 | todo!("{} ({:?})", insn, insn); 445 | } 446 | } 447 | }, 448 | Opcode::Movl => { 449 | let dest = operands[0]; 450 | let src = operands[1]; 451 | 452 | let predicate = insn.predicate() as usize; 453 | 454 | if let Operand::GPRegister(GPRegister(dest)) = dest { 455 | if let Operand::ImmU64(src) = src { 456 | let f = Box::new(move || { 457 | if self.pr[predicate].get() { 458 | self.gpr[dest as usize].set(Some(src)); 459 | } 460 | Action::Continue 461 | }); 462 | trace.push(f); 463 | } else { 464 | todo!("{} ({:?})", insn, insn); 465 | } 466 | } 467 | }, 468 | Opcode::Rsm => { 469 | let mask = operands[0]; 470 | let predicate = insn.predicate() as usize; 471 | 472 | if let Operand::ImmU64(mask) = mask { 473 | let mask = Psr::from_bits_truncate(!mask); 474 | let f = Box::new(move || { 475 | if self.pr[predicate].get() { 476 | self.psr.set(self.psr.get() & mask); 477 | } 478 | Action::Continue 479 | }); 480 | trace.push(f); 481 | } 482 | }, 483 | Opcode::Tbit_z => { 484 | let dest1 = operands[0]; 485 | let dest2 = operands[1]; 486 | let src = operands[2]; 487 | let pos = operands[3]; 488 | let predicate = insn.predicate() as usize; 489 | 490 | if let Operand::GPRegister(GPRegister(src)) = src { 491 | if let Operand::ImmU64(pos) = pos { 492 | let f = Box::new(move || { 493 | if self.pr[predicate].get() { 494 | let (result1, result2) = if let Some() 495 | } 496 | }); 497 | } 498 | } 499 | }, 500 | Opcode::Nop_m | Opcode::Nop_i | Opcode::Nop_f | Opcode::Srlz_i | Opcode::Srlz_d => {}, 501 | _ => todo!("{} ({:?})", insn, insn), 502 | } 503 | } 504 | 505 | ip += 16; 506 | } 507 | println!("*** END TRACE AT {:016x} ***", ip); 508 | 509 | Ok(trace) 510 | } 511 | 512 | pub fn ip(&self) -> u64 { 513 | self.ip.get() 514 | } 515 | } 516 | 517 | -------------------------------------------------------------------------------- /src/mmio.rs: -------------------------------------------------------------------------------- 1 | pub trait Mmio { 2 | /// Read a byte. 3 | // TODO: read may fail. 4 | fn read1(&self, address: u64) -> u8; 5 | 6 | /// Read 8 bytes. 7 | // TODO: read may fail. 8 | fn read8(&self, address: u64) -> u64; 9 | 10 | /// Read 16 bytes. 11 | // TODO: read may fail. 12 | fn read16(&self, address: u64) -> u128; 13 | } --------------------------------------------------------------------------------